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

BasePaginator::previousPageNumber()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 10
rs 10
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
	protected function getPageLink($pageNumber, $withBaseUrl = false): ?string
201
	{
202
		if (!empty($pageNumber)){
203
			return $this->getUri($withBaseUrl) . self::PER_PAGE .'=' . $this->perPage . '&'. self::PAGE .'=' . $pageNumber;
204
		}
205
		return null;
206
	}
207
208
	/**
209
	 * @param bool $withBaseUrl
210
	 * @param $pageItemsCount
211
	 * @return string|null
212
	 */
213
	public function getPagination(bool $withBaseUrl = false, $pageItemsCount = null): ?string
214
	{
215
		$totalPages = $this->lastPageNumber();
216
		if ($totalPages <= 1) {
217
			return null;
218
		}
219
220
		if (!is_null($pageItemsCount) && $pageItemsCount < self::MINIMUM_PAGE_ITEMS_COUNT) {
221
			$pageItemsCount = self::MINIMUM_PAGE_ITEMS_COUNT;
222
		}
223
224
		$pagination = '<ul class="'. self::PAGINATION_CLASS .'">';
225
		$currentPage = $this->currentPageNumber();
226
		
227
		if ($currentPage > 1) {
228
			$pagination .= $this->getPreviousPageItem($this->previousPageLink());
229
		}
230
231
		if ($pageItemsCount) {
232
			$pagination = $this->getPageItems($pagination, $currentPage, $totalPages, $pageItemsCount, $withBaseUrl);
233
		}
234
235
		if ($currentPage < $totalPages) {
236
			$pagination .= $this->getNextPageItem($this->nextPageLink());
237
		}
238
239
		$pagination .= '</ul>';
240
241
		return $pagination;
242
	}
243
244
	/**
245
	 * @param $pagination
246
	 * @param $currentPage
247
	 * @param $totalPages
248
	 * @param $pageItemsCount
249
	 * @param bool $withBaseUrl
250
	 * @return mixed|string
251
	 */
252
	protected function getPageItems($pagination, $currentPage, $totalPages, $pageItemsCount, bool $withBaseUrl = false)
253
	{
254
		$startPage = $this->calculateStartPage($currentPage, $pageItemsCount);
255
		$endPage = $this->calculateEndPage($startPage, $totalPages, $pageItemsCount);
256
257
		$pagination = $this->addFirstPageLink($pagination, $startPage);
258
259
		$links = $this->links($withBaseUrl);
260
		$pagination = $this->getItemsLinks($pagination, $startPage, $endPage, $currentPage, $links);
261
262
		return $this->addLastPageLink($pagination, $endPage, $totalPages, $links);
263
	}
264
265
	/**
266
	 * @param string|null $nextPageLink
267
	 * @return string
268
	 */
269
	protected function getNextPageItem(?string $nextPageLink): string
270
	{
271
		$link = '';
272
		if (!empty($nextPageLink)){
273
			$link = '<li><a href="' . $nextPageLink . '">Next &raquo;</a></li>';
274
		}
275
		return $link;
276
	}
277
278
	/**
279
	 * @param string|null $previousPageLink
280
	 * @return string
281
	 */
282
	protected function getPreviousPageItem(?string $previousPageLink): string
283
	{
284
		$link = '';
285
		if (!empty($previousPageLink)){
286
			$link = '<li><a href="' . $previousPageLink . '">&laquo; Previous</a></li>';
287
		}
288
		return $link;
289
	}
290
291
	protected function getItemsLinks($pagination, $startPage, $endPage, $currentPage, array $links)
292
	{
293
		for ($i = $startPage; $i <= $endPage; $i++) {
294
			$active = $i == $currentPage ? 'class="'. self::PAGINATION_CLASS_ACTIVE .'"' : '';
295
			$pagination .= '<li ' . $active . '><a href="' . $links[$i - 1] . '">' . $i . '</a></li>';
296
		}
297
		return $pagination;
298
	}
299
300
	protected function calculateStartPage($currentPage, $pageItemsCount)
301
	{
302
		return max(1, $currentPage - ceil(($pageItemsCount - self::EDGE_PADDING) / 2));
303
	}
304
305
	protected function calculateEndPage($startPage, $totalPages, $pageItemsCount)
306
	{
307
		$endPage = min($totalPages, $startPage + $pageItemsCount - self::EDGE_PADDING);
308
		return max(1, $endPage - $pageItemsCount + self::EDGE_PADDING);
309
	}
310
311
	protected function addFirstPageLink($pagination, $startPage)
312
	{
313
		if ($startPage > 1) {
314
			$pagination .= '<li><a href="' . $this->firstPageLink() . '">'. self::FIRST_PAGE_NUMBER .'</a></li>';
315
			if ($startPage > 2) {
316
				$pagination .= '<li><span>...</span></li>';
317
			}
318
		}
319
		return $pagination;
320
	}
321
322
	protected function addLastPageLink($pagination, $endPage, $totalPages, $links)
323
	{
324
		if ($endPage < $totalPages) {
325
			if ($endPage < $totalPages - 1) {
326
				$pagination .= '<li><span>...</span></li>';
327
			}
328
			$pagination .= '<li><a href="' . $links[$totalPages - 1] . '">' . $totalPages . '</a></li>';
329
		}
330
		return $pagination;
331
	}
332
}