Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.01% covered (success)
81.01%
64 / 79
91.67% covered (success)
91.67%
11 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractPaginator
81.01% covered (success)
81.01%
64 / 79
91.67% covered (success)
91.67%
11 / 12
39.01
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setQueryKey
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setBookends
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getTotal
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPerPage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRange
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQueryKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCurrentPage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNumberOfPages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBookend
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBookends
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 calculateRange
73.68% covered (success)
73.68%
42 / 57
0.00% covered (danger)
0.00%
0 / 1
22.27
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Paginator;
15
16/**
17 * Abstract paginator type class
18 *
19 * @category   Pop
20 * @package    Pop\Paginator
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    4.0.0
25 */
26abstract class AbstractPaginator implements PaginatorInterface
27{
28
29    /**
30     * Total number of items
31     * @var int
32     */
33    protected int $total = 0;
34
35    /**
36     * Number of items per page
37     * @var int
38     */
39    protected int $perPage = 10;
40
41    /**
42     * Range of pages per page
43     * @var int
44     */
45    protected int $range = 10;
46
47    /**
48     * Query key
49     * @var string
50     */
51    protected string $queryKey = 'page';
52
53    /**
54     * Current page property
55     * @var int
56     */
57    protected int $currentPage = 1;
58
59    /**
60     * Number of pages property
61     * @var ?int
62     */
63    protected ?int $numberOfPages = null;
64
65    /**
66     * Current page start index property
67     * @var ?int
68     */
69    protected ?int $start = null;
70
71    /**
72     * Current page end index property
73     * @var ?int
74     */
75    protected ?int $end = null;
76
77    /**
78     * Page bookends
79     * @var array
80     */
81    protected array $bookends = [
82        'start'    => '&laquo;',
83        'previous' => '&lsaquo;',
84        'next'     => '&rsaquo;',
85        'end'      => '&raquo;'
86    ];
87
88    /**
89     * Constructor
90     *
91     * Instantiate the paginator object
92     *
93     * @param  int $total
94     * @param  int $perPage
95     * @param  int $range
96     */
97    public function __construct(int $total, int $perPage = 10, int $range = 10)
98    {
99        $this->total   = $total;
100        $this->perPage = $perPage;
101        $this->range   = $range;
102    }
103
104    /**
105     * Set the query key
106     *
107     * @param  string $key
108     * @return AbstractPaginator
109     */
110    public function setQueryKey(string $key): AbstractPaginator
111    {
112        $this->queryKey = $key;
113        return $this;
114    }
115
116    /**
117     * Set the bookends
118     *
119     * @param  array $bookends
120     * @return AbstractPaginator
121     */
122    public function setBookends(array $bookends): AbstractPaginator
123    {
124        if (array_key_exists('start', $bookends)) {
125            $this->bookends['start'] = $bookends['start'];
126        }
127        if (array_key_exists('previous', $bookends)) {
128            $this->bookends['previous'] = $bookends['previous'];
129        }
130        if (array_key_exists('next', $bookends)) {
131            $this->bookends['next'] = $bookends['next'];
132        }
133        if (array_key_exists('end', $bookends)) {
134            $this->bookends['end'] = $bookends['end'];
135        }
136
137        return $this;
138    }
139
140    /**
141     * Get the content items total
142     *
143     * @return int
144     */
145    public function getTotal(): int
146    {
147        return $this->total;
148    }
149
150    /**
151     * Get the per page
152     *
153     * @return int
154     */
155    public function getPerPage(): int
156    {
157        return $this->perPage;
158    }
159
160    /**
161     * Get the page range
162     *
163     * @return int
164     */
165    public function getRange(): int
166    {
167        return $this->range;
168    }
169
170    /**
171     * Get the query key
172     *
173     * @return string
174     */
175    public function getQueryKey(): string
176    {
177        return $this->queryKey;
178    }
179
180    /**
181     * Get the current page
182     *
183     * @return int
184     */
185    public function getCurrentPage(): int
186    {
187        return $this->currentPage;
188    }
189
190    /**
191     * Get the number of pages
192     *
193     * @return int
194     */
195    public function getNumberOfPages(): int
196    {
197        return $this->numberOfPages;
198    }
199
200    /**
201     * Get a bookend
202     *
203     * @param  string $key
204     * @return string|null
205     */
206    public function getBookend(string $key): string|null
207    {
208        return $this->bookends[$key] ?? null;
209    }
210
211    /**
212     * Get the bookends
213     *
214     * @return array
215     */
216    public function getBookends(): array
217    {
218        return $this->bookends;
219    }
220
221    /**
222     * Calculate the page range
223     *
224     * @param  int $page
225     * @return array
226     */
227    public function calculateRange(int $page = 1): array
228    {
229        $this->currentPage = $page;
230
231        // Calculate the number of pages based on the remainder.
232        $remainder = $this->total % $this->perPage;
233        $this->numberOfPages = ($remainder != 0) ? (floor(($this->total / $this->perPage)) + 1) :
234            floor(($this->total / $this->perPage));
235
236        // Calculate the start index.
237        $this->start = ($page * $this->perPage) - $this->perPage;
238
239        // Calculate the end index.
240        if (($page == $this->numberOfPages) && ($remainder == 0)) {
241            $this->end = $this->start + $this->perPage;
242        } else if ($page == $this->numberOfPages) {
243            $this->end = (($page * $this->perPage) - ($this->perPage - $remainder));
244        } else {
245            $this->end = ($page * $this->perPage);
246        }
247
248        // Calculate if out of range.
249        if ($this->start >= $this->total) {
250            $this->start = 0;
251            $this->end   = $this->perPage;
252        }
253
254        // Check and calculate for any page ranges.
255        if ((($this->range === null) || ($this->range > $this->numberOfPages)) && ($this->total === null)) {
256            $range = [
257                'start' => 1,
258                'end'   => $this->numberOfPages,
259                'prev'  => false,
260                'next'  => false
261            ];
262        } else {
263            // If page is within the first range block.
264            if (($page <= $this->range) && ($this->numberOfPages <= $this->range)) {
265                $range = [
266                    'start' => 1,
267                    'end'   => $this->numberOfPages,
268                    'prev'  => false,
269                    'next'  => false
270                ];
271            // If page is within the first range block, with a next range.
272            } else if (($page <= $this->range) && ($this->numberOfPages > $this->range)) {
273                $range = [
274                    'start' => 1,
275                    'end'   => $this->range,
276                    'prev'  => false,
277                    'next'  => true
278                ];
279            // Else, if page is within the last range block, with an uneven remainder.
280            } else if ($page > ($this->range * floor($this->numberOfPages / $this->range))) {
281                $range = [
282                    'start' => ($this->range * floor($this->numberOfPages / $this->range)) + 1,
283                    'end'   => $this->numberOfPages,
284                    'prev'  => true,
285                    'next'  => false
286                ];
287            // Else, if page is within the last range block, with no remainder.
288            } else if ((($this->numberOfPages % $this->range) == 0) && ($page > ($this->range * (($this->numberOfPages / $this->range) - 1)))) {
289                $range = [
290                    'start' => ($this->range * (($this->numberOfPages / $this->range) - 1)) + 1,
291                    'end'   => $this->numberOfPages,
292                    'prev'  => true,
293                    'next'  => false
294                ];
295            // Else, if page is within a middle range block.
296            } else {
297                $posInRange = (($page % $this->range) == 0) ? ($this->range - 1) : (($page % $this->range) - 1);
298                $linkStart = $page - $posInRange;
299                $range = [
300                    'start' => $linkStart,
301                    'end'   => $linkStart + ($this->range - 1),
302                    'prev'  => true,
303                    'next'  => true
304                ];
305            }
306        }
307
308        return $range;
309    }
310
311}