Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 747dfcd

Browse files
committed
Add paginated response
1 parent c17c546 commit 747dfcd

File tree

8 files changed

+495
-8
lines changed

8 files changed

+495
-8
lines changed
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Core\Contracts\Pagination;
21+
22+
use Countable;
23+
use Illuminate\Contracts\Support\Responsable;
24+
use IteratorAggregate;
25+
use LaravelJsonApi\Core\Document\Links;
26+
27+
interface Page extends IteratorAggregate, Countable, Responsable
28+
{
29+
30+
/**
31+
* Get the page meta.
32+
*
33+
* @return array
34+
*/
35+
public function meta(): array;
36+
37+
/**
38+
* Get the page links.
39+
*
40+
* @return Links
41+
*/
42+
public function links(): Links;
43+
44+
/**
45+
* Specify the query string parameters that should be present on pagination links.
46+
*
47+
* @param iterable $query
48+
* @return $this
49+
*/
50+
public function withQuery(iterable $query): self;
51+
}

src/Core/Pagination/Page.php

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Core\Pagination;
21+
22+
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
23+
use Illuminate\Contracts\Pagination\Paginator;
24+
use Illuminate\Support\Arr;
25+
use InvalidArgumentException;
26+
use LaravelJsonApi\Core\Contracts\Pagination\Page as PageContract;
27+
use LaravelJsonApi\Core\Document\Link;
28+
use LaravelJsonApi\Core\Document\Links;
29+
use LaravelJsonApi\Core\Resources\PaginatedResourceResponse;
30+
use function collect;
31+
use function is_null;
32+
33+
class Page implements PageContract
34+
{
35+
36+
/**
37+
* @var Paginator|LengthAwarePaginator
38+
*/
39+
private $paginator;
40+
41+
/**
42+
* @var array|null
43+
*/
44+
private $queryParameters;
45+
46+
/**
47+
* @param PageContract|Paginator $page
48+
* @return PageContract
49+
*/
50+
public static function cast($page): PageContract
51+
{
52+
if ($page instanceof PageContract) {
53+
return $page;
54+
}
55+
56+
if ($page instanceof Paginator) {
57+
return new self($page);
58+
}
59+
60+
throw new InvalidArgumentException('Expecting a JSON API page or a Laravel paginator.');
61+
}
62+
63+
/**
64+
* Page constructor.
65+
*
66+
* @param Paginator $paginator
67+
*/
68+
public function __construct(Paginator $paginator)
69+
{
70+
$this->paginator = $paginator;
71+
}
72+
73+
/**
74+
* @inheritDoc
75+
*/
76+
public function meta(): array
77+
{
78+
return \collect([
79+
'current_page' => (int) $this->paginator->currentPage(),
80+
'from' => (int) $this->paginator->firstItem(),
81+
'last_page' => $this->isLengthAware() ? (int) $this->paginator->lastPage() : null,
82+
'per_page' => (int) $this->paginator->perPage(),
83+
'to' => (int) $this->paginator->lastItem(),
84+
'total' => $this->isLengthAware() ? (int) $this->paginator->total() : null,
85+
])->reject(function ($value) {
86+
return is_null($value);
87+
})->all();
88+
}
89+
90+
/**
91+
* @inheritDoc
92+
*/
93+
public function links(): Links
94+
{
95+
return new Links(...\array_filter([
96+
$this->first(),
97+
$this->prev(),
98+
$this->next(),
99+
$this->last(),
100+
]));
101+
}
102+
103+
/**
104+
* @return Link
105+
*/
106+
public function first(): Link
107+
{
108+
return new Link('first', $this->url(1));
109+
}
110+
111+
/**
112+
* @return Link|null
113+
*/
114+
public function prev(): ?Link
115+
{
116+
if (1 < $this->paginator->currentPage()) {
117+
return new Link('prev', $this->url(
118+
$this->paginator->currentPage() - 1
119+
));
120+
}
121+
122+
return null;
123+
}
124+
125+
/**
126+
* @return Link|null
127+
*/
128+
public function next(): ?Link
129+
{
130+
if ($this->isLengthAware() && $this->paginator->hasMorePages()) {
131+
return new Link('next', $this->url(
132+
$this->paginator->currentPage() + 1
133+
));
134+
}
135+
136+
return null;
137+
}
138+
139+
/**
140+
* @return Link|null
141+
*/
142+
public function last(): ?Link
143+
{
144+
if ($this->isLengthAware()) {
145+
return new Link('last', $this->url($this->paginator->lastPage()));
146+
}
147+
148+
return null;
149+
}
150+
151+
/**
152+
* @param int $page
153+
* @return string
154+
*/
155+
public function url(int $page): string
156+
{
157+
$params = \collect($this->queryParameters)
158+
->put('page', ['number' => $page, 'size' => $this->paginator->perPage()])
159+
->sortKeys()
160+
->all();
161+
162+
return $this->paginator->path() . '?' . Arr::query($params);
163+
}
164+
165+
/**
166+
* @inheritDoc
167+
*/
168+
public function withQuery(iterable $query): PageContract
169+
{
170+
$this->paginator->appends(collect($query)->all());
171+
172+
return $this;
173+
}
174+
175+
/**
176+
* @inheritDoc
177+
*/
178+
public function getIterator()
179+
{
180+
yield from $this->paginator;
181+
}
182+
183+
/**
184+
* @inheritDoc
185+
*/
186+
public function count()
187+
{
188+
return \count($this->paginator);
189+
}
190+
191+
/**
192+
* @inheritDoc
193+
*/
194+
public function toResponse($request)
195+
{
196+
return (new PaginatedResourceResponse($this))->toResponse($request);
197+
}
198+
199+
protected function pageParameter(): array
200+
{
201+
return [
202+
'number' => 1,
203+
];
204+
}
205+
206+
/**
207+
* @return bool
208+
*/
209+
protected function isLengthAware(): bool
210+
{
211+
return $this->paginator instanceof LengthAwarePaginator;
212+
}
213+
214+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Core\Resources;
21+
22+
use Illuminate\Contracts\Pagination\Paginator;
23+
use InvalidArgumentException;
24+
use LaravelJsonApi\Core\Contracts\Pagination\Page as PageContract;
25+
use LaravelJsonApi\Core\Document\Links;
26+
use LaravelJsonApi\Core\Json\Hash;
27+
use LaravelJsonApi\Core\Pagination\Page;
28+
29+
class PaginatedResourceResponse extends ResourceCollectionResponse
30+
{
31+
32+
/**
33+
* @var ResourceCollection
34+
*/
35+
private $resources;
36+
37+
/**
38+
* @var PageContract
39+
*/
40+
private $page;
41+
42+
/**
43+
* PaginatedResourceResponse constructor.
44+
*
45+
* @param PageContract|Paginator|ResourceCollection $resources
46+
*/
47+
public function __construct($resources)
48+
{
49+
if ($resources instanceof PageContract) {
50+
$this->page = $resources;
51+
$resources = new ResourceCollection($resources);
52+
} else if ($resources instanceof Paginator) {
53+
$this->page = new Page($resources);
54+
$resources = new ResourceCollection($resources);
55+
} else if ($resources instanceof ResourceCollection && $resources->resources instanceof PageContract) {
56+
$this->page = $resources->resources;
57+
} else {
58+
throw new InvalidArgumentException('Expecting a page or a resource collection that contains a page.');
59+
}
60+
61+
parent::__construct($resources);
62+
}
63+
64+
/**
65+
* @return Hash
66+
*/
67+
public function meta(): Hash
68+
{
69+
return (new Hash($this->page->meta()))->merge(
70+
parent::meta()
71+
);
72+
}
73+
74+
/**
75+
* @return Links
76+
*/
77+
public function links(): Links
78+
{
79+
return $this->page->links()->merge(
80+
parent::links()
81+
);
82+
}
83+
84+
}

0 commit comments

Comments
 (0)