Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit f4e120a

Browse files
committed
refactor: extract common pagination routines
Extracts two methods, `createPaginatedCollectionResource()` and `createCollectionResource()` from the bodies of each of `extractPaginator()` and `extractDoctrinePaginator()`. The first method determines if we have a known pagination type; if not, it simply generates a self relational link and creates a HAL collection by iterating the paginator. If it does know the pagination type, it will generate any necessary pagination links before iterating the collection to produce the resource. These changes simplify the logic of `extractPaginator()` and `extractDoctrinePaginator()`, allowing them to focus primarily on how they determine the required pagination information.
1 parent 303dad2 commit f4e120a

File tree

1 file changed

+137
-95
lines changed

1 file changed

+137
-95
lines changed

src/ResourceGenerator/ExtractCollectionTrait.php

Lines changed: 137 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)