Passed
Pull Request — master (#241)
by Arman
03:23
created

Paginator::data()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 12
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.6
13
 */
14
15
namespace Quantum\Paginator;
16
17
use Quantum\Libraries\Database\Contracts\DbalInterface;
18
use Quantum\Paginator\Contracts\PaginatorInterface;
19
use Quantum\Paginator\Constants\Pagination;
20
use Quantum\Model\ModelCollection;
21
22
/**
23
 * Class Paginator
24
 * @package Quantum\Paginator
25
 */
26
class Paginator implements PaginatorInterface
27
{
28
29
    /**
30
     * @var string
31
     */
32
    protected $baseUrl;
33
34
    /**
35
     * @var int
36
     */
37
    protected $total;
38
39
    /**
40
     * @var int
41
     */
42
    protected $perPage;
43
44
    /**
45
     * @var int
46
     */
47
    protected $page;
48
49
    /**
50
     * @var ModelCollection|null
51
     */
52
    protected $data = null;
53
54
    /**
55
     * @var DbalInterface
56
     */
57
    private $ormInstance;
58
59
    /**
60
     * @var string|null
61
     */
62
    private $modelClass;
63
64
    /**
65
     * @param DbalInterface $ormInstance
66
     * @param int $perPage
67
     * @param int $page
68
     * @param string|null $modelClass
69
     */
70
    public function __construct(DbalInterface $ormInstance, string $modelClass, int $perPage, int $page = 1)
71
    {
72
        $this->baseUrl = base_url();
73
        $this->ormInstance = $ormInstance;
74
        $this->modelClass = $modelClass;
75
76
        $this->perPage = $perPage;
77
        $this->page = $page;
78
79
        $this->total = $this->ormInstance->count();
80
    }
81
82
    /**
83
     * @inheritDoc
84
     */
85
    public function data(): ModelCollection
86
    {
87
        $ormInstances = $this->ormInstance
88
            ->limit($this->perPage)
89
            ->offset($this->perPage * ($this->page - 1))
90
            ->get();
91
92
            $models = array_map(function ($item) {
93
                return wrapToModel($item, $this->modelClass);
0 ignored issues
show
Bug introduced by
It seems like $this->modelClass can also be of type null; however, parameter $modelClass of wrapToModel() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
                return wrapToModel($item, /** @scrutinizer ignore-type */ $this->modelClass);
Loading history...
94
            }, $ormInstances);
95
96
        return new ModelCollection($models);
97
    }
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function firstItem()
103
    {
104
        $data = $this->data();
105
106
        if ($data->isEmpty()) {
107
            return null;
108
        }
109
110
        return $data->first();
111
    }
112
113
    /**
114
     * @inheritDoc
115
     */
116
    public function lastItem()
117
    {
118
        $data = $this->data();
119
120
        if ($data->isEmpty()) {
121
            return null;
122
        }
123
124
        return $data->last();
125
    }
126
127
    /**
128
     * @inheritDoc
129
     */
130
    public function currentPageNumber(): int
131
    {
132
        return $this->page;
133
    }
134
135
    /**
136
     * @inheritDoc
137
     */
138
    public function previousPageNumber(): ?int
139
    {
140
        if ($this->page > 1) {
141
            return $this->page - 1;
142
        }
143
144
        if ($this->page == 1) {
145
            return $this->page;
146
        }
147
148
        return null;
149
    }
150
151
    /**
152
     * @inheritDoc
153
     */
154
    public function nextPageNumber(): ?int
155
    {
156
        if ($this->page < $this->lastPageNumber()) {
157
            return $this->page + 1;
158
        }
159
160
        if ($this->page == $this->lastPageNumber()) {
161
            return $this->page;
162
        }
163
164
        return null;
165
    }
166
167
    /**
168
     * @inheritDoc
169
     */
170
    public function lastPageNumber(): int
171
    {
172
        return (int)ceil($this->total() / $this->perPage);
173
    }
174
175
    /**
176
     * @inheritDoc
177
     */
178
    public function currentPageLink(bool $withBaseUrl = false): ?string
179
    {
180
        return $this->getPageLink($this->page, $withBaseUrl);
181
    }
182
183
    /**
184
     * @inheritDoc
185
     */
186
    public function firstPageLink(bool $withBaseUrl = false): ?string
187
    {
188
        return $this->getPageLink(Pagination::FIRST_PAGE_NUMBER, $withBaseUrl);
189
    }
190
191
    /**
192
     * @inheritDoc
193
     */
194
    public function previousPageLink(bool $withBaseUrl = false): ?string
195
    {
196
        return $this->getPageLink($this->previousPageNumber(), $withBaseUrl);
197
    }
198
199
    /**
200
     * @inheritDoc
201
     */
202
    public function nextPageLink(bool $withBaseUrl = false): ?string
203
    {
204
        return $this->getPageLink($this->nextPageNumber(), $withBaseUrl);
205
    }
206
207
    /**
208
     * @inheritDoc
209
     */
210
    public function lastPageLink(bool $withBaseUrl = false): ?string
211
    {
212
        return $this->getPageLink($this->lastPageNumber(), $withBaseUrl);
213
    }
214
215
    /**
216
     * @inheritDoc
217
     */
218
    public function perPage(): int
219
    {
220
        return $this->perPage;
221
    }
222
223
    /**
224
     * @inheritDoc
225
     */
226
    public function total(): int
227
    {
228
        return $this->total;
229
    }
230
231
    /**
232
     * @inheritDoc
233
     */
234
    public function links(bool $withBaseUrl = false): array
235
    {
236
        $links = [];
237
238
        for ($i = 1; $i <= $this->lastPageNumber(); $i++) {
239
            $links[] = $this->getPageLink($i, $withBaseUrl);
240
        }
241
242
        return $links;
243
    }
244
245
    /**
246
     * @inheritDoc
247
     */
248
    public function getPagination(bool $withBaseUrl = false, $pageItemsCount = null): ?string
249
    {
250
        $totalPages = $this->lastPageNumber();
251
        $currentPage = $this->currentPageNumber();
252
253
        if ($totalPages <= 1) {
254
            return null;
255
        }
256
257
        $pageItemsCount = !empty($pageItemsCount) && $pageItemsCount < Pagination::MINIMUM_PAGE_ITEMS_COUNT
258
            ? Pagination::MINIMUM_PAGE_ITEMS_COUNT
259
            : $pageItemsCount;
260
261
        $pagination = ['<ul class="' . Pagination::PAGINATION_CLASS . '">'];
262
263
        if ($currentPage > 1) {
264
            $pagination[] = $this->getPreviousPageItem($this->previousPageLink());
265
        }
266
267
        if ($pageItemsCount) {
268
            $links = $this->links($withBaseUrl);
269
270
            list($startPage, $endPage) = $this->calculateStartEndPages($currentPage, $totalPages, $pageItemsCount);
271
272
            $pagination[] = $this->addFirstPageLink($startPage);
273
            $pagination[] = $this->getItemsLinks($startPage, $endPage, $currentPage, $links);
274
            $pagination[] = $this->addLastPageLink($endPage, $totalPages, $links);
275
        }
276
277
        if ($currentPage < $totalPages) {
278
            $pagination[] = $this->getNextPageItem($this->nextPageLink());
279
        }
280
281
        $pagination[] = '</ul>';
282
283
        return implode('', $pagination);
284
    }
285
286
    /**
287
     * @param bool $withBaseUrl
288
     * @return string
289
     */
290
    protected function getUri(bool $withBaseUrl = false): string
291
    {
292
        $routeUrl = preg_replace('/([?&](page|per_page)=\d+)/', '', route_uri());
293
        $routeUrl = preg_replace('/&/', '?', $routeUrl, 1);
294
        $url = $routeUrl;
295
296
        if ($withBaseUrl) {
297
            $url = $this->baseUrl . $routeUrl;
298
        }
299
300
        $delimiter = strpos($url, '?') ? '&' : '?';
301
302
        return $url . $delimiter;
303
    }
304
305
    /**
306
     * @param $pageNumber
307
     * @param bool $withBaseUrl
308
     * @return string|null
309
     */
310
    protected function getPageLink($pageNumber, bool $withBaseUrl = false): ?string
311
    {
312
        if (!empty($pageNumber)) {
313
            return $this->getUri($withBaseUrl) . Pagination::PER_PAGE . '=' . $this->perPage . '&' . Pagination::PAGE . '=' . $pageNumber;
314
        }
315
316
        return null;
317
    }
318
319
    /**
320
     * @param string|null $nextPageLink
321
     * @return string
322
     */
323
    protected function getNextPageItem(?string $nextPageLink): string
324
    {
325
        $link = '';
326
        if (!empty($nextPageLink)) {
327
            $link = '<li><a href="' . $nextPageLink . '">' . t('common.pagination.next') . '</a></li>';
328
        }
329
330
        return $link;
331
    }
332
333
    /**
334
     * @param string|null $previousPageLink
335
     * @return string
336
     */
337
    protected function getPreviousPageItem(?string $previousPageLink): string
338
    {
339
        $link = '';
340
        if (!empty($previousPageLink)) {
341
            $link = '<li><a href="' . $previousPageLink . '">' . t('common.pagination.prev') . '</a></li>';
342
        }
343
        return $link;
344
    }
345
346
    /**
347
     * @param $startPage
348
     * @param $endPage
349
     * @param $currentPage
350
     * @param array $links
351
     * @return string
352
     */
353
    protected function getItemsLinks($startPage, $endPage, $currentPage, array $links): string
354
    {
355
        $pagination = '';
356
        for ($i = $startPage; $i <= $endPage; $i++) {
357
            $active = $i == $currentPage ? 'class="' . Pagination::PAGINATION_CLASS_ACTIVE . '"' : '';
358
            $pagination .= '<li ' . $active . '><a href="' . $links[$i - 1] . '">' . $i . '</a></li>';
359
        }
360
        return $pagination;
361
    }
362
363
    /**
364
     * @param $currentPage
365
     * @param $totalPages
366
     * @param $pageItemsCount
367
     * @return array
368
     */
369
    protected function calculateStartEndPages($currentPage, $totalPages, $pageItemsCount): array
370
    {
371
        $startPage = max(1, $currentPage - ceil(($pageItemsCount - Pagination::EDGE_PADDING) / 2));
372
        $endPage = min($totalPages, $startPage + $pageItemsCount - Pagination::EDGE_PADDING);
373
374
        return [$startPage, $endPage];
375
    }
376
377
    /**
378
     * @param $startPage
379
     * @return string
380
     */
381
    protected function addFirstPageLink($startPage): string
382
    {
383
        $pagination = '';
384
385
        if ($startPage > 1) {
386
            $pagination .= '<li><a href="' . $this->firstPageLink() . '">' . Pagination::FIRST_PAGE_NUMBER . '</a></li>';
387
            if ($startPage > 2) {
388
                $pagination .= '<li><span>...</span></li>';
389
            }
390
        }
391
392
        return $pagination;
393
    }
394
395
    /**
396
     * @param $endPage
397
     * @param $totalPages
398
     * @param $links
399
     * @return string
400
     */
401
    protected function addLastPageLink($endPage, $totalPages, $links): string
402
    {
403
        $pagination = '';
404
405
        if ($endPage < $totalPages) {
406
            if ($endPage < $totalPages - 1) {
407
                $pagination .= '<li><span>...</span></li>';
408
            }
409
            $pagination .= '<li><a href="' . $links[$totalPages - 1] . '">' . $totalPages . '</a></li>';
410
        }
411
412
        return $pagination;
413
    }
414
}