Passed
Push — develop ( 1ae069...6c4a60 )
by nguereza
30:00 queued 27:46
created

Pagination::getInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 2
nop 0
dl 0
loc 17
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Platine Pagination
5
 *
6
 * Platine Pagination is a lightweight PHP paginator, for generating pagination controls
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Pagination
11
 * Copyright (c) 2014 Jason Grimes
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file Pagination.php
34
 *
35
 *  The pagination main class
36
 *
37
 *  @package    Platine\Pagination
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Pagination;
49
50
use InvalidArgumentException;
51
use Platine\Pagination\Renderer\DefaultRenderer;
52
use Platine\Pagination\UrlGenerator\SimpleUrlGenerator;
53
54
/**
55
 * Class Pagination
56
 * @package Platine\Pagination
57
 */
58
class Pagination
59
{
0 ignored issues
show
Coding Style introduced by
Opening brace must not be followed by a blank line
Loading history...
60
61
    /**
62
     * The URL generator instance
63
     * @var UrlGeneratorInterface
64
     */
65
    protected UrlGeneratorInterface $urlGenerator;
66
67
    /**
68
     * The pagination renderer
69
     * @var RendererInterface
70
     */
71
    protected RendererInterface $renderer;
72
73
    /**
74
     * The pagination total items
75
     * @var int
76
     */
77
    protected int $totalItems = 0;
78
79
    /**
80
     * The pagination number of items per page
81
     * @var int
82
     */
83
    protected int $itemsPerPage = 10;
84
85
    /**
86
     * The pagination current page number
87
     * @var int
88
     */
89
    protected int $currentPage = 1;
90
91
    /**
92
     * The pagination total page after calculation
93
     * @var int
94
     */
95
    protected int $totalPages = 0;
96
97
    /**
98
     * The pagination max page to show
99
     * @var int
100
     */
101
    protected int $maxPages = 10;
102
103
    /**
104
     * The pagination previous text
105
     * @var string
106
     */
107
    protected string $previousText = 'Previous';
108
109
    /**
110
     * The pagination next text
111
     * @var string
112
     */
113
    protected string $nextText = 'Next';
114
115
    /**
116
     * Create new instance
117
     * @param UrlGeneratorInterface|null $urlGenerator
118
     * @param RendererInterface|null $renderer
119
     */
120
    public function __construct(
121
        ?UrlGeneratorInterface $urlGenerator = null,
122
        ?RendererInterface $renderer = null
123
    ) {
124
        $this->urlGenerator = $urlGenerator
125
                               ? $urlGenerator
126
                               : new SimpleUrlGenerator();
127
128
        $this->renderer = $renderer
129
                            ? $renderer
130
                            : new DefaultRenderer();
131
132
        $this->updateTotalPages();
133
    }
134
135
    /**
136
     * Whether the pagination has next page
137
     * @return bool
138
     */
139
    public function hasNextPage(): bool
140
    {
141
        return $this->currentPage < $this->totalPages;
142
    }
143
144
    /**
145
     * Return the next page number
146
     * @return int|null
147
     */
148
    public function getNextPage(): ?int
149
    {
150
        if ($this->hasNextPage()) {
151
            return $this->currentPage + 1;
152
        }
153
154
        return null;
155
    }
156
157
    /**
158
     * Return the next page URL
159
     * @return string|null
160
     */
161
    public function getNextUrl(): ?string
162
    {
163
        if ($this->hasNextPage()) {
164
            return $this->getPageUrl($this->getNextPage());
0 ignored issues
show
Bug introduced by
It seems like $this->getNextPage() can also be of type null; however, parameter $page of Platine\Pagination\Pagination::getPageUrl() does only seem to accept integer, 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

164
            return $this->getPageUrl(/** @scrutinizer ignore-type */ $this->getNextPage());
Loading history...
165
        }
166
167
        return null;
168
    }
169
170
    /**
171
     * Whether the pagination has previous page
172
     * @return bool
173
     */
174
    public function hasPreviousPage(): bool
175
    {
176
        return $this->currentPage > 1;
177
    }
178
179
    /**
180
     * Return the previous page number
181
     * @return int|null
182
     */
183
    public function getPreviousPage(): ?int
184
    {
185
        if ($this->hasPreviousPage()) {
186
            return $this->currentPage - 1;
187
        }
188
189
        return null;
190
    }
191
192
    /**
193
     * Return the previous page URL
194
     * @return string|null
195
     */
196
    public function getPreviousUrl(): ?string
197
    {
198
        if ($this->hasPreviousPage()) {
199
            return $this->getPageUrl($this->getPreviousPage());
0 ignored issues
show
Bug introduced by
It seems like $this->getPreviousPage() can also be of type null; however, parameter $page of Platine\Pagination\Pagination::getPageUrl() does only seem to accept integer, 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

199
            return $this->getPageUrl(/** @scrutinizer ignore-type */ $this->getPreviousPage());
Loading history...
200
        }
201
202
        return null;
203
    }
204
205
    /**
206
     * Return the offset so that it can be used
207
     * in other contexts, for example in SQL query
208
     * @return int
209
     */
210
    public function getOffset(): int
211
    {
212
        return ($this->currentPage - 1) * $this->itemsPerPage;
213
    }
214
215
    /**
216
     * Return the page URL for the given number
217
     * @param int $page
218
     * @return string
219
     */
220
    public function getPageUrl(int $page): string
221
    {
222
        return $this->urlGenerator->generatePageUrl($page);
223
    }
224
225
    /**
226
     * Set the URL generator to use
227
     * @param UrlGeneratorInterface $urlGenerator
228
     * @return $this
229
     */
230
    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator): self
231
    {
232
        $this->urlGenerator = $urlGenerator;
233
234
        return $this;
235
    }
236
237
    /**
238
     * Set the renderer to use
239
     * @param RendererInterface $renderer
240
     * @return $this
241
     */
242
    public function setRenderer(RendererInterface $renderer): self
243
    {
244
        $this->renderer = $renderer;
245
246
        return $this;
247
    }
248
249
    /**
250
     * Set total items
251
     * @param int $totalItems
252
     * @return $this
253
     */
254
    public function setTotalItems(int $totalItems): self
255
    {
256
        $this->totalItems = $totalItems;
257
258
        $this->updateTotalPages();
259
260
        return $this;
261
    }
262
263
    /**
264
     * Set number of items to show per page
265
     * @param int $itemsPerPage
266
     * @return $this
267
     */
268
    public function setItemsPerPage(int $itemsPerPage): self
269
    {
270
        $this->itemsPerPage = $itemsPerPage;
271
272
        $this->updateTotalPages();
273
274
        return $this;
275
    }
276
277
    /**
278
     * Set current page number
279
     * @param int $currentPage
280
     * @return $this
281
     */
282
    public function setCurrentPage(int $currentPage): self
283
    {
284
        $this->currentPage = $currentPage;
285
286
        return $this;
287
    }
288
289
    /**
290
     * Set max pages to show int the links
291
     * @param int $maxPages
292
     * @return $this
293
     */
294
    public function setMaxPages(int $maxPages): self
295
    {
296
        if ($maxPages < 3) {
297
            throw new InvalidArgumentException(sprintf(
298
                'Max page to show can not be less than 3, %d given',
299
                $maxPages
300
            ));
301
        }
302
303
        $this->maxPages = $maxPages;
304
305
        return $this;
306
    }
307
308
    /**
309
     * Set previous text
310
     * @param string $previousText
311
     * @return $this
312
     */
313
    public function setPreviousText(string $previousText): self
314
    {
315
        $this->previousText = $previousText;
316
317
        return $this;
318
    }
319
320
    /**
321
     * Set next text
322
     * @param string $nextText
323
     * @return $this
324
     */
325
    public function setNextText(string $nextText): self
326
    {
327
        $this->nextText = $nextText;
328
329
        return $this;
330
    }
331
332
    /**
333
     * Return the URL generator instance
334
     * @return UrlGeneratorInterface
335
     */
336
    public function getUrlGenerator(): UrlGeneratorInterface
337
    {
338
        return $this->urlGenerator;
339
    }
340
341
    /**
342
     * Return the renderer instance
343
     * @return RendererInterface
344
     */
345
    public function getRenderer(): RendererInterface
346
    {
347
        return $this->renderer;
348
    }
349
350
    /**
351
     * Return the previous text
352
     * @return string
353
     */
354
    public function getPreviousText(): string
355
    {
356
        return $this->previousText;
357
    }
358
359
    /**
360
     * Return the next text
361
     * @return string
362
     */
363
    public function getNextText(): string
364
    {
365
        return $this->nextText;
366
    }
367
368
    /**
369
     * Return the total items
370
     * @return int
371
     */
372
    public function getTotalItems(): int
373
    {
374
        return $this->totalItems;
375
    }
376
377
    /**
378
     * Return the number of items per page
379
     * @return int
380
     */
381
    public function getItemsPerPage(): int
382
    {
383
        return $this->itemsPerPage;
384
    }
385
386
    /**
387
     * Return the current page number
388
     * @return int
389
     */
390
    public function getCurrentPage(): int
391
    {
392
        return $this->currentPage;
393
    }
394
395
    /**
396
     * Return the total number of pages
397
     * @return int
398
     */
399
    public function getTotalPages(): int
400
    {
401
        return $this->totalPages;
402
    }
403
404
    /**
405
     * Return the max pages to show
406
     * @return int
407
     */
408
    public function getMaxPages(): int
409
    {
410
        return $this->maxPages;
411
    }
412
413
    /**
414
     * Return the pages links data
415
     *
416
     * @return array<Page>
417
     */
418
    public function getPages(): array
419
    {
420
        if ($this->totalPages <= 1) {
421
            return [];
422
        }
423
424
        $pages = [];
425
426
        if ($this->totalPages <= $this->maxPages) {
427
            for ($i = 1; $i <= $this->totalPages; $i++) {
428
                $pages[] = $this->createPage($i, $i === $this->currentPage);
429
            }
430
        } else {
431
            // Determine the sliding range, centered around the current page.
432
            $numAdjacents = (int) floor(($this->maxPages - 3) / 2);
433
            $slidingStart = -1;
434
435
            if ($this->currentPage + $numAdjacents > $this->totalPages) {
436
                $slidingStart = $this->totalPages - $this->maxPages + 2;
437
            } else {
438
                $slidingStart = $this->currentPage - $numAdjacents;
439
            }
440
441
            if ($slidingStart < 2) {
442
                $slidingStart = 2;
443
            }
444
445
            $slidingEnd = $slidingStart + $this->maxPages - 3;
446
447
            if ($slidingEnd >= $this->totalPages) {
448
                $slidingEnd = $this->totalPages - 1;
449
            }
450
451
            // Build the list of pages.
452
            $pages[] = $this->createPage(1, 1 === $this->currentPage);
453
454
            if ($slidingStart > 2) {
455
                $pages[] = $this->createPageEllipsis();
456
            }
457
458
            for ($i = $slidingStart; $i <= $slidingEnd; $i++) {
459
                $pages[] = $this->createPage($i, $i === $this->currentPage);
460
            }
461
462
            if ($slidingEnd < $this->totalPages - 1) {
463
                $pages[] = $this->createPageEllipsis();
464
            }
465
466
            $pages[] = $this->createPage(
467
                $this->totalPages,
468
                $this->totalPages === $this->currentPage
469
            );
470
        }
471
472
        return $pages;
473
    }
474
475
    /**
476
     * Render the pagination links
477
     * @return string
478
     */
479
    public function render(): string
480
    {
481
        return $this->renderer->render($this);
482
    }
483
484
    /**
485
     * Return the pagination details
486
     * @return array<string, mixed>
487
     */
488
    public function getInfo(): array
489
    {
490
        $pages = [];
491
        foreach ($this->getPages() as $page) {
492
            $pages[] = $page->getNumber();
493
        }
494
495
        return [
496
            'offset' => $this->getOffset(),
497
            'limit' => $this->getItemsPerPage(),
498
            'total_items' => $this->getTotalItems(),
499
            'total_page' => $this->getTotalPages(),
500
            'page' => $this->getCurrentPage(),
501
            'pages' => $pages,
502
            'next' => $this->getNextPage(),
503
            'previous' => $this->getPreviousPage(),
504
            'url' => $this->getUrlGenerator()->getUrlPattern(),
505
        ];
506
    }
507
508
    /**
509
     * The string representation of pagination
510
     * @return string
511
     */
512
    public function __toString(): string
513
    {
514
        return $this->render();
515
    }
516
517
    /**
518
     * Create page data
519
     * @param int $page
520
     * @param bool $isCurrent
521
     * @return Page
522
     */
523
    protected function createPage(int $page, bool $isCurrent = false): Page
524
    {
525
        return new Page(
526
            $page,
527
            $this->getPageUrl($page),
528
            $isCurrent,
529
        );
530
    }
531
532
    /**
533
     * Create page ellipsis data
534
     * @return Page
535
     */
536
    protected function createPageEllipsis(): Page
537
    {
538
        return new Page(
539
            '...',
540
            null,
541
            false,
542
        );
543
    }
544
545
    /**
546
     * Update the total pages information's
547
     * @return void
548
     */
549
    protected function updateTotalPages(): void
550
    {
551
        $this->totalPages = ($this->itemsPerPage <= 0)
552
                            ? 0
553
                            : (int) ceil($this->totalItems / $this->itemsPerPage);
554
    }
555
}
556