1 | <?php |
||
13 | class Paginator extends DataComponent { |
||
14 | |||
15 | /** @var int */ |
||
16 | private $_perPage; |
||
17 | |||
18 | /** @var int */ |
||
19 | private $_currentPage; |
||
20 | |||
21 | /** @var string */ |
||
22 | private $_firstPageSymbol = 'first'; |
||
23 | |||
24 | /** @var string */ |
||
25 | private $_previousPageSymbol = '←'; |
||
26 | |||
27 | /** @var string */ |
||
28 | private $_nextPageSymbol = '→'; |
||
29 | |||
30 | /** @var string */ |
||
31 | private $_lastPageSymbol = 'last'; |
||
32 | |||
33 | /** @var int */ |
||
34 | private $_surroundingPages = 2; |
||
35 | |||
36 | |||
37 | /** |
||
38 | * Paginator constructor. |
||
39 | * @param int $perPage |
||
40 | */ |
||
41 | 9 | public function __construct(int $perPage = 15) |
|
45 | |||
46 | /** |
||
47 | * How many entries per page? |
||
48 | * |
||
49 | * @param int $perPage |
||
50 | * @return Paginator |
||
51 | */ |
||
52 | 1 | public function entriesPerPage($perPage = 15) : Paginator |
|
53 | { |
||
54 | 1 | $this->_perPage = $perPage; |
|
55 | |||
56 | 1 | return $this; |
|
57 | } |
||
58 | |||
59 | /** |
||
60 | * How many surrounding pages should be shown? |
||
61 | * |
||
62 | * @param int $count |
||
63 | * @return Paginator |
||
64 | */ |
||
65 | 1 | public function surroundingPages($count = 2) : Paginator |
|
66 | { |
||
67 | 1 | $this->_surroundingPages = $count; |
|
68 | |||
69 | 1 | return $this; |
|
70 | } |
||
71 | |||
72 | /** |
||
73 | * Render the page links. |
||
74 | * |
||
75 | * @return string |
||
76 | * @throws \RuntimeException |
||
77 | */ |
||
78 | 5 | public function render() : string |
|
79 | { |
||
80 | 5 | if ($this->_perPage === 0) |
|
81 | { |
||
82 | 1 | return '<ul class="pagination"><li class="active">1</li></ul>'; |
|
83 | } |
||
84 | |||
85 | return '<ul class="pagination">' . |
||
86 | 4 | $this->_renderListItem($this->_currentPage - 1, $this->_getFirstPageUrl(), $this->_firstPageSymbol) . |
|
87 | 4 | $this->_renderListItem($this->_currentPage - 1, $this->_getPreviousPageUrl(), $this->_previousPageSymbol) . |
|
88 | 4 | $this->_renderPageList() . |
|
89 | 4 | $this->_renderListItem($this->_currentPage + 1, $this->_getNextPageUrl(), $this->_nextPageSymbol) . |
|
90 | 4 | $this->_renderListItem($this->_currentPage + 1, $this->_getLastPageUrl(), $this->_lastPageSymbol) . |
|
91 | 4 | '</ul>'; |
|
92 | } |
||
93 | |||
94 | /** |
||
95 | * Renders a list item with a page link. |
||
96 | * |
||
97 | * @param int $pagenumber |
||
98 | * @param string $url |
||
99 | * @param string|null $symbol |
||
100 | * |
||
101 | * @return string |
||
102 | */ |
||
103 | 4 | private function _renderListItem(int $pagenumber, ? string $url, ? string $symbol = null) : string |
|
104 | { |
||
105 | 4 | if ($url === null) |
|
106 | { |
||
107 | 3 | return ''; |
|
108 | } |
||
109 | |||
110 | 4 | return '<li' . ($pagenumber === $this->_currentPage ? ' class="active"' : '') . '><a href="' . $url . '">' . ($symbol ?? $pagenumber) . '</a></li>'; |
|
111 | } |
||
112 | |||
113 | /** |
||
114 | * @return null|string |
||
115 | * @throws \RuntimeException |
||
116 | */ |
||
117 | 4 | private function _getFirstPageUrl() : ? string |
|
118 | { |
||
119 | 4 | if ($this->_currentPage <= $this->_surroundingPages + 1) |
|
120 | { |
||
121 | 1 | return null; |
|
122 | } |
||
123 | |||
124 | 3 | return $this->_buildPageUrl(1); |
|
125 | } |
||
126 | |||
127 | /** |
||
128 | * Generate URL to jump to {$pageNumber}. |
||
129 | * |
||
130 | * @param int $pageNumber |
||
131 | * @return string |
||
132 | * |
||
133 | * @throws \RuntimeException |
||
134 | */ |
||
135 | 4 | private function _buildPageUrl(int $pageNumber) : string |
|
136 | { |
||
137 | 4 | $parameters = UrlHelper::queryParameters(); |
|
138 | 4 | $parameters['page'] = $pageNumber; |
|
139 | |||
140 | 4 | return \request()->url() . '?' . \http_build_query($parameters); |
|
141 | } |
||
142 | |||
143 | /** |
||
144 | * @return null|string |
||
145 | * @throws \RuntimeException |
||
146 | */ |
||
147 | 4 | private function _getPreviousPageUrl() : ? string |
|
148 | { |
||
149 | 4 | $previousPage = $this->_currentPage - 1; |
|
150 | 4 | if ($previousPage < 1) |
|
151 | { |
||
152 | 1 | return null; |
|
153 | } |
||
154 | |||
155 | 3 | return $this->_buildPageUrl($previousPage); |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * Renders a list of pages. |
||
160 | * |
||
161 | * @return string |
||
162 | * @throws \RuntimeException |
||
163 | */ |
||
164 | 4 | private function _renderPageList() : string |
|
165 | { |
||
166 | 4 | $end = $this->_getEndPage(); |
|
167 | |||
168 | 4 | $pageList = ''; |
|
169 | 4 | for ($i = $this->_getStartPage(); $i <= $end; $i ++) |
|
170 | { |
||
171 | 4 | $pageList .= $this->_renderListItem($i, $this->_buildPageUrl($i)); |
|
172 | } |
||
173 | |||
174 | 4 | return $pageList; |
|
175 | } |
||
176 | |||
177 | /** |
||
178 | * @return int |
||
179 | */ |
||
180 | 4 | private function _getEndPage() : int |
|
181 | { |
||
182 | 4 | $end = $this->_currentPage + $this->_surroundingPages; |
|
183 | 4 | $pageCount = $this->pageCount(); |
|
184 | |||
185 | 4 | return $end > $pageCount ? $pageCount : $end; |
|
186 | } |
||
187 | |||
188 | /** |
||
189 | * @return int |
||
190 | */ |
||
191 | 7 | public function pageCount() : int |
|
192 | { |
||
193 | 7 | if (empty($this->_perPage)) |
|
194 | { |
||
195 | 1 | return 1; |
|
196 | } |
||
197 | |||
198 | 6 | $queryCount = $this->getQueryCount(); |
|
199 | 6 | if ($queryCount < $this->_perPage) |
|
200 | { |
||
201 | 1 | return 1; |
|
202 | } |
||
203 | |||
204 | 5 | return (int) \ceil($queryCount / $this->_perPage); |
|
205 | } |
||
206 | |||
207 | /** |
||
208 | * @return int |
||
209 | */ |
||
210 | 4 | private function _getStartPage() : int |
|
211 | { |
||
212 | 4 | $start = $this->_currentPage - $this->_surroundingPages; |
|
213 | |||
214 | 4 | return $start < 1 ? 1 : $start; |
|
215 | } |
||
216 | |||
217 | /** |
||
218 | * @return null|string |
||
219 | * @throws \RuntimeException |
||
220 | */ |
||
221 | 4 | private function _getNextPageUrl() : ? string |
|
222 | { |
||
223 | 4 | if ($this->_currentPage >= $this->pageCount()) |
|
224 | { |
||
225 | 1 | return null; |
|
226 | } |
||
227 | |||
228 | 3 | return $this->_buildPageUrl($this->_currentPage + 1); |
|
229 | } |
||
230 | |||
231 | /** |
||
232 | * @return null|string |
||
233 | * @throws \RuntimeException |
||
234 | */ |
||
235 | 4 | private function _getLastPageUrl() : ? string |
|
236 | { |
||
237 | 4 | $lastPage = $this->pageCount(); |
|
238 | 4 | if ($this->_currentPage + $this->_surroundingPages >= $lastPage) |
|
239 | { |
||
240 | 2 | return null; |
|
241 | } |
||
242 | |||
243 | 2 | return $this->_buildPageUrl($lastPage); |
|
244 | } |
||
245 | |||
246 | /** |
||
247 | * @return Builder |
||
248 | */ |
||
249 | 2 | protected function _shapeData() : Builder |
|
250 | { |
||
251 | 2 | if (empty($this->_perPage)) |
|
252 | { |
||
253 | 1 | return $this->_dataTable->query(); |
|
254 | } |
||
255 | |||
256 | 1 | return $this->_dataTable->query()->limit($this->_perPage)->offset(($this->_currentPage - 1) * $this->_perPage); |
|
257 | } |
||
258 | |||
259 | 9 | protected function _afterInit() : void |
|
263 | } |