@@ -79,56 +79,20 @@ private function extractPaginator(
7979 ResourceGenerator $ resourceGenerator ,
8080 ServerRequestInterface $ request
8181 ) : 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+ );
13296 }
13397
13498 /**
@@ -150,52 +114,18 @@ private function extractDoctrinePaginator(
150114 $ pageCount = (int ) ceil ($ totalItems / $ perPage );
151115
152116 $ data = ['_total_items ' => $ totalItems ];
153- $ links = [];
154117
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+ );
199129 }
200130
201131 private function extractIterator (
@@ -224,4 +154,116 @@ private function extractIterator(
224154 $ metadata ->getCollectionRelation () => $ resources ,
225155 ]);
226156 }
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+ }
227269}
0 commit comments