Paginator::getPageNumbersWithoutTotal()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
c 0
b 0
f 0
nc 8
nop 0
dl 0
loc 25
rs 9.8666
1
<?php
2
3
/*
4
The MIT License (MIT)
5
6
Copyright (c) 2014 Jason Grimes
7
8
Permission is hereby granted, free of charge, to any person obtaining a copy
9
of this software and associated documentation files (the "Software"), to deal
10
in the Software without restriction, including without limitation the rights
11
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
copies of the Software, and to permit persons to whom the Software is
13
furnished to do so, subject to the following conditions:
14
15
The above copyright notice and this permission notice shall be included in all
16
copies or substantial portions of the Software.
17
18
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
SOFTWARE.
25
*/
26
27
/**
28
 * Paginator.php - Jaxon Paginator
29
 *
30
 * Create pagination links from a Jaxon request and a data array.
31
 *
32
 * @package jaxon-core
33
 * @author Jason Grimes
34
 * @author Thierry Feuzeu
35
 * @copyright 2014 Jason Grimes
36
 * @copyright 2016 Thierry Feuzeu
37
 * @license https://opensource.org/licenses/MIT MIT License
38
 * @link https://github.com/jaxon-php/jaxon-core
39
 */
40
41
namespace Jaxon\App\Pagination;
42
43
use Jaxon\App\Pagination\Page;
44
use Jaxon\Script\JsExpr;
45
use Closure;
46
47
use function array_map;
48
use function ceil;
49
use function count;
50
use function max;
51
use function range;
52
use function trim;
53
54
abstract class Paginator
55
{
56
    /**
57
     * @var integer
58
     */
59
    protected $nPagesCount = 0;
60
61
    /**
62
     * @var integer
63
     */
64
    protected $nMaxPages = 9;
65
66
    /**
67
     * @var string
68
     */
69
    protected $sPreviousText = '&laquo;';
70
71
    /**
72
     * @var string
73
     */
74
    protected $sNextText = '&raquo;';
75
76
    /**
77
     * @var string
78
     */
79
    protected $sEllipsysText = '...';
80
81
    /**
82
     * The constructor.
83
     *
84
     * @param int $nPageNumber     The current page number
85
     * @param int $nItemsPerPage    The number of items per page
86
     * @param int $nItemsCount      The total number of items
87
     * @param PaginationRenderer $xRenderer
88
     */
89
    public function __construct(protected int $nPageNumber, protected int $nItemsPerPage,
90
        protected int $nItemsCount, protected PaginationRenderer $xRenderer)
91
    {
92
        $this->updatePagesCount();
93
    }
94
95
    /**
96
     * Update the number of pages
97
     *
98
     * @return Paginator
99
     */
100
    private function updatePagesCount(): Paginator
101
    {
102
        $this->nItemsPerPage = $this->nItemsPerPage > 0 ? $this->nItemsPerPage : 0;
103
        // $this->nItemsCount = $this->nItemsCount > 0 ? $this->nItemsCount : 0;
104
        $this->nPageNumber = $this->nPageNumber < 1 ? 1 : $this->nPageNumber;
105
106
        if($this->nItemsCount >= 0)
107
        {
108
            $this->nPagesCount = ($this->nItemsPerPage === 0 ? 0 :
109
                (int)ceil($this->nItemsCount / $this->nItemsPerPage));
110
            if($this->nPageNumber > $this->nPagesCount)
111
            {
112
                $this->nPageNumber = $this->nPagesCount;
113
            }
114
        }
115
        return $this;
116
    }
117
118
    /**
119
     * Set the text for the previous page link
120
     *
121
     * @param string $sText    The text for the previous page link
122
     *
123
     * @return Paginator
124
     */
125
    public function setPreviousText(string $sText): Paginator
126
    {
127
        $this->sPreviousText = $sText;
128
        return $this;
129
    }
130
131
    /**
132
     * Set the text for the next page link
133
     *
134
     * @param string $sText    The text for the previous page link
135
     *
136
     * @return Paginator
137
     */
138
    public function setNextText(string $sText): Paginator
139
    {
140
        $this->sNextText = $sText;
141
        return $this;
142
    }
143
144
    /**
145
     * Set the max number of pages to show
146
     *
147
     * @param int $nMaxPages    The max number of pages to show
148
     *
149
     * @return Paginator
150
     */
151
    public function setMaxPages(int $nMaxPages): Paginator
152
    {
153
        // Make sure the max number of pages is odd and greater than 5.
154
        $this->nMaxPages = max((int)(($nMaxPages - 1) / 2) * 2 + 1, 5);
155
        return $this;
156
    }
157
158
    /**
159
     * Get the previous page data.
160
     *
161
     * @return Page
162
     */
163
    protected function getPrevPage(): Page
164
    {
165
        return $this->nPageNumber <= 1 ?
166
            new Page('disabled', $this->sPreviousText, 0) :
167
            new Page('enabled', $this->sPreviousText, $this->nPageNumber - 1);
168
    }
169
170
    /**
171
     * Get the next page data.
172
     *
173
     * @return Page
174
     */
175
    protected function getNextPage(): Page
176
    {
177
        // The next page link is always active when the total number of items is not privided.
178
        return $this->nItemsCount >= 0 && $this->nPageNumber >= $this->nPagesCount ?
179
            new Page('disabled', $this->sNextText, 0) :
180
            new Page('enabled', $this->sNextText, $this->nPageNumber + 1);
181
    }
182
183
    /**
184
     * Get a page data.
185
     *
186
     * @param integer $nNumber    The page number
187
     *
188
     * @return Page
189
     */
190
    protected function getPage(int $nNumber): Page
191
    {
192
        if($nNumber < 1)
193
        {
194
            return new Page('disabled', $this->sEllipsysText, 0);
195
        }
196
        $sType = ($nNumber === $this->nPageNumber ? 'current' : 'enabled');
197
        return new Page($sType, "$nNumber", $nNumber);
198
    }
199
200
    /**
201
     * Get the array of page numbers to be printed.
202
     *
203
     * Example: [1, 2, 3, 4, 5, 6, 7]
204
     *
205
     * @return array
206
     */
207
    protected function getAllPageNumbers(): array
208
    {
209
        return range(1, $this->nPagesCount);
210
    }
211
212
    /**
213
     * Get the array of page numbers to be printed, when the total number of items is not provided.
214
     *
215
     * Example: [1, 0, 4, 5, 6, 0, 10]
216
     *
217
     * @return array
218
     */
219
    protected function getPageNumbersWithoutTotal(): array
220
    {
221
        $aPageNumbers = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $aPageNumbers is dead and can be removed.
Loading history...
222
223
        // Determine the sliding range, centered around the current page.
224
        $nNumAdjacents = ($this->nMaxPages - 1) / 2;
225
226
        $nSlidingStart = 1;
227
        $nSlidingStartThreshold = $nNumAdjacents;
228
        $nSlidingEnd = $this->nPageNumber + $nNumAdjacents - 1;
229
230
        if($this->nPageNumber > $nNumAdjacents + 1)
231
        {
232
            $nSlidingStart = $this->nPageNumber - $nNumAdjacents + 2;
233
        }
234
        if($this->nPageNumber <= $nSlidingStartThreshold)
235
        {
236
            $nSlidingEnd += $nSlidingStartThreshold - $this->nPageNumber + 1;
237
        }
238
239
        // Build the list of page numbers. Pages with 0 as number are ellipsys.
240
        $aStartPages = $nSlidingStart > 1 ? [1, 0] : [];
241
        $aPageNumbers = range($nSlidingStart, $nSlidingEnd);
242
        // Ellipsys are always added at the end of the list.
243
        return [...$aStartPages, ...$aPageNumbers, 0];
244
    }
245
246
    /**
247
     * Get the array of page numbers to be printed, when the total number of items is provided.
248
     *
249
     * Example: [1, 0, 4, 5, 6, 0, 10]
250
     *
251
     * @return array
252
     */
253
    protected function getPageNumbersWithTotal(): array
254
    {
255
        $aPageNumbers = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $aPageNumbers is dead and can be removed.
Loading history...
256
257
        // Determine the sliding range, centered around the current page.
258
        $nNumAdjacents = ($this->nMaxPages - 1) / 2;
259
260
        $nSlidingStart = 1;
261
        $nSlidingStartThreshold = $nNumAdjacents;
262
        $nSlidingEnd = $this->nPagesCount;
263
        $nSlidingEndThreshold = $this->nPagesCount - $nNumAdjacents;
264
265
        if($this->nPageNumber > $nNumAdjacents + 1)
266
        {
267
            $nSlidingStart = $this->nPageNumber - $nNumAdjacents + 2;
268
        }
269
        if($this->nPageNumber > $nSlidingEndThreshold)
270
        {
271
            $nSlidingStart -= $this->nPageNumber - $nSlidingEndThreshold;
272
        }
273
274
        if($this->nPageNumber < $this->nPagesCount - $nNumAdjacents)
275
        {
276
            $nSlidingEnd = $this->nPageNumber + $nNumAdjacents - 2;
277
        }
278
        if($this->nPageNumber <= $nSlidingStartThreshold)
279
        {
280
            $nSlidingEnd += $nSlidingStartThreshold - $this->nPageNumber + 1;
281
        }
282
283
        // Build the list of page numbers. Pages with 0 as number are ellipsys.
284
        $aStartPages = $nSlidingStart > 1 ? [1, 0] : [];
285
        $aEndPages = $nSlidingEnd < $this->nPagesCount ? [0, $this->nPagesCount] : [];
286
        $aPageNumbers = range($nSlidingStart, $nSlidingEnd);
287
        return [...$aStartPages, ...$aPageNumbers, ...$aEndPages];
288
    }
289
290
    /**
291
     * Get the current page number.
292
     *
293
     * @return int
294
     */
295
    public function currentPage(): int
296
    {
297
        return $this->nPageNumber;
298
    }
299
300
    /**
301
     * Get the links (pages raw data).
302
     *
303
     * @return array<null|Page|array<Page>>
304
     */
305
    public function pages(): array
306
    {
307
        $aPageNumbers = match(true) {
308
            $this->nItemsCount < 0 => $this->getPageNumbersWithoutTotal(),
309
            $this->nPagesCount < 2 => [],
310
            $this->nPagesCount <= $this->nMaxPages => $this->getAllPageNumbers(),
311
            default => $this->getPageNumbersWithTotal(),
312
        };
313
        if(count($aPageNumbers) === 0)
314
        {
315
            return [null, [], null];
316
        }
317
318
        $aPages = array_map($this->getPage(...), $aPageNumbers);
319
        return [$this->getPrevPage(), $aPages, $this->getNextPage()];
320
    }
321
322
    /**
323
     * Call a closure that will receive the page number as parameter.
324
     *
325
     * @param Closure $fPageCallback
326
     *
327
     * @return Paginator
328
     */
329
    public function page(Closure $fPageCallback): Paginator
330
    {
331
        $fPageCallback($this->nPageNumber);
332
333
        return $this;
334
    }
335
336
    /**
337
     * Call a closure that will receive the pagination offset as parameter.
338
     *
339
     * @param Closure $fOffsetCallback
340
     *
341
     * @return Paginator
342
     */
343
    public function offset(Closure $fOffsetCallback): Paginator
344
    {
345
        $fOffsetCallback(($this->nPageNumber - 1) * $this->nItemsPerPage);
346
347
        return $this;
348
    }
349
350
    /**
351
     * @param string $sHtml
352
     * @param array $aParams
353
     *
354
     * @return void
355
     */
356
    abstract protected function showHtml(string $sHtml, array $aParams): void;
357
358
    /**
359
     * Render the pagination links with a given javascript call.
360
     *
361
     * @param JsExpr $xCall
362
     * @param array $aParams
363
     *
364
     * @return void
365
     */
366
    protected function paginate(JsExpr $xCall, ...$aParams): void
367
    {
368
        if(($xFunc = $xCall->func()) !== null)
369
        {
370
            $this->showHtml(trim((string)$this->xRenderer->getHtml($this)),
371
                [$xFunc->withPage()->jsonSerialize(), ...$aParams]);
372
        }
373
    }
374
}
375