@@ -79,56 +79,20 @@ private function extractPaginator(
79
79
ResourceGenerator $ resourceGenerator ,
80
80
ServerRequestInterface $ request
81
81
) : HalResource {
82
- $ data = ['_total_items ' => $ collection ->getTotalItemCount ()];
83
- $ links = [];
84
-
85
- $ paginationParamType = $ metadata ->getPaginationParamType ();
86
- if (in_array ($ paginationParamType , $ this ->paginationTypes , true )) {
87
- // Supports pagination
88
- $ pageCount = $ collection ->count ();
89
-
90
- $ paginationParam = $ metadata ->getPaginationParam ();
91
- $ page = $ paginationParamType === AbstractCollectionMetadata::TYPE_QUERY
92
- ? (int ) ($ request ->getQueryParams ()[$ paginationParam ] ?? 1 )
93
- : (int ) $ request ->getAttribute ($ paginationParam , 1 );
94
-
95
- if ($ page < 1 || ($ page > $ pageCount && $ pageCount > 0 )) {
96
- throw new Exception \OutOfBoundsException (sprintf (
97
- 'Page %d is out of bounds. Collection has %d page%s. ' ,
98
- $ page ,
99
- $ pageCount ,
100
- $ pageCount > 1 ? 's ' : ''
101
- ));
102
- }
103
-
104
- $ collection ->setCurrentPageNumber ($ page );
105
-
106
- $ links [] = $ this ->generateLinkForPage ('self ' , $ page , $ metadata , $ resourceGenerator , $ request );
107
- if ($ page > 1 ) {
108
- $ links [] = $ this ->generateLinkForPage ('first ' , 1 , $ metadata , $ resourceGenerator , $ request );
109
- $ links [] = $ this ->generateLinkForPage ('prev ' , $ page - 1 , $ metadata , $ resourceGenerator , $ request );
110
- }
111
- if ($ page < $ pageCount ) {
112
- $ links [] = $ this ->generateLinkForPage ('next ' , $ page + 1 , $ metadata , $ resourceGenerator , $ request );
113
- $ links [] = $ this ->generateLinkForPage ('last ' , $ pageCount , $ metadata , $ resourceGenerator , $ request );
114
- }
115
-
116
- $ data ['_page ' ] = $ page ;
117
- $ data ['_page_count ' ] = $ pageCount ;
118
- }
119
-
120
- if (empty ($ links )) {
121
- $ links [] = $ this ->generateSelfLink ($ metadata , $ resourceGenerator , $ request );
122
- }
123
-
124
- $ resources = [];
125
- foreach ($ collection as $ item ) {
126
- $ resources [] = $ resourceGenerator ->fromObject ($ item , $ request );
127
- }
128
-
129
- return new HalResource ($ data , $ links , [
130
- $ metadata ->getCollectionRelation () => $ resources ,
131
- ]);
82
+ $ data = ['_total_items ' => $ collection ->getTotalItemCount ()];
83
+ $ pageCount = $ collection ->count ();
84
+
85
+ return $ this ->createPaginatedCollectionResource (
86
+ $ pageCount ,
87
+ $ data ,
88
+ function (int $ page ) use ($ collection ) {
89
+ $ collection ->setCurrentPageNumber ($ page );
90
+ },
91
+ $ collection ,
92
+ $ metadata ,
93
+ $ resourceGenerator ,
94
+ $ request
95
+ );
132
96
}
133
97
134
98
/**
@@ -150,52 +114,18 @@ private function extractDoctrinePaginator(
150
114
$ pageCount = (int ) ceil ($ totalItems / $ perPage );
151
115
152
116
$ data = ['_total_items ' => $ totalItems ];
153
- $ links = [];
154
117
155
- $ paginationParamType = $ metadata ->getPaginationParamType ();
156
- if (in_array ($ paginationParamType , $ this ->paginationTypes , true )) {
157
- $ paginationParam = $ metadata ->getPaginationParam ();
158
- $ page = $ paginationParamType === AbstractCollectionMetadata::TYPE_QUERY
159
- ? (int ) ($ request ->getQueryParams ()[$ paginationParam ] ?? 1 )
160
- : (int ) $ request ->getAttribute ($ paginationParam , 1 );
161
-
162
- if ($ page < 1 || ($ page > $ pageCount && $ pageCount > 0 )) {
163
- throw new Exception \OutOfBoundsException (sprintf (
164
- 'Page %d is out of bounds. Collection has %d page%s. ' ,
165
- $ page ,
166
- $ pageCount ,
167
- $ pageCount > 1 ? 's ' : ''
168
- ));
169
- }
170
-
171
- $ query ->setFirstResult ($ pageCount * ($ page - 1 ));
172
-
173
- $ links [] = $ this ->generateLinkForPage ('self ' , $ page , $ metadata , $ resourceGenerator , $ request );
174
- if ($ page > 1 ) {
175
- $ links [] = $ this ->generateLinkForPage ('first ' , 1 , $ metadata , $ resourceGenerator , $ request );
176
- $ links [] = $ this ->generateLinkForPage ('prev ' , $ page - 1 , $ metadata , $ resourceGenerator , $ request );
177
- }
178
- if ($ page < $ pageCount ) {
179
- $ links [] = $ this ->generateLinkForPage ('next ' , $ page + 1 , $ metadata , $ resourceGenerator , $ request );
180
- $ links [] = $ this ->generateLinkForPage ('last ' , $ pageCount , $ metadata , $ resourceGenerator , $ request );
181
- }
182
-
183
- $ data ['_page ' ] = $ page ;
184
- $ data ['_page_count ' ] = $ pageCount ;
185
- }
186
-
187
- if (empty ($ links )) {
188
- $ links [] = $ this ->generateSelfLink ($ metadata , $ resourceGenerator , $ request );
189
- }
190
-
191
- $ resources = [];
192
- foreach ($ collection as $ item ) {
193
- $ resources [] = $ resourceGenerator ->fromObject ($ item , $ request );
194
- }
195
-
196
- return new HalResource ($ data , $ links , [
197
- $ metadata ->getCollectionRelation () => $ resources ,
198
- ]);
118
+ return $ this ->createPaginatedCollectionResource (
119
+ $ pageCount ,
120
+ $ data ,
121
+ function (int $ page ) use ($ query , $ pageCount ) {
122
+ $ query ->setFirstResult ($ pageCount * ($ page - 1 ));
123
+ },
124
+ $ collection ,
125
+ $ metadata ,
126
+ $ resourceGenerator ,
127
+ $ request
128
+ );
199
129
}
200
130
201
131
private function extractIterator (
@@ -224,4 +154,116 @@ private function extractIterator(
224
154
$ metadata ->getCollectionRelation () => $ resources ,
225
155
]);
226
156
}
157
+
158
+ /**
159
+ * Create a collection resource representing a paginated set.
160
+ *
161
+ * Determines if the metadata uses a query or placeholder pagination type.
162
+ * If not, it generates a self relational link, and then immediately creates
163
+ * and returns a collection resource containing every item in the collection.
164
+ *
165
+ * If it does, it pulls the pagination parameter from the request using the
166
+ * appropriate source (query string arguments or routing parameter), and
167
+ * then checks to see if we have a valid page number, throwing an out of
168
+ * bounds exception if we do not. From the page, it then determines which
169
+ * relational pagination links to create, including a `self` relation,
170
+ * and aggregates the current page and total page count in the $data array
171
+ * before calling on createCollectionResource() to generate the final
172
+ * HAL resource instance.
173
+ *
174
+ * @param array<string, mixed> $data Data to render in the root of the HAL
175
+ * resource.
176
+ * @param callable $notifyCollectionOfPage A callback that receives an integer
177
+ * $page argument; this should be used to update the paginator instance
178
+ * with the current page number.
179
+ */
180
+ private function createPaginatedCollectionResource (
181
+ int $ pageCount ,
182
+ array $ data ,
183
+ callable $ notifyCollectionOfPage ,
184
+ iterable $ collection ,
185
+ AbstractCollectionMetadata $ metadata ,
186
+ ResourceGenerator $ resourceGenerator ,
187
+ ServerRequestInterface $ request
188
+ ) : HalResource {
189
+ $ links = [];
190
+ $ paginationParamType = $ metadata ->getPaginationParamType ();
191
+
192
+ if (! in_array ($ paginationParamType , $ this ->paginationTypes , true )) {
193
+ $ links [] = $ this ->generateSelfLink ($ metadata , $ resourceGenerator , $ request );
194
+ return $ this ->createCollectionResource (
195
+ $ links ,
196
+ $ data ,
197
+ $ collection ,
198
+ $ metadata ,
199
+ $ resourceGenerator ,
200
+ $ request
201
+ );
202
+ }
203
+
204
+ $ paginationParam = $ metadata ->getPaginationParam ();
205
+ $ page = $ paginationParamType === AbstractCollectionMetadata::TYPE_QUERY
206
+ ? (int ) ($ request ->getQueryParams ()[$ paginationParam ] ?? 1 )
207
+ : (int ) $ request ->getAttribute ($ paginationParam , 1 );
208
+
209
+ if ($ page < 1 || ($ page > $ pageCount && $ pageCount > 0 )) {
210
+ throw new Exception \OutOfBoundsException (sprintf (
211
+ 'Page %d is out of bounds. Collection has %d page%s. ' ,
212
+ $ page ,
213
+ $ pageCount ,
214
+ $ pageCount > 1 ? 's ' : ''
215
+ ));
216
+ }
217
+
218
+ $ notifyCollectionOfPage ($ page );
219
+
220
+ $ links [] = $ this ->generateLinkForPage ('self ' , $ page , $ metadata , $ resourceGenerator , $ request );
221
+ if ($ page > 1 ) {
222
+ $ links [] = $ this ->generateLinkForPage ('first ' , 1 , $ metadata , $ resourceGenerator , $ request );
223
+ $ links [] = $ this ->generateLinkForPage ('prev ' , $ page - 1 , $ metadata , $ resourceGenerator , $ request );
224
+ }
225
+ if ($ page < $ pageCount ) {
226
+ $ links [] = $ this ->generateLinkForPage ('next ' , $ page + 1 , $ metadata , $ resourceGenerator , $ request );
227
+ $ links [] = $ this ->generateLinkForPage ('last ' , $ pageCount , $ metadata , $ resourceGenerator , $ request );
228
+ }
229
+
230
+ $ data ['_page ' ] = $ page ;
231
+ $ data ['_page_count ' ] = $ pageCount ;
232
+
233
+ return $ this ->createCollectionResource (
234
+ $ links ,
235
+ $ data ,
236
+ $ collection ,
237
+ $ metadata ,
238
+ $ resourceGenerator ,
239
+ $ request
240
+ );
241
+ }
242
+
243
+ /**
244
+ * Create the collection resource with its embedded resources.
245
+ *
246
+ * Iterates the collection, passing each item to the resource generator
247
+ * to produce a HAL resource. These are then used to create an embedded
248
+ * relation in a master HAL resource that contains metadata around the
249
+ * collection itself (number of items, number of pages, etc.), and any
250
+ * relational links.
251
+ */
252
+ private function createCollectionResource (
253
+ array $ links ,
254
+ array $ data ,
255
+ iterable $ collection ,
256
+ AbstractCollectionMetadata $ metadata ,
257
+ ResourceGenerator $ resourceGenerator ,
258
+ ServerRequestInterface $ request
259
+ ) : HalResource {
260
+ $ resources = [];
261
+ foreach ($ collection as $ item ) {
262
+ $ resources [] = $ resourceGenerator ->fromObject ($ item , $ request );
263
+ }
264
+
265
+ return new HalResource ($ data , $ links , [
266
+ $ metadata ->getCollectionRelation () => $ resources ,
267
+ ]);
268
+ }
227
269
}
0 commit comments