8000 Add pagination links and pagination config. · josh-taylor/laravel-json-api@7f0b8b5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7f0b8b5

Browse files
committed
Add pagination links and pagination config.
1 parent ce690dc commit 7f0b8b5

File tree

8 files changed

+340
-53
lines changed

8 files changed

+340
-53
lines changed

config/json-api.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,41 @@
9292
],
9393
'columns' => [],
9494
],
95+
96+
/*
97+
|--------------------------------------------------------------------------
98+
| Pagination
99+
|--------------------------------------------------------------------------
100+
|
101+
| Here you can customise how pagination is handled. If any values are
102+
| set as `null` then default values will be used.
103+
|
104+
| The `params` config sets the keys that the client uses for the page number
105+
| and the amount per-page in the request. The JSON API spec defines the
106+
| `page` parameter as where these will appear. So if the `params.page`
107+
| setting is `number`, the client will need to submit `page[number]=2` to
108+
| get the second page.
109+
|
110+
| The `meta` config sets the keys to use for pagination meta in responses.
111+
| Pagination meta will be added to your response meta under the key defined
112+
| in the `meta.key` setting. The other settings define the keys to use
113+
| within the pagination meta for the values returned by the Laravel
114+
| Paginator/LengthAwarePaginator contracts.
115+
|
116+
*/
117+
'pagination' => [
118+
'params' => [
119+
'page' => null,
120+
'per-page' => null,
121+
],
122+
'meta' => [
123+
'key' => null,
124+
'current-page' => null,
125+
'per-page' => null,
126+
'first-item' => null,
127+
'last-item' => null,
128+
'total' => null,
129+
'last-page' => null,
130+
],
131+
],
95132
];

src/Contracts/Document/LinkFactoryInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ interface LinkFactoryInterface
3232
const ROUTE_NAME_RELATED_RESOURCE = '%s.related';
3333
const ROUTE_NAME_RELATIONSHIPS = '%s.relationships';
3434

35+
/**
36+
* Get a link to the current URL.
37+
*
38+
* @param array $queryParams
39+
* @param array|object|null $meta
40+
* meta to attach to the link object.
41+
* @return LinkInterface
42+
*/
43+
public function current(array $queryParams = [], $meta = null);
44+
3545
/**
3646
* Get a link to the index of a resource type.
3747
*

src/Contracts/Pagination/PaginatorInterface.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,22 @@ interface PaginatorInterface
3535
const LINK_NEXT = DocumentInterface::KEYWORD_NEXT;
3636

3737
/**
38+
* Add pagination meta to the supplied meta.
39+
*
3840
* @param Paginator $results
3941
* @param array|object|null $meta
4042
* the meta to merge the pagination meta with.
4143
* @return array|object
4244
*/
43-
public function getMeta(Paginator $results, $meta = null);
45+
public function addMeta(Paginator $results, $meta = null);
4446

4547
/**
48+
* Add pagination links to the supplied links.
49+
*
4650
* @param Paginator $results
47-
* @return LinkInterface[]
51+
* @param array $links
52+
* the links to add the pagination links to.
53+
* @return array
4854
*/
49-
public function getLinks(Paginator $results);
55+
public function addLinks(Paginator $results, array $links = []);
5056
}

src/Document/LinkFactory.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,22 @@ public function __construct(UrlGenerator $generator)
4747
$this->generator = $generator;
4848
}
4949

50+
/**
51+
* @param array $queryParams
52+
* @param array|object|null $meta
53+
* @return Link
54+
*/
55+
public function current(array $queryParams = [], $meta = null)
56+
{
57+
$url = $this->generator->current();
58+
59+
if ($queryParams) {
60+
$url .= '?' . http_build_query($queryParams);
61+
}
62+
63+
return new Link($url, $meta, true);
64+
}
65+
5066
/**
5167
* Get a link to the index of a resource type.
5268
*

src/Http/Responses/ResponseFactory.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ public function content(
101101
array $headers = []
102102
) {
103103
if ($data instanceof Paginator) {
104-
$meta = $this->paginator->getMeta($data, $meta);
105-
$links = array_merge($this->paginator->getLinks($data), $links);
104+
$meta = $this->paginator->addMeta($data, $meta);
105+
$links = $this->paginator->addLinks($data, $links);
106106
}
107107

108108
return $this->responses->getContentResponse($data, $statusCode, $links, $meta, $headers);
@@ -136,8 +136,8 @@ public function relationship(
136136
array $headers = []
137137
) {
138138
if ($data instanceof Paginator) {
139-
$meta = $this->paginator->getMeta($data, $meta);
140-
$links = array_merge($this->paginator->getLinks($data), $links);
139+
$meta = $this->paginator->addMeta($data, $meta);
140+
$links = $this->paginator->addLinks($data, $links);
141141
}
142142

143143
return $this->responses->getIdentifiersResponse($data, $statusCode, $links, $meta, $headers);

src/Pagination/PageParameterHandler.php

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,25 @@
2929
class PageParameterHandler implements PageParameterHandlerInterface
3030
{
3131

32-
const DEFAULT_PAGE_KEY = 'number';
33-
const DEFAULT_PER_PAGE_KEY = 'size';
34-
3532
/**
3633
* @var JsonApiService
3734
*/
3835
private $service;
3936

37+
/**
38+
* @var PaginatorConfiguration
39+
*/
40+
private $config;
41+
4042
/**
4143
* PageParameter constructor.
4244
* @param JsonApiService $service
45+
* @param PaginatorConfiguration $config
4346
*/
44-
public function __construct(JsonApiService $service)
47+
public function __construct(JsonApiService $service, PaginatorConfiguration $config)
4548
{
4649
$this->service = $service;
50+
$this->config = $config;
4751
}
4852

4953
/**
@@ -57,8 +61,10 @@ public function __construct(JsonApiService $service)
5761
public function isPaginated()
5862
{
5963
$params = $this->getParams();
64+
$pageKey = $this->config->getParamPage();
65+
$perPageKey = $this->config->getParamPerPage();
6066

61-
return Arr::has($params, $this->getPageKey()) || Arr::has($params, $this->getPerPageKey());
67+
return Arr::has($params, $pageKey) || Arr::has($params, $perPageKey);
6268
}
6369

6470
/**
@@ -70,7 +76,8 @@ public function isPaginated()
7076
*/
7177
public function getCurrentPage()
7278
{
73-
$page = (int) $this->getParam($this->getPageKey(), 1);
79+
$key = $this->config->getParamPage();
80+
$page = (int) $this->getParam($key, 1);
7481

7582
return (0 < $page) ? $page : 1;
7683
}
@@ -86,7 +93,8 @@ public function getCurrentPage()
8693
*/
8794
public function getPerPage($default = 15, $max = null)
8895
{
89-
$perPage = (int) $this->getParam($this->getPerPageKey(), $default);
96+
$key = $this->config->getParamPerPage();
97+
$perPage = (int) $this->getParam($key, $default);
9098

9199
if (is_int($max) && $perPage > $max) {
92100
$perPage = $max;
@@ -105,31 +113,11 @@ public function getPerPage($default = 15, $max = null)
105113
public function getAllowedPagingParameters()
106114
{
107115
return [
108-
$this->getPageKey(),
109-
$this->getPerPageKey(),
116+
$this->config->getParamPage(),
117+
$this->config->getParamPerPage(),
110118
];
111119
}
112120

113-
/**
114-
* @return string
115-
*/
116-
protected function getPageKey()
117-
{
118-
$key = config('json-api.pagination.params.page');
119-
120-
return is_string($key) && !empty($key) ? $key : self::DEFAULT_PAGE_KEY;
121-
}
122-
123-
/**
124-
* @return string
125-
*/
126-
protected function getPerPageKey()
127-
{
128-
$key = config('json-api.pagination.params.per-page');
129-
130-
return is_string($key) && !empty($key) ? $key : self::DEFAULT_PER_PAGE_KEY;
131-
}
132-
133121
/**
134122
* @return array
135123
*/

src/Pagination/Paginator.php

Lines changed: 105 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818

1919
namespace CloudCreativity\LaravelJsonApi\Pagination;
2020

21+
use CloudCreativity\LaravelJsonApi\Contracts\Document\LinkFactoryInterface;
2122
use CloudCreativity\LaravelJsonApi\Contracts\Pagination\PaginatorInterface;
2223
use CloudCreativity\LaravelJsonApi\Services\JsonApiService;
2324
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
2425
use Illuminate\Contracts\Pagination\Paginator as IlluminatePaginator;
26+
use Neomerx\JsonApi\Contracts\Document\LinkInterface;
27+
use Neomerx\JsonApi\Contracts\Http\Query\QueryParametersParserInterface;
2528

2629
/**
2730
* Class Paginator
@@ -35,47 +38,132 @@ class Paginator implements PaginatorInterface
3538
*/
3639
private $service;
3740

41+
/**
42+
* @var LinkFactoryInterface
43+
*/
44+
private $links;
45+
46+
/**
47+
* @var PaginatorConfiguration
48+
*/
49+
private $config;
50+
3851
/**
3952
* Paginator constructor.
4053
* @param JsonApiService $service
54+
* @param LinkFactoryInterface $links
55+
* @param PaginatorConfiguration $config
4156
*/
42-
public function __construct(JsonApiService $service)
43-
{
57+
public function __construct(
58+
JsonApiService $service,
59+
LinkFactoryInterface $links,
60+
PaginatorConfiguration $config
61+
) {
4462
$this->service = $service;
63+
$this->links = $links;
64+
$this->config = $config;
4565
}
4666

4767
/**
4868
* @param IlluminatePaginator $results
49-
* @param null $meta
50-
* @return array|null
51-
* @todo customise keys in config
52-
* @todo allow meta to be set into nested config plus merge with existing meta.
69+
* @param array|object|null $meta
70+
* the meta to add the pagination meta to.
71+
* @return array|object|null
72+
* the combined meta.
5373
*/
54-
public function getMeta(IlluminatePaginator $results, $meta = null)
74+
public function addMeta(IlluminatePaginator $results, $meta = null)
5575
{
56-
$meta = [
57-
'number' => $results->currentPage(),
58-
'size' => $results->perPage(),
59-
'from' => $results->firstItem(),
60-
'to' => $results->lastItem(),
76+
$meta = $meta ?: [];
77+
78+
$page = [
79+
$this->config->getMetaCurrentPage() => $results->currentPage(),
80+
$this->config->getMetaPerPage() => $results->perPage(),
81+
$this->config->getMetaFirstItem() => $results->firstItem(),
82+
$this->config->getMetaLastItem() => $results->lastItem(),
6183
];
6284

6385
if ($results instanceof LengthAwarePaginator) {
64-
$meta['total'] = $results->total();
65-
$meta['last'] = $results->lastPage();
86+
$page[$this->config->getMetaTotal()] = $results->total();
87+
$page[$this->config->getMetaLastPage()] = $results->lastPage();
88+
}
89+
90+
$key = $this->config->getMetaKey();
91+
92+
if (is_array($meta)) {
93+
$meta[$key] = $page;
94+
} elseif (is_object($meta)) {
95+
$meta->{$key} = $page;
6696
}
6797

6898
return $meta;
6999
}
70100

71101
/**
72102
* @param IlluminatePaginator $results
103+
* @param array $links
73104
* @return array
74-
* @todo
75105
*/
76-
public function getLinks(IlluminatePaginator $results)
106+
public function addLinks(IlluminatePaginator $results, array $links = [])
77107
{
78-
return [];
108+
$currentPage = $results->currentPage();
109+
$perPage = $results->perPage();
110+
$params = $this->buildParams();
111+
112+
$previousPage = (1 < $currentPage) ? $currentPage - 1 : null;
113+
114+
if ($results instanceof LengthAwarePaginator) {
115+
$lastPage = $results->lastPage();
116+
$nextPage = ($currentPage < $lastPage) ? $currentPage + 1 : null;
117+
} else {
118+
$lastPage = null;
119+
$nextPage = $currentPage + 1;
120+
}
121+
122+
$page = array_filter([
123+
self::LINK_FIRST => $this->createLink(1, $perPage, $params),
124+
self::LINK_PREV => $this->createLink($previousPage, $perPage, $params),
125+
self::LINK_NEXT => $this->createLink($nextPage, $perPage, $params),
126+
self::LINK_LAST => $this->createLink($lastPage, $perPage, $params),
127+
]);
128+
129+
return array_merge($page, $links);
79130
}
80131

132+
/**
133+
* Build parameters that are to be included with pagination links.
134+
*
135+
* @return array
136+
*/
137+
protected function buildParams()
138+
{
139+
$encodingParameters = $this
140+
->service
141+
->getRequest()
142+
->getEncodingParameters();
143+
144+
return [
145+
QueryParametersParserInterface::PARAM_FILTER => $encodingParameters->getFilteringParameters(),
146+
];
147+
}
148+
149+
/**
150+
* @param int|null $page
151+
* @param int $perPage
152+
* @param array $parameters
153+
* @param array|object|null $meta
154+
* @return LinkInterface|null
155+
*/
156+
protected function createLink($page, $perPage, array $parameters = [], $meta = null)
157+
{
158+
if (!is_int($page)) {
159+
return null;
160+
}
161+
162+
return $this->links->current(array_merge($parameters, [
163+
QueryParametersParserInterface::PARAM_PAGE => [
164+
$this->config->getParamPage() => $page,
165+
$this->config->getParamPerPage() => $perPage,
166+
],
167+
]), $meta);
168+
}
81169
}

0 commit comments

Comments
 (0)
0