Passed
Pull Request — master (#154)
by
unknown
02:47
created

BasePaginator::getPagination()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
nc 17
nop 2
dl 0
loc 34
rs 8.6666
c 4
b 0
f 0
1
<?php
2
3
namespace Quantum\Libraries\Database;
4
5
abstract class BasePaginator implements PaginatorInterface
6
{
7
	/**
8
	 * @var string
9
	 */
10
	protected const PAGINATION_CLASS = 'pagination';
11
12
	/**
13
	 * @var string
14
	 */
15
	protected const PAGINATION_CLASS_ACTIVE = 'active';
16
17
	/**
18
	 * @var string
19
	 */
20
	protected const PER_PAGE = 'per_page';
21
22
	/**
23
	 * @var string
24
	 */
25
	protected const PAGE = 'page';
26
27
	/**
28
	 * @var int
29
	 */
30
	protected const FIRST_PAGE_NUMBER = 1;
31
32
	/**
33
	 * @var int
34
	 */
35
	protected const MINIMUM_PAGE_ITEMS_COUNT = 3;
36
37
	/**
38
	 * @var int
39
	 */
40
	protected const EDGE_PADDING = 3;
41
42
	/**
43
	 * @var int
44
	 */
45
	protected $total;
46
47
	/**
48
	 * @var string
49
	 */
50
	protected $baseUrl;
51
52
	/**
53
	 * @var int
54
	 */
55
	protected $perPage;
56
57
	/**
58
	 * @var int
59
	 */
60
	protected $page;
61
62
	/**
63
	 * @return int
64
	 */
65
	public function currentPageNumber(): int
66
	{
67
		return $this->page;
68
	}
69
70
	/**
71
	 * @param bool $withBaseUrl
72
	 * @return string|null
73
	 */
74
	public function currentPageLink(bool $withBaseUrl = false): ?string
75
	{
76
		return $this->getPageLink($this->page, $withBaseUrl);
77
	}
78
79
	/**
80
	 * @return int|null
81
	 */
82
	public function previousPageNumber(): ?int
83
	{
84
		$previous = null;
85
		if ($this->page > 1) {
86
			$previous = $this->page - 1;
87
		} elseif ($this->page == 1) {
88
			$previous = $this->page;
89
		}
90
91
		return $previous;
92
	}
93
94
	/**
95
	 * @param bool $withBaseUrl
96
	 * @return string|null
97
	 */
98
	public function previousPageLink(bool $withBaseUrl = false): ?string
99
	{
100
		return $this->getPageLink($this->previousPageNumber(), $withBaseUrl);
101
	}
102
103
	/**
104
	 * @return int|null
105
	 */
106
	public function nextPageNumber(): ?int
107
	{
108
		$next = null;
109
		if ($this->page < $this->lastPageNumber()) {
110
			$next = $this->page + 1;
111
		} elseif ($this->page == $this->lastPageNumber()) {
112
			$next = $this->page;
113
		}
114
		return $next;
115
	}
116
117
	/**
118
	 * @param bool $withBaseUrl
119
	 * @return string|null
120
	 */
121
	public function nextPageLink(bool $withBaseUrl = false): ?string
122
	{
123
		return $this->getPageLink($this->nextPageNumber(), $withBaseUrl);
124
	}
125
126
	/**
127
	 * @param bool $withBaseUrl
128
	 * @return string|null
129
	 */
130
	public function firstPageLink(bool $withBaseUrl = false): ?string
131
	{
132
		return $this->getPageLink(self::FIRST_PAGE_NUMBER, $withBaseUrl);
133
	}
134
135
	/**
136
	 * @return int
137
	 */
138
	public function lastPageNumber(): int
139
	{
140
		return (int)ceil($this->total() / $this->perPage);
141
	}
142
143
	/**
144
	 * @param bool $withBaseUrl
145
	 * @return string|null
146
	 */
147
	public function lastPageLink(bool $withBaseUrl = false): ?string
148
	{
149
		return $this->getPageLink($this->lastPageNumber(), $withBaseUrl);
150
	}
151
152
	/**
153
	 * @return int
154
	 */
155
	public function perPage(): int
156
	{
157
		return $this->perPage;
158
	}
159
160
	/**
161
	 * @return int
162
	 */
163
	public function total(): int
164
	{
165
		return $this->total;
166
	}
167
168
	/**
169
	 * @param bool $withBaseUrl
170
	 * @return array
171
	 */
172
	public function links(bool $withBaseUrl = false): array
173
	{
174
		$links = [];
175
		for ($i = 1; $i <= $this->lastPageNumber(); $i++) {
176
			$links[] = $this->getPageLink($i, $withBaseUrl);
177
		}
178
179
		return $links;
180
	}
181
182
	/**
183
	 * @param bool $withBaseUrl
184
	 * @return string
185
	 */
186
	protected function getUri(bool $withBaseUrl = false): string
187
	{
188
		$routeUrl = preg_replace('/([?&](page|per_page)=\d+)/', '', route_uri());
189
		$routeUrl = preg_replace('/&/', '?', $routeUrl, 1);
190
		$url = $routeUrl;
191
192
		if ($withBaseUrl) {
193
			$url = $this->baseUrl . $routeUrl;
194
		}
195
196
		$delimiter = strpos($url, '?') ? '&' : '?';
197
		return $url . $delimiter;
198
	}
199
200
	/**
201
	 * @param $pageNumber
202
	 * @param bool $withBaseUrl
203
	 * @return string|null
204
	 */
205
	protected function getPageLink($pageNumber, bool $withBaseUrl = false): ?string
206
	{
207
		if (!empty($pageNumber)){
208
			return $this->getUri($withBaseUrl) . self::PER_PAGE .'=' . $this->perPage . '&'. self::PAGE .'=' . $pageNumber;
209
		}
210
		return null;
211
	}
212
213
	/**
214
	 * @param bool $withBaseUrl
215
	 * @param $pageItemsCount
216
	 * @return string|null
217
	 */
218
	public function getPagination(bool $withBaseUrl = false, $pageItemsCount = null): ?string
219
	{
220
		$totalPages = $this->lastPageNumber();
221
		if ($totalPages <= 1) {
222
			return null;
223
		}
224
225
		if (!is_null($pageItemsCount) && $pageItemsCount < self::MINIMUM_PAGE_ITEMS_COUNT) {
226
			$pageItemsCount = self::MINIMUM_PAGE_ITEMS_COUNT;
227
		}
228
229
		$pagination = '<ul class="'. self::PAGINATION_CLASS .'">';
230
		$currentPage = $this->currentPageNumber();
231
		
232
		if ($currentPage > 1) {
233
			$pagination .= $this->getPreviousPageItem($this->previousPageLink());
234
		}
235
236
		if ($pageItemsCount) {
237
			$links = $this->links($withBaseUrl);
238
			$startPage = $this->calculateStartPage($currentPage, $pageItemsCount);
239
			$endPage = $this->calculateEndPage($startPage, $totalPages, $pageItemsCount);
240
			$pagination = $this->addFirstPageLink($pagination, $startPage);
241
			$pagination = $this->getItemsLinks($pagination, $startPage, $endPage, $currentPage, $links);
242
			$pagination = $this->addLastPageLink($pagination, $endPage, $totalPages, $links);
243
		}
244
245
		if ($currentPage < $totalPages) {
246
			$pagination .= $this->getNextPageItem($this->nextPageLink());
247
		}
248
249
		$pagination .= '</ul>';
250
251
		return $pagination;
252
	}
253
254
	/**
255
	 * @param string|null $nextPageLink
256
	 * @return string
257
	 */
258
	protected function getNextPageItem(?string $nextPageLink): string
259
	{
260
		$link = '';
261
		if (!empty($nextPageLink)){
262
			$link = '<li><a href="' . $nextPageLink . '">Next &raquo;</a></li>';
263
		}
264
		return $link;
265
	}
266
267
	/**
268
	 * @param string|null $previousPageLink
269
	 * @return string
270
	 */
271
	protected function getPreviousPageItem(?string $previousPageLink): string
272
	{
273
		$link = '';
274
		if (!empty($previousPageLink)){
275
			$link = '<li><a href="' . $previousPageLink . '">&laquo; Previous</a></li>';
276
		}
277
		return $link;
278
	}
279
280
	/**
281
	 * @param $pagination
282
	 * @param $startPage
283
	 * @param $endPage
284
	 * @param $currentPage
285
	 * @param array $links
286
	 * @return mixed|string
287
	 */
288
	protected function getItemsLinks($pagination, $startPage, $endPage, $currentPage, array $links)
289
	{
290
		for ($i = $startPage; $i <= $endPage; $i++) {
291
			$active = $i == $currentPage ? 'class="'. self::PAGINATION_CLASS_ACTIVE .'"' : '';
292
			$pagination .= '<li ' . $active . '><a href="' . $links[$i - 1] . '">' . $i . '</a></li>';
293
		}
294
		return $pagination;
295
	}
296
297
	/**
298
	 * @param $currentPage
299
	 * @param $pageItemsCount
300
	 * @return mixed
301
	 */
302
	protected function calculateStartPage($currentPage, $pageItemsCount)
303
	{
304
		return max(1, $currentPage - ceil(($pageItemsCount - self::EDGE_PADDING) / 2));
305
	}
306
307
	/**
308
	 * @param $startPage
309
	 * @param $totalPages
310
	 * @param $pageItemsCount
311
	 * @return mixed
312
	 */
313
	protected function calculateEndPage($startPage, $totalPages, $pageItemsCount)
314
	{
315
		$endPage = min($totalPages, $startPage + $pageItemsCount - self::EDGE_PADDING);
316
		return max(1, $endPage - $pageItemsCount + self::EDGE_PADDING);
317
	}
318
319
	/**
320
	 * @param $pagination
321
	 * @param $startPage
322
	 * @return mixed|string
323
	 */
324
	protected function addFirstPageLink($pagination, $startPage)
325
	{
326
		if ($startPage > 1) {
327
			$pagination .= '<li><a href="' . $this->firstPageLink() . '">'. self::FIRST_PAGE_NUMBER .'</a></li>';
328
			if ($startPage > 2) {
329
				$pagination .= '<li><span>...</span></li>';
330
			}
331
		}
332
		return $pagination;
333
	}
334
335
	/**
336
	 * @param $pagination
337
	 * @param $endPage
338
	 * @param $totalPages
339
	 * @param $links
340
	 * @return mixed|string
341
	 */
342
	protected function addLastPageLink($pagination, $endPage, $totalPages, $links)
343
	{
344
		if ($endPage < $totalPages) {
345
			if ($endPage < $totalPages - 1) {
346
				$pagination .= '<li><span>...</span></li>';
347
			}
348
			$pagination .= '<li><a href="' . $links[$totalPages - 1] . '">' . $totalPages . '</a></li>';
349
		}
350
		return $pagination;
351
	}
352
}