Passed
Push — master ( bd6ebf...99194d )
by Alexander
02:22
created

OffsetPaginator::withPageSize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 9
ccs 7
cts 7
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Data\Paginator;
6
7
use Yiisoft\Data\Reader\CountableDataInterface;
8
use Yiisoft\Data\Reader\OffsetableDataInterface;
9
use Yiisoft\Data\Reader\ReadableDataInterface;
10
use Yiisoft\Data\Reader\Sort;
11
use Yiisoft\Data\Reader\SortableDataInterface;
12
13
/**
14
 * @template TKey as array-key
15
 * @template TValue
16
 *
17
 * @implements PaginatorInterface<TKey, TValue>
18
 */
19
final class OffsetPaginator implements PaginatorInterface
20
{
21
    /** @var CountableDataInterface|OffsetableDataInterface|ReadableDataInterface */
22
    private ReadableDataInterface $dataReader;
23
    private int $currentPage = 1;
24
    private int $pageSize = self::DEFAULT_PAGE_SIZE;
25
    /**
26
     * @psalm-var ReadableDataInterface<TKey, TValue>
27
     */
28
    private ?ReadableDataInterface $cachedReader = null;
29
30
    /**
31
     * @psalm-param ReadableDataInterface<TKey, TValue> $dataReader
32
     */
33 24
    public function __construct(ReadableDataInterface $dataReader)
34
    {
35 24
        if (!$dataReader instanceof OffsetableDataInterface) {
36 1
            throw new \InvalidArgumentException(
37 1
                sprintf(
38 1
                    'Data reader should implement %s in order to be used with offset paginator',
39 1
                    OffsetableDataInterface::class
40
                )
41
            );
42
        }
43
44 23
        if (!$dataReader instanceof CountableDataInterface) {
45 1
            throw new \InvalidArgumentException(
46 1
                sprintf(
47 1
                    'Data reader should implement %s in order to be used with offset paginator',
48 1
                    CountableDataInterface::class
49
                )
50
            );
51
        }
52
53 22
        $this->dataReader = $dataReader;
54 22
    }
55
56 3
    public function isRequired(): bool
57
    {
58 3
        return $this->getTotalPages() > 1;
59
    }
60
61 3
    public function getCurrentPage(): int
62
    {
63 3
        return $this->currentPage;
64
    }
65
66
    /**
67
     * @return $this
68
     *
69
     * @psalm-mutation-free
70
     */
71 12
    public function withCurrentPage(int $page): self
72
    {
73 12
        if ($page < 1) {
74 1
            throw new PaginatorException('Current page should be at least 1');
75
        }
76 11
        $new = clone $this;
77 11
        $new->currentPage = $page;
78 11
        $new->cachedReader = null;
79 11
        return $new;
80
    }
81
82
    /**
83
     * @return $this
84
     *
85
     * @psalm-mutation-free
86
     */
87 16
    public function withPageSize(int $size): self
88
    {
89 16
        if ($size < 1) {
90 1
            throw new PaginatorException('Page size should be at least 1');
91
        }
92 15
        $new = clone $this;
93 15
        $new->pageSize = $size;
94 15
        $new->cachedReader = null;
95 15
        return $new;
96
    }
97
98 4
    public function isOnFirstPage(): bool
99
    {
100 4
        return $this->currentPage === 1;
101
    }
102
103 4
    public function isOnLastPage(): bool
104
    {
105 4
        if ($this->currentPage > $this->getInternalTotalPages()) {
106 1
            throw new PaginatorException('Page not found');
107
        }
108 3
        return $this->currentPage === $this->getInternalTotalPages();
109
    }
110
111 14
    public function getTotalPages(): int
112
    {
113 14
        return (int) ceil($this->getTotalItems() / $this->pageSize);
114
    }
115
116 6
    public function getOffset(): int
117
    {
118 6
        return $this->pageSize * ($this->currentPage - 1);
119
    }
120
121
    /**
122
     * @psalm-return \Generator<TKey, TValue, mixed, void>
123
     */
124 5
    public function read(): iterable
125
    {
126 5
        if ($this->cachedReader !== null) {
127
            yield from $this->cachedReader->read();
128
            return;
129
        }
130 5
        if ($this->currentPage > $this->getInternalTotalPages()) {
131 1
            throw new PaginatorException('Page not found');
132
        }
133
        /** @var CountableDataInterface|OffsetableDataInterface|ReadableDataInterface $reader */
134 4
        $this->cachedReader = $this->dataReader->withLimit($this->pageSize)->withOffset($this->getOffset());
0 ignored issues
show
Bug introduced by
The method withOffset() does not exist on Yiisoft\Data\Reader\ReadableDataInterface. It seems like you code against a sub-type of Yiisoft\Data\Reader\ReadableDataInterface such as anonymous//tests/Paginat...fsetPaginatorTest.php$1 or Yiisoft\Data\Reader\DataReaderInterface. ( Ignorable by Annotation )

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

134
        $this->cachedReader = $this->dataReader->withLimit($this->pageSize)->/** @scrutinizer ignore-call */ withOffset($this->getOffset());
Loading history...
135 4
        yield from $this->cachedReader->read();
0 ignored issues
show
Bug introduced by
The method read() does not exist on null. ( Ignorable by Annotation )

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

135
        yield from $this->cachedReader->/** @scrutinizer ignore-call */ read();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
136 4
    }
137
138
    public function getNextPageToken(): ?string
139
    {
140
        return $this->isOnLastPage() ? null : (string) ($this->currentPage + 1);
141
    }
142
143
    public function getPreviousPageToken(): ?string
144
    {
145
        return $this->isOnFirstPage() ? null : (string) ($this->currentPage - 1);
146
    }
147
148
    /**
149
     * @return $this
150
     *
151
     * @psalm-mutation-free
152
     */
153
    public function withNextPageToken(?string $token): self
154
    {
155
        return $this->withCurrentPage((int)$token);
156
    }
157
158
    /**
159
     * @return $this
160
     *
161
     * @psalm-mutation-free
162
     */
163
    public function withPreviousPageToken(?string $token): self
164
    {
165
        return $this->withCurrentPage((int)$token);
166
    }
167
168 4
    public function getCurrentPageSize(): int
169
    {
170 4
        $pages = $this->getInternalTotalPages();
171 4
        if ($pages === 1) {
172 2
            return $this->getTotalItems();
173
        }
174 2
        if ($this->currentPage === $pages) {
175 1
            return $this->getTotalItems() - $this->getOffset();
176
        }
177 1
        if ($this->currentPage > $pages) {
178
            throw new PaginatorException('Page not found');
179
        }
180 1
        return $this->pageSize;
181
    }
182
183 15
    public function getTotalItems(): int
184
    {
185 15
        return $this->dataReader->count();
0 ignored issues
show
Bug introduced by
The method count() does not exist on Yiisoft\Data\Reader\ReadableDataInterface. It seems like you code against a sub-type of said class. However, the method does not exist in anonymous//tests/Paginat...ysetPaginatorTest.php$2 or anonymous//tests/Paginat...ysetPaginatorTest.php$1. Are you sure you never get one of those? ( Ignorable by Annotation )

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

185
        return $this->dataReader->/** @scrutinizer ignore-call */ count();
Loading history...
186
    }
187
188 1
    public function getPageSize(): int
189
    {
190 1
        return $this->pageSize;
191
    }
192
193 1
    public function getSort(): ?Sort
194
    {
195 1
        if (!$this->dataReader instanceof SortableDataInterface) {
196
            return null;
197
        }
198
199 1
        return $this->dataReader->getSort();
0 ignored issues
show
Bug introduced by
The method getSort() does not exist on Yiisoft\Data\Reader\ReadableDataInterface. It seems like you code against a sub-type of Yiisoft\Data\Reader\ReadableDataInterface such as anonymous//tests/Paginat...ysetPaginatorTest.php$2 or Yiisoft\Data\Reader\DataReaderInterface. ( Ignorable by Annotation )

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

199
        return $this->dataReader->/** @scrutinizer ignore-call */ getSort();
Loading history...
200
    }
201
202 11
    private function getInternalTotalPages(): int
203
    {
204 11
        return max(1, $this->getTotalPages());
205
    }
206
}
207