Passed
Push — master ( fd4a88...7449e0 )
by Caen
04:01 queued 12s
created

Paginator   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 57
dl 0
loc 190
rs 9.52
c 0
b 0
f 0
wmc 36

23 Methods

Rating   Name   Duplication   Size   Complexity  
A nextPageNumber() 0 7 2
A previous() 0 11 3
A currentPage() 0 3 1
A canNavigateForward() 0 3 1
A formatLink() 0 3 1
A next() 0 11 3
A getPaginatedItems() 0 3 1
A getRoute() 0 3 1
A __construct() 0 12 3
A hasMultiplePages() 0 3 1
A generate() 0 3 1
A getItemsForPage() 0 3 1
A firstPage() 0 3 1
A formatPageName() 0 3 1
A canNavigateBack() 0 3 1
A getPageLinks() 0 15 4
A firstItemNumberOnPage() 0 3 1
A totalPages() 0 3 1
A perPage() 0 3 1
A setCurrentPage() 0 7 1
A previousPageNumber() 0 7 2
A validateCurrentPageValue() 0 8 3
A lastPage() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Support;
6
7
use Hyde\Hyde;
8
use InvalidArgumentException;
9
use Hyde\Support\Models\Route;
10
use Hyde\Foundation\Facades\Routes;
11
use Illuminate\Support\Collection;
12
use Illuminate\Contracts\Support\Arrayable;
13
use function collect;
14
use function sprintf;
15
use function range;
16
17
/**
18
 * @see \Hyde\Framework\Testing\Feature\PaginatorTest
19
 */
20
class Paginator
21
{
22
    protected Collection $paginatedItems;
23
24
    protected int $pageSize = 25;
25
    protected int $currentPage = 1;
26
27
    /**
28
     * Optionally provide a route basename to be used in generating the pagination links.
29
     */
30
    protected string $routeBasename;
31
32
    public function __construct(Arrayable|array $items = [], int $pageSize = 25, int $currentPageNumber = null, string $paginationRouteBasename = null)
33
    {
34
        $this->pageSize = $pageSize;
35
36
        $this->generate(collect($items));
0 ignored issues
show
Bug introduced by
$items of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

36
        $this->generate(collect(/** @scrutinizer ignore-type */ $items));
Loading history...
37
38
        if ($currentPageNumber) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $currentPageNumber of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
39
            $this->setCurrentPage($currentPageNumber);
40
        }
41
42
        if ($paginationRouteBasename) {
43
            $this->routeBasename = $paginationRouteBasename;
44
        }
45
    }
46
47
    protected function generate(Collection $items): void
48
    {
49
        $this->paginatedItems = $items->chunk($this->perPage());
50
    }
51
52
    /** Set the current page number. */
53
    public function setCurrentPage(int $currentPage): Paginator
54
    {
55
        $this->validateCurrentPageValue($currentPage);
56
57
        $this->currentPage = $currentPage;
58
59
        return $this;
60
    }
61
62
    /** Get the current page number (which is used as a cursor). */
63
    public function currentPage(): int
64
    {
65
        return $this->currentPage;
66
    }
67
68
    /** Get the paginated collection */
69
    public function getPaginatedItems(): Collection
70
    {
71
        return $this->paginatedItems;
72
    }
73
74
    public function getItemsForPage(): Collection
75
    {
76
        return $this->paginatedItems->get($this->currentPage - 1);
77
    }
78
79
    public function getPageLinks(): array
80
    {
81
        $array = [];
82
        $pageRange = range(1, $this->totalPages());
83
        if (isset($this->routeBasename)) {
84
            foreach ($pageRange as $number) {
85
                $array[$number] = Routes::get("$this->routeBasename/page-$number") ?? Hyde::formatLink("$this->routeBasename/page-$number");
86
            }
87
        } else {
88
            foreach ($pageRange as $number) {
89
                $array[$number] = Hyde::formatLink("page-$number.html");
90
            }
91
        }
92
93
        return $array;
94
    }
95
96
    /** The number of items to be shown per page. */
97
    public function perPage(): int
98
    {
99
        return $this->pageSize;
100
    }
101
102
    /** Get the total number of pages. */
103
    public function totalPages(): int
104
    {
105
        return $this->paginatedItems->count();
106
    }
107
108
    /** Determine if there are enough items to split into multiple pages. */
109
    public function hasMultiplePages(): bool
110
    {
111
        return $this->totalPages() > 1;
112
    }
113
114
    /** Get the page number of the last available page. */
115
    public function lastPage(): int
116
    {
117
        return $this->totalPages();
118
    }
119
120
    /** Determine if there are fewer items after the cursor in the data store. */
121
    public function canNavigateBack(): bool
122
    {
123
        return $this->currentPage > $this->firstPage();
124
    }
125
126
    /** Determine if there are more items after the cursor in the data store. */
127
    public function canNavigateForward(): bool
128
    {
129
        return $this->currentPage < $this->lastPage();
130
    }
131
132
    public function previousPageNumber(): false|int
133
    {
134
        if (! $this->canNavigateBack()) {
135
            return false;
136
        }
137
138
        return $this->currentPage - 1;
139
    }
140
141
    public function nextPageNumber(): false|int
142
    {
143
        if (! $this->canNavigateForward()) {
144
            return false;
145
        }
146
147
        return $this->currentPage + 1;
148
    }
149
150
    public function previous(): false|string|Route
151
    {
152
        if (! $this->canNavigateBack()) {
153
            return false;
154
        }
155
156
        if (! isset($this->routeBasename)) {
157
            return $this->formatLink(-1);
158
        }
159
160
        return $this->getRoute(-1);
161
    }
162
163
    public function next(): false|string|Route
164
    {
165
        if (! $this->canNavigateForward()) {
166
            return false;
167
        }
168
169
        if (! isset($this->routeBasename)) {
170
            return $this->formatLink(+1);
171
        }
172
173
        return $this->getRoute(+1);
174
    }
175
176
    public function firstItemNumberOnPage(): int
177
    {
178
        return (($this->currentPage - 1) * $this->perPage()) + 1;
179
    }
180
181
    protected function validateCurrentPageValue(int $currentPage): void
182
    {
183
        if ($currentPage < $this->firstPage()) {
184
            throw new InvalidArgumentException('Current page number must be greater than 0.');
185
        }
186
187
        if ($currentPage > $this->lastPage()) {
188
            throw new InvalidArgumentException('Current page number must be less than or equal to the last page number.');
189
        }
190
    }
191
192
    protected function formatPageName(int $offset): string
193
    {
194
        return sprintf('page-%d', $this->currentPage + $offset);
195
    }
196
197
    protected function formatLink(int $offset): string
198
    {
199
        return Hyde::formatLink("{$this->formatPageName($offset)}.html");
200
    }
201
202
    protected function getRoute(int $offset): Route|string
203
    {
204
        return Routes::get("$this->routeBasename/{$this->formatPageName($offset)}") ?? Hyde::formatLink("$this->routeBasename/{$this->formatPageName($offset)}");
205
    }
206
207
    protected function firstPage(): int
208
    {
209
        return 1;
210
    }
211
}
212