Paginator   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 96
c 1
b 0
f 0
dl 0
loc 300
rs 9.52
wmc 36

11 Methods

Rating   Name   Duplication   Size   Complexity  
A setPaginateMethod() 0 7 2
A paginateFake() 0 13 2
A paginate() 0 22 4
A getTotalPages() 0 15 4
A getPageSize() 0 7 2
A getResultTotalCount() 0 13 3
B getPageNumbers() 0 41 9
A __construct() 0 18 1
A getCurrentPageNumber() 0 7 2
A getNextPageNumber() 0 11 4
A getPreviousPageNumber() 0 11 3
1
<?php declare(strict_types=1);
2
3
namespace Janisbiz\LightOrm\Paginator;
4
5
use Janisbiz\LightOrm\Entity\EntityInterface;
6
use Janisbiz\LightOrm\QueryBuilder\QueryBuilderInterface;
7
8
class Paginator implements PaginatorInterface
9
{
10
    const OPTION_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE = 'show_pages_before_and_after_current_page';
11
12
    const OPTION_VALUE_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE_UNLIMITED = 'unlimited';
13
14
    /**
15
     * @var QueryBuilderInterface
16
     */
17
    protected $queryBuilder;
18
19
    /**
20
     * @var \Closure
21
     */
22
    protected $addPaginateQuery;
23
24
    /**
25
     * @var \Closure
26
     */
27
    protected $getPaginateResult;
28
29
    /**
30
     * @var int
31
     */
32
    protected $pageSize;
33
34
    /**
35
     * @var int
36
     */
37
    protected $currentPage;
38
39
    /**
40
     * @var array
41
     */
42
    protected $options = [];
43
44
    /**
45
     * @var bool
46
     */
47
    protected $paginate = true;
48
49
    /**
50
     * @var string
51
     */
52
    protected $paginateMethod;
53
54
    /**
55
     * @var array
56
     */
57
    protected $result = [];
58
59
    /**
60
     * @var int
61
     */
62
    protected $resultTotalCount = 0;
63
64
    /**
65
     * @var int
66
     */
67
    protected $resultTotalPages = 0;
68
69
    /**
70
     * @param QueryBuilderInterface $queryBuilder
71
     * @param \Closure $addPaginateQuery
72
     * @param \Closure $getPaginateResult
73
     * @param int $pageSize
74
     * @param int $currentPage
75
     * @param array $options
76
     */
77
    public function __construct(
78
        QueryBuilderInterface $queryBuilder,
79
        \Closure $addPaginateQuery,
80
        \Closure $getPaginateResult,
81
        int $pageSize,
82
        int $currentPage = 1,
83
        array $options = []
84
    ) {
85
        $this->queryBuilder = clone $queryBuilder;
86
        $this->addPaginateQuery = $addPaginateQuery;
87
        $this->getPaginateResult = $getPaginateResult;
88
        $this->pageSize = $pageSize;
89
        $this->currentPage = (int) $currentPage;
90
        $this->options = \array_merge(
91
            [
92
                static::OPTION_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE => 2,
93
            ],
94
            $options
95
        );
96
    }
97
98
    /**
99
     * @param bool $toString
100
     *
101
     * @return EntityInterface[]|string
102
     */
103
    public function paginate(bool $toString = false)
104
    {
105
        if (false === $this->paginate) {
106
            return $this->result;
107
        }
108
109
        $this->setPaginateMethod(__METHOD__);
110
111
        $countQueryBuilder = clone $this->queryBuilder;
112
        $this->resultTotalCount = $countQueryBuilder->count();
113
114
        if (0 < $this->resultTotalCount) {
115
            $this->resultTotalPages = (int) \ceil($this->resultTotalCount / $this->pageSize);
116
117
            if ($this->currentPage > $this->resultTotalPages) {
118
                $this->currentPage = $this->resultTotalPages;
119
            }
120
121
            $this->paginateFake($toString);
122
        }
123
124
        return $this->result;
125
    }
126
127
    /**
128
     * Same behavior as paginate method, except executing count to get the total result count!
129
     *
130
     * @param bool $toString
131
     *
132
     * @return EntityInterface[]|string
133
     */
134
    public function paginateFake(bool $toString = false)
135
    {
136
        if (false === $this->paginate) {
137
            return $this->result;
138
        }
139
140
        $this->setPaginateMethod(__METHOD__);
141
142
        \call_user_func($this->addPaginateQuery, $this->queryBuilder, $this->pageSize, $this->currentPage);
143
        $this->result = \call_user_func($this->getPaginateResult, $this->queryBuilder, $toString);
144
        $this->paginate = false;
145
146
        return $this->result;
147
    }
148
149
    /**
150
     * @return array
151
     * @throws PaginatorException
152
     */
153
    public function getPageNumbers(): array
154
    {
155
        if (true === $this->paginate) {
156
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
157
        }
158
159
        $currentPage = $this->currentPage;
160
        $resultTotalPages = $this->resultTotalPages;
161
        if ('paginateFake' === $this->paginateMethod) {
162
            if (empty($this->result)) {
163
                $currentPage--;
164
                $resultTotalPages = $currentPage;
165
            } else {
166
                $resultTotalPages = $currentPage + 1;
167
            }
168
        }
169
170
        $pages = [];
171
172
        if ($this->options[static::OPTION_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE]
173
            === static::OPTION_VALUE_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE_UNLIMITED
174
        ) {
175
            for ($i = 1; $i <= $resultTotalPages; $i++) {
176
                $pages[$i] = $i;
177
            }
178
        } else {
179
            $pages[$currentPage] = $currentPage;
180
            for ($i = $this->options[static::OPTION_SHOW_PAGES_BEFORE_AND_AFTER_CURRENT_PAGE]; $i > 0; $i--) {
181
                if ($currentPage - $i >= 1) {
182
                    $pages[$currentPage - $i] = ($currentPage - $i);
183
                }
184
185
                if ($currentPage + $i <= $resultTotalPages) {
186
                    $pages[$currentPage + $i] = ($currentPage + $i);
187
                }
188
            }
189
        }
190
191
        \ksort($pages);
192
193
        return $pages;
194
    }
195
196
    /**
197
     * @return int
198
     * @throws PaginatorException
199
     */
200
    public function getTotalPages(): int
201
    {
202
        if (true === $this->paginate) {
203
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
204
        }
205
206
        if ('paginateFake' === $this->paginateMethod) {
207
            if (empty($this->result)) {
208
                return $this->currentPage;
209
            }
210
211
            return $this->currentPage + 1;
212
        }
213
214
        return $this->resultTotalPages;
215
    }
216
217
    /**
218
     * @return int
219
     * @throws PaginatorException
220
     */
221
    public function getCurrentPageNumber(): int
222
    {
223
        if (true === $this->paginate) {
224
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
225
        }
226
227
        return $this->currentPage;
228
    }
229
230
    /**
231
     * @return null|int
232
     * @throws PaginatorException
233
     */
234
    public function getNextPageNumber(): ?int
235
    {
236
        if (true === $this->paginate) {
237
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
238
        }
239
240
        if ($this->currentPage + 1 <= $this->resultTotalPages || 'paginateFake' === $this->paginateMethod) {
241
            return $this->currentPage + 1;
242
        }
243
244
        return null;
245
    }
246
247
    /**
248
     * @return null|int
249
     * @throws PaginatorException
250
     */
251
    public function getPreviousPageNumber(): ?int
252
    {
253
        if (true === $this->paginate) {
254
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
255
        }
256
257
        if (1 <= ($previousPage = $this->currentPage - 1)) {
258
            return $previousPage;
259
        }
260
261
        return null;
262
    }
263
264
    /**
265
     * @return int
266
     * @throws PaginatorException
267
     */
268
    public function getResultTotalCount(): int
269
    {
270
        if (true === $this->paginate) {
271
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
272
        }
273
274
        if ('paginateFake' === $this->paginateMethod) {
275
            throw new PaginatorException(
276
                'When calling "paginateFake", is is not possible to determine result total count!'
277
            );
278
        }
279
280
        return $this->resultTotalCount;
281
    }
282
283
    /**
284
     * @return int
285
     * @throws PaginatorException
286
     */
287
    public function getPageSize(): int
288
    {
289
        if (true === $this->paginate) {
290
            throw new PaginatorException('You must call "paginate" or "paginateFake" before calling this method!');
291
        }
292
293
        return $this->pageSize;
294
    }
295
296
    /**
297
     * @param string $paginateMethod
298
     *
299
     * @return $this
300
     */
301
    protected function setPaginateMethod(string $paginateMethod): Paginator
302
    {
303
        if (null === $this->paginateMethod) {
304
            $this->paginateMethod = \explode('::', $paginateMethod)[1];
305
        }
306
307
        return $this;
308
    }
309
}
310