Completed
Push — master ( a98e5a...279cc6 )
by Javier
02:44
created

src/Datagrid/Pager.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Datagrid;
15
16
/**
17
 * @author Fabien Potencier <[email protected]>
18
 * @author Thomas Rabaix <[email protected]>
19
 *
20
 * @implements \Iterator<object>
21
 */
22
abstract class Pager implements \Iterator, \Countable, \Serializable, PagerInterface
23
{
24
    public const TYPE_DEFAULT = 'default';
25
    public const TYPE_SIMPLE = 'simple';
26
27
    /**
28
     * @var int
29
     */
30
    protected $page = 1;
31
32
    /**
33
     * @var int
34
     */
35
    protected $maxPerPage = 0;
36
37
    /**
38
     * @var int
39
     */
40
    protected $lastPage = 1;
41
42
    /**
43
     * @var int
44
     */
45
    protected $nbResults = 0;
46
47
    /**
48
     * @var int
49
     */
50
    protected $cursor = 1;
51
52
    /**
53
     * @var array<string, mixed>
54
     */
55
    protected $parameters = [];
56
57
    /**
58
     * @var int
59
     */
60
    protected $currentMaxLink = 1;
61
62
    /**
63
     * @var int|false
64
     */
65
    protected $maxRecordLimit = false;
66
67
    /**
68
     * @var int
69
     */
70
    protected $maxPageLinks = 0;
71
72
    /**
73
     * Results are null prior to its initialization in `initializeIterator()`.
74
     *
75
     * @var object[]|null
76
     */
77
    protected $results;
78
79
    /**
80
     * @var int
81
     */
82
    protected $resultsCounter = 0;
83
84
    /**
85
     * @var ProxyQueryInterface|null
86
     */
87
    protected $query;
88
89
    /**
90
     * @var string[]
91
     */
92
    protected $countColumn = ['id'];
93
94
    /**
95
     * @param int $maxPerPage Number of records to display per page
96
     */
97
    public function __construct(int $maxPerPage = 10)
98
    {
99
        $this->setMaxPerPage($maxPerPage);
100
    }
101
102
    /**
103
     * Returns the current pager's max link.
104
     */
105
    public function getCurrentMaxLink(): int
106
    {
107
        return $this->currentMaxLink;
108
    }
109
110
    /**
111
     * Returns the current pager's max record limit.
112
     *
113
     * @return int|false
114
     */
115
    public function getMaxRecordLimit()
116
    {
117
        return $this->maxRecordLimit;
118
    }
119
120
    /**
121
     * Sets the current pager's max record limit.
122
     */
123
    public function setMaxRecordLimit(int $limit): void
124
    {
125
        $this->maxRecordLimit = $limit;
126
    }
127
128
    /**
129
     * Returns an array of page numbers to use in pagination links.
130
     *
131
     * @return int[]
132
     */
133
    public function getLinks(?int $nbLinks = null): array
134
    {
135
        if (null === $nbLinks) {
136
            $nbLinks = $this->getMaxPageLinks();
137
        }
138
        $links = [];
139
        $tmp = $this->page - floor($nbLinks / 2);
140
        $check = $this->lastPage - $nbLinks + 1;
141
        $limit = $check > 0 ? $check : 1;
142
        $begin = $tmp > 0 ? ($tmp > $limit ? $limit : $tmp) : 1;
143
144
        $i = (int) $begin;
145
        while ($i < $begin + $nbLinks && $i <= $this->lastPage) {
146
            $links[] = $i++;
147
        }
148
149
        $this->currentMaxLink = \count($links) ? $links[\count($links) - 1] : 1;
150
151
        return $links;
152
    }
153
154
    /**
155
     * Returns true if the current query requires pagination.
156
     */
157
    public function haveToPaginate(): bool
158
    {
159
        return $this->getMaxPerPage() && $this->getNbResults() > $this->getMaxPerPage();
160
    }
161
162
    /**
163
     * Returns the current cursor.
164
     */
165
    public function getCursor(): int
166
    {
167
        return $this->cursor;
168
    }
169
170
    /**
171
     * Sets the current cursor.
172
     */
173
    public function setCursor(int $pos): void
174
    {
175
        if ($pos < 1) {
176
            $this->cursor = 1;
177
        } else {
178
            if ($pos > $this->nbResults) {
179
                $this->cursor = $this->nbResults;
180
            } else {
181
                $this->cursor = $pos;
182
            }
183
        }
184
    }
185
186
    /**
187
     * Returns an object by cursor position.
188
     */
189
    public function getObjectByCursor(int $pos): ?object
190
    {
191
        $this->setCursor($pos);
192
193
        return $this->getCurrent();
194
    }
195
196
    /**
197
     * Returns the current object.
198
     */
199
    public function getCurrent(): ?object
200
    {
201
        return $this->retrieveObject($this->cursor);
202
    }
203
204
    /**
205
     * Returns the next object.
206
     */
207
    public function getNext(): ?object
208
    {
209
        if ($this->cursor + 1 > $this->nbResults) {
210
            return null;
211
        }
212
213
        return $this->retrieveObject($this->cursor + 1);
214
    }
215
216
    /**
217
     * Returns the previous object.
218
     */
219
    public function getPrevious(): ?object
220
    {
221
        if ($this->cursor - 1 < 1) {
222
            return null;
223
        }
224
225
        return $this->retrieveObject($this->cursor - 1);
226
    }
227
228
    /**
229
     * Returns the first index on the current page.
230
     */
231
    public function getFirstIndex(): int
232
    {
233
        if (0 === $this->page) {
234
            return 1;
235
        }
236
237
        return ($this->page - 1) * $this->maxPerPage + 1;
238
    }
239
240
    /**
241
     * Returns the last index on the current page.
242
     */
243
    public function getLastIndex(): int
244
    {
245
        if (0 === $this->page) {
246
            return $this->nbResults;
247
        }
248
        if ($this->page * $this->maxPerPage >= $this->nbResults) {
249
            return $this->nbResults;
250
        }
251
252
        return $this->page * $this->maxPerPage;
253
    }
254
255
    public function getNbResults(): int
256
    {
257
        return $this->nbResults;
258
    }
259
260
    public function getFirstPage(): int
261
    {
262
        return 1;
263
    }
264
265
    public function getLastPage(): int
266
    {
267
        return $this->lastPage;
268
    }
269
270
    public function getPage(): int
271
    {
272
        return $this->page;
273
    }
274
275
    public function getNextPage(): int
276
    {
277
        return min($this->getPage() + 1, $this->getLastPage());
278
    }
279
280
    public function getPreviousPage(): int
281
    {
282
        return max($this->getPage() - 1, $this->getFirstPage());
283
    }
284
285
    public function setPage($page): void
286
    {
287
        $this->page = (int) $page;
288
289
        if ($this->page <= 0) {
290
            // set first page, which depends on a maximum set
291
            $this->page = $this->getMaxPerPage() ? 1 : 0;
292
        }
293
    }
294
295
    public function getMaxPerPage(): int
296
    {
297
        return $this->maxPerPage;
298
    }
299
300
    public function setMaxPerPage($max): void
301
    {
302
        $max = (int) $max;
303
304
        if ($max > 0) {
305
            $this->maxPerPage = $max;
306
            if (0 === $this->page) {
307
                $this->page = 1;
308
            }
309
        } else {
310
            if (0 === $max) {
311
                $this->maxPerPage = 0;
312
                $this->page = 0;
313
            } else {
314
                $this->maxPerPage = 1;
315
                if (0 === $this->page) {
316
                    $this->page = 1;
317
                }
318
            }
319
        }
320
    }
321
322
    public function getMaxPageLinks(): int
323
    {
324
        return $this->maxPageLinks;
325
    }
326
327
    public function setMaxPageLinks(int $maxPageLinks): void
328
    {
329
        $this->maxPageLinks = $maxPageLinks;
330
    }
331
332
    /**
333
     * Returns true if on the first page.
334
     */
335
    public function isFirstPage(): bool
336
    {
337
        return 1 === $this->page;
338
    }
339
340
    /**
341
     * Returns true if on the last page.
342
     */
343
    public function isLastPage(): bool
344
    {
345
        return $this->page === $this->lastPage;
346
    }
347
348
    /**
349
     * Returns the current pager's parameter holder.
350
     *
351
     * @return array<string, mixed>
0 ignored issues
show
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
352
     */
353
    public function getParameters(): array
354
    {
355
        return $this->parameters;
356
    }
357
358
    /**
359
     * Returns a parameter.
360
     *
361
     * @param mixed $default
362
     *
363
     * @return mixed
364
     */
365
    public function getParameter(string $name, $default = null)
366
    {
367
        return isset($this->parameters[$name]) ? $this->parameters[$name] : $default;
368
    }
369
370
    /**
371
     * Checks whether a parameter has been set.
372
     */
373
    public function hasParameter(string $name): bool
374
    {
375
        return isset($this->parameters[$name]);
376
    }
377
378
    /**
379
     * Sets a parameter.
380
     *
381
     * @param mixed $value
382
     */
383
    public function setParameter(string $name, $value): void
384
    {
385
        $this->parameters[$name] = $value;
386
    }
387
388
    /**
389
     * @return object|false
390
     */
391
    public function current()
392
    {
393
        if (!$this->isIteratorInitialized()) {
394
            $this->initializeIterator();
395
        }
396
397
        return current($this->results);
398
    }
399
400
    /**
401
     * @return int|string
402
     */
403
    public function key()
404
    {
405
        if (!$this->isIteratorInitialized()) {
406
            $this->initializeIterator();
407
        }
408
409
        return key($this->results);
410
    }
411
412
    /**
413
     * @return object|false
414
     */
415
    public function next()
416
    {
417
        if (!$this->isIteratorInitialized()) {
418
            $this->initializeIterator();
419
        }
420
421
        --$this->resultsCounter;
422
423
        return next($this->results);
424
    }
425
426
    /**
427
     * @return object|false
428
     */
429
    public function rewind()
430
    {
431
        if (!$this->isIteratorInitialized()) {
432
            $this->initializeIterator();
433
        }
434
435
        $this->resultsCounter = \count($this->results);
436
437
        return reset($this->results);
438
    }
439
440
    public function valid(): bool
441
    {
442
        if (!$this->isIteratorInitialized()) {
443
            $this->initializeIterator();
444
        }
445
446
        return $this->resultsCounter > 0;
447
    }
448
449
    public function count(): int
450
    {
451
        return $this->getNbResults();
452
    }
453
454
    public function serialize(): string
455
    {
456
        $vars = get_object_vars($this);
457
        unset($vars['query']);
458
459
        return serialize($vars);
460
    }
461
462
    public function unserialize($serialized): void
463
    {
464
        $array = unserialize($serialized);
465
466
        foreach ($array as $name => $values) {
467
            $this->$name = $values;
468
        }
469
    }
470
471
    /**
472
     * @return string[]
473
     */
474
    public function getCountColumn(): array
475
    {
476
        return $this->countColumn;
477
    }
478
479
    /**
480
     * @param string[] $countColumn
481
     */
482
    public function setCountColumn(array $countColumn): void
483
    {
484
        $this->countColumn = $countColumn;
485
    }
486
487
    public function setQuery(ProxyQueryInterface $query): void
488
    {
489
        $this->query = $query;
490
    }
491
492
    public function getQuery(): ?ProxyQueryInterface
493
    {
494
        return $this->query;
495
    }
496
497
    protected function setNbResults(int $nbResults): void
498
    {
499
        $this->nbResults = $nbResults;
500
    }
501
502
    protected function setLastPage(int $page): void
503
    {
504
        $this->lastPage = $page;
505
506
        if ($this->getPage() > $page) {
507
            $this->setPage($page);
508
        }
509
    }
510
511
    /**
512
     * Returns true if the properties used for iteration have been initialized.
513
     */
514
    protected function isIteratorInitialized(): bool
515
    {
516
        return null !== $this->results;
517
    }
518
519
    /**
520
     * Loads data into properties used for iteration.
521
     */
522
    protected function initializeIterator(): void
523
    {
524
        $this->results = $this->getResults();
525
        $this->resultsCounter = \count($this->results);
526
    }
527
528
    /**
529
     * Empties properties used for iteration.
530
     */
531
    protected function resetIterator(): void
532
    {
533
        $this->results = null;
534
        $this->resultsCounter = 0;
535
    }
536
537
    /**
538
     * Retrieve the object for a certain offset.
539
     */
540
    protected function retrieveObject(int $offset): ?object
541
    {
542
        $query = $this->getQuery();
543
544
        if (null === $query) {
545
            return null;
546
        }
547
548
        $queryForRetrieve = clone $query;
549
        $queryForRetrieve
550
            ->setFirstResult($offset - 1)
551
            ->setMaxResults(1);
552
553
        $results = $queryForRetrieve->execute();
554
555
        return $results[0] ?? null;
556
    }
557
}
558