Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
53 / 53 |
|
100.00% |
23 / 23 |
CRAP | |
100.00% |
1 / 1 |
Paginator | |
100.00% |
53 / 53 |
|
100.00% |
23 / 23 |
36 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
generate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setCurrentPage | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
currentPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPaginatedItems | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getItemsForPage | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getPageLinks | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
perPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
totalPages | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasMultiplePages | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
lastPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
canNavigateBack | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
canNavigateForward | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
previousPageNumber | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
nextPageNumber | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
previous | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
next | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
firstItemNumberOnPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
validateCurrentPageValue | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
formatPageName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
formatLink | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRoute | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
firstPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Hyde\Support; |
6 | |
7 | use Hyde\Hyde; |
8 | use InvalidArgumentException; |
9 | use Hyde\Support\Models\Route; |
10 | use Hyde\Foundation\Facades\Routes; |
11 | use Illuminate\Support\Collection; |
12 | use Illuminate\Contracts\Support\Arrayable; |
13 | |
14 | use function collect; |
15 | use function sprintf; |
16 | use function range; |
17 | |
18 | class Paginator |
19 | { |
20 | protected Collection $paginatedItems; |
21 | |
22 | protected int $pageSize = 25; |
23 | protected int $currentPage = 1; |
24 | |
25 | /** |
26 | * Optionally provide a route basename to be used in generating the pagination links. |
27 | */ |
28 | protected string $routeBasename; |
29 | |
30 | public function __construct(Arrayable|array $items = [], int $pageSize = 25, int $currentPageNumber = null, string $paginationRouteBasename = null) |
31 | { |
32 | $this->pageSize = $pageSize; |
33 | |
34 | $this->generate(collect($items)); |
35 | |
36 | if ($currentPageNumber) { |
37 | $this->setCurrentPage($currentPageNumber); |
38 | } |
39 | |
40 | if ($paginationRouteBasename) { |
41 | $this->routeBasename = $paginationRouteBasename; |
42 | } |
43 | } |
44 | |
45 | protected function generate(Collection $items): void |
46 | { |
47 | $this->paginatedItems = $items->chunk($this->perPage()); |
48 | } |
49 | |
50 | /** Set the current page number. */ |
51 | public function setCurrentPage(int $currentPage): Paginator |
52 | { |
53 | $this->validateCurrentPageValue($currentPage); |
54 | |
55 | $this->currentPage = $currentPage; |
56 | |
57 | return $this; |
58 | } |
59 | |
60 | /** Get the current page number (which is used as a cursor). */ |
61 | public function currentPage(): int |
62 | { |
63 | return $this->currentPage; |
64 | } |
65 | |
66 | /** Get the paginated collection */ |
67 | public function getPaginatedItems(): Collection |
68 | { |
69 | return $this->paginatedItems; |
70 | } |
71 | |
72 | public function getItemsForPage(): Collection |
73 | { |
74 | /** @var Collection $paginated */ |
75 | $paginated = $this->paginatedItems->get($this->currentPage - 1); |
76 | |
77 | return $paginated; |
78 | } |
79 | |
80 | public function getPageLinks(): array |
81 | { |
82 | $array = []; |
83 | |
84 | $pageRange = range(1, $this->totalPages()); |
85 | |
86 | if (isset($this->routeBasename)) { |
87 | foreach ($pageRange as $number) { |
88 | $array[$number] = Routes::get("$this->routeBasename/page-$number") ?? Hyde::formatLink("$this->routeBasename/page-$number"); |
89 | } |
90 | } else { |
91 | foreach ($pageRange as $number) { |
92 | $array[$number] = Hyde::formatLink("page-$number.html"); |
93 | } |
94 | } |
95 | |
96 | return $array; |
97 | } |
98 | |
99 | /** The number of items to be shown per page. */ |
100 | public function perPage(): int |
101 | { |
102 | return $this->pageSize; |
103 | } |
104 | |
105 | /** Get the total number of pages. */ |
106 | public function totalPages(): int |
107 | { |
108 | return $this->paginatedItems->count(); |
109 | } |
110 | |
111 | /** Determine if there are enough items to split into multiple pages. */ |
112 | public function hasMultiplePages(): bool |
113 | { |
114 | return $this->totalPages() > 1; |
115 | } |
116 | |
117 | /** Get the page number of the last available page. */ |
118 | public function lastPage(): int |
119 | { |
120 | return $this->totalPages(); |
121 | } |
122 | |
123 | /** Determine if there are fewer items after the cursor in the data store. */ |
124 | public function canNavigateBack(): bool |
125 | { |
126 | return $this->currentPage > $this->firstPage(); |
127 | } |
128 | |
129 | /** Determine if there are more items after the cursor in the data store. */ |
130 | public function canNavigateForward(): bool |
131 | { |
132 | return $this->currentPage < $this->lastPage(); |
133 | } |
134 | |
135 | public function previousPageNumber(): false|int |
136 | { |
137 | if (! $this->canNavigateBack()) { |
138 | return false; |
139 | } |
140 | |
141 | return $this->currentPage - 1; |
142 | } |
143 | |
144 | public function nextPageNumber(): false|int |
145 | { |
146 | if (! $this->canNavigateForward()) { |
147 | return false; |
148 | } |
149 | |
150 | return $this->currentPage + 1; |
151 | } |
152 | |
153 | public function previous(): false|string|Route |
154 | { |
155 | if (! $this->canNavigateBack()) { |
156 | return false; |
157 | } |
158 | |
159 | if (! isset($this->routeBasename)) { |
160 | return $this->formatLink(-1); |
161 | } |
162 | |
163 | return $this->getRoute(-1); |
164 | } |
165 | |
166 | public function next(): false|string|Route |
167 | { |
168 | if (! $this->canNavigateForward()) { |
169 | return false; |
170 | } |
171 | |
172 | if (! isset($this->routeBasename)) { |
173 | return $this->formatLink(+1); |
174 | } |
175 | |
176 | return $this->getRoute(+1); |
177 | } |
178 | |
179 | public function firstItemNumberOnPage(): int |
180 | { |
181 | return (($this->currentPage - 1) * $this->perPage()) + 1; |
182 | } |
183 | |
184 | protected function validateCurrentPageValue(int $currentPage): void |
185 | { |
186 | if ($currentPage < $this->firstPage()) { |
187 | throw new InvalidArgumentException('Current page number must be greater than 0.'); |
188 | } |
189 | |
190 | if ($currentPage > $this->lastPage()) { |
191 | throw new InvalidArgumentException('Current page number must be less than or equal to the last page number.'); |
192 | } |
193 | } |
194 | |
195 | protected function formatPageName(int $offset): string |
196 | { |
197 | return sprintf('page-%d', $this->currentPage + $offset); |
198 | } |
199 | |
200 | protected function formatLink(int $offset): string |
201 | { |
202 | return Hyde::formatLink("{$this->formatPageName($offset)}.html"); |
203 | } |
204 | |
205 | protected function getRoute(int $offset): Route|string |
206 | { |
207 | return Routes::get("$this->routeBasename/{$this->formatPageName($offset)}") ?? Hyde::formatLink("$this->routeBasename/{$this->formatPageName($offset)}"); |
208 | } |
209 | |
210 | protected function firstPage(): int |
211 | { |
212 | return 1; |
213 | } |
214 | } |