Passed
Pull Request — master (#29)
by Sébastien
08:57
created

Walker::load()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Bdf\Prime\Query\Pagination;
4
5
use BadMethodCallException;
6
use Bdf\Prime\Connection\ConnectionInterface;
7
use Bdf\Prime\Exception\PrimeException;
8
use Bdf\Prime\PrimeSerializable;
9
use Bdf\Prime\Query\Contract\Limitable;
10
use Bdf\Prime\Query\Contract\Orderable;
11
use Bdf\Prime\Query\Contract\Paginable;
12
use Bdf\Prime\Query\Contract\ReadOperation;
13
use Bdf\Prime\Query\Pagination\WalkStrategy\PaginationWalkStrategy;
14
use Bdf\Prime\Query\Pagination\WalkStrategy\WalkCursor;
15
use Bdf\Prime\Query\Pagination\WalkStrategy\WalkStrategyInterface;
16
use Bdf\Prime\Query\ReadCommandInterface;
17
use Iterator;
18
use LogicException;
19
20
/**
21
 * Query Walker
22
 * 
23
 * Permet de parcourir des collections contenant de gros volume d'entités.
24
 * Le parcourt se fait par paquet d'entités définis par la limit de la query
25
 * Une fois la limite atteinte, la classe lance la requête suivante
0 ignored issues
show
introduced by
Doc comment long description must end with a full stop
Loading history...
26
 * 
27
 * Attention, le walker ne gère pas les objects collection
28
 *
29
 * @template R as array|object
30
 *
31
 * @implements PaginatorInterface<R>
32
 * @implements Iterator<array-key, R>
33
 */
34
class Walker extends PrimeSerializable implements Iterator, PaginatorInterface
35
{
36
    const DEFAULT_PAGE  = 1;
37
    const DEFAULT_LIMIT = 150;
38
    
39
    /**
40
     * First page
41
     * 
42
     * @var int
0 ignored issues
show
Bug introduced by
Expected "integer" but found "int" for @var tag in member variable comment
Loading history...
43
     */
44
    protected $startPage;
45
46
    /**
47
     * The current offset
48
     *
49
     * @var int|null
0 ignored issues
show
Bug introduced by
Expected "integer|null" but found "int|null" for @var tag in member variable comment
Loading history...
50
     */
51
    protected $offset;
52
53
    /**
54
     * @var R[]
55
     */
56
    private $collection = [];
57
58
    /**
59
     * @var WalkStrategyInterface<R>
0 ignored issues
show
introduced by
Expected "WalkStrategyInterfaceR" but found "WalkStrategyInterface<R>" for @var tag in member variable comment
Loading history...
60
     */
61
    private $strategy;
62
63
    /**
64
     * @var WalkCursor<R>
0 ignored issues
show
introduced by
Expected "WalkCursorR" but found "WalkCursor<R>" for @var tag in member variable comment
Loading history...
65
     */
66
    private $cursor;
67
68
    /**
69
     * @var ReadCommandInterface<ConnectionInterface, R>
0 ignored issues
show
introduced by
Expected "ReadCommandInterfaceConnectionInterfaceR" but found "ReadCommandInterface<ConnectionInterface, R>" for @var tag in member variable comment
Loading history...
70
     */
71
    private $query;
72
73
    /**
74
     * @var int
0 ignored issues
show
Bug introduced by
Expected "integer" but found "int" for @var tag in member variable comment
Loading history...
75
     */
76
    private $page;
77
78
    /**
79
     * @var int
0 ignored issues
show
Bug introduced by
Expected "integer" but found "int" for @var tag in member variable comment
Loading history...
80
     */
81
    private $maxRows;
82
83
    /**
84
     * Create a query walker
85
     * 
86
     * @param ReadCommandInterface<ConnectionInterface, R> $query
0 ignored issues
show
introduced by
Parameter type "ReadCommandInterface<ConnectionInterface, R>" must not contain spaces
Loading history...
87
     * @param int            $maxRows
0 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
88
     * @param int            $page
0 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
89
     */
90 27
    public function __construct(ReadCommandInterface $query, $maxRows = null, $page = null)
91
    {
92 27
        $this->query = $query;
93 27
        $this->page = 0;
94 27
        $this->maxRows = $maxRows ?: self::DEFAULT_LIMIT;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
95 27
        $this->startPage = $page ?: self::DEFAULT_PAGE;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
96 27
    }
97
98
    /**
99
     * Change the walk strategy
100
     *
101
     * @param WalkStrategyInterface<R> $strategy
0 ignored issues
show
introduced by
Expected "WalkStrategyInterfaceR" but found "WalkStrategyInterface<R>" for parameter type
Loading history...
102
     *
103
     * @return $this
104
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
105 21
    public function setStrategy(WalkStrategyInterface $strategy): self
106
    {
107 21
        if ($this->cursor !== null) {
108
            throw new LogicException('Cannot change walk strategy during walk');
109
        }
110
111 21
        $this->strategy = $strategy;
112
113 21
        return $this;
114
    }
115
116
    /**
117
     * Get the current active walk strategy
118
     *
119
     * @return WalkStrategyInterface<R>
0 ignored issues
show
introduced by
Expected "WalkStrategyInterfaceR" but found "WalkStrategyInterface<R>" for function return type
Loading history...
120
     */
121 24
    public function getStrategy(): WalkStrategyInterface
122
    {
123 24
        if ($this->strategy) {
124 17
            return $this->strategy;
125
        }
126
127
        /**
0 ignored issues
show
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
128
         * @var WalkStrategyInterface<R>
129
         * @psalm-suppress InvalidPropertyAssignmentValue
130
         */
131 8
        return $this->strategy = new PaginationWalkStrategy();
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
132
    }
133
134
    /**
135
     * Load the first page of collection
136
     *
137
     * @throws PrimeException
138
     */
139
    #[ReadOperation]
140 23
    public function load()
141
    {
142 23
        $this->page = $this->startPage;
143 23
        $this->cursor = $this->getStrategy()->initialize($this->query, $this->maxRows, $this->page);
144 20
        $this->loadCollection();
145 20
    }
146
147
    /**
148
     * @return ReadCommandInterface<ConnectionInterface, R>
149
     */
150 2
    public function query(): ReadCommandInterface
151
    {
152 2
        if ($this->cursor) {
153 1
            return $this->cursor->query;
154
        }
155
156 1
        return $this->query;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function collection()
163
    {
164
        return $this->collection;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
170 5
    public function size()
171
    {
172 5
        if (!$this->query instanceof Paginable) {
173
            throw new BadMethodCallException(__METHOD__.' should be called with a Paginable query');
174
        }
175
176 5
        return $this->query->paginationCount();
177
    }
178
179
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $attribute should have a doc-comment as per coding-style.
Loading history...
180
     * {@inheritdoc}
181
     */
182
    public function order($attribute = null)
183
    {
184
        $query = $this->query();
185
186
        if (!$query instanceof Orderable) {
187
            return $attribute ? null : [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $attribute ? null : array() also could return the type array which is incompatible with the return type mandated by Bdf\Prime\Query\Paginati...natorInterface::order() of Bdf\Prime\Query\Contract\Orderable.
Loading history...
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
188
        }
189
190
        $orders = $query->getOrders();
191
192
        if ($attribute === null) {
193
            return $orders;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $orders returns the type array which is incompatible with the return type mandated by Bdf\Prime\Query\Paginati...natorInterface::order() of Bdf\Prime\Query\Contract\Orderable.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
194
        }
195
196
        return $orders[$attribute] ?? null;
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 3
    public function limit(): ?int
203
    {
204 3
        if ($this->cursor->query instanceof Limitable) {
205 3
            return $this->cursor->query->getLimit();
206
        }
207
208
        return 0;
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214 1
    public function offset(): ?int
215
    {
216 1
        if ($this->cursor->query instanceof Limitable) {
217 1
            return $this->cursor->query->getOffset();
218
        }
219
220
        return 0;
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226 3
    public function page()
227
    {
228 3
        return $this->page;
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234 3
    public function pageMaxRows()
235
    {
236 3
        return $this->maxRows;
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242 20
    protected function loadCollection()
243
    {
244 20
        $this->cursor = $this->strategy->next($this->cursor);
245 20
        $this->collection = $this->cursor->entities;
246
247
        // Test if the collection has numerical keys.
248
        // We have to add the offset to the numerical key.
249 20
        if (isset($this->collection[0])) {
250 18
            $this->offset = ($this->page - $this->startPage) * $this->maxRows;
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
251
        } else {
252 17
            $this->offset = null;
253
        }
254 20
    }
255
256
    /**
257
     * SPL - Iterator
258
     *
259
     * {@inheritdoc}
260
     */
261 16
    public function current()
262
    {
263 16
        return current($this->collection);
264
    }
265
    
266
    /**
267
     * SPL - Iterator
268
     *
269
     * {@inheritdoc}
270
     */
271 11
    public function key()
272
    {
273 11
        if ($this->offset !== null) {
274
            /** @var array<int, mixed> $this->collection */
0 ignored issues
show
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
275 10
            return $this->offset + key($this->collection);
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
276
        }
277
278 1
        return key($this->collection);
279
    }
280
    
281
    /**
282
     * SPL - Iterator
283
     *
284
     * {@inheritdoc}
285
     *
286
     * @throws PrimeException
287
     */
288
    #[ReadOperation]
289 16
    public function next()
290
    {
291 16
        if (false === next($this->collection)) {
292 16
            $this->page++;
293 16
            $this->loadCollection();
294
        }
295 16
    }
296
    
297
    /**
298
     * SPL - Iterator
299
     *
300
     * {@inheritdoc}
301
     */
302 16
    public function valid()
303
    {
304 16
        return false !== current($this->collection);
305
    }
306
    
307
    /**
308
     * SPL - Iterator
309
     *
310
     * {@inheritdoc}
311
     *
312
     * @throws PrimeException
313
     */
314
    #[ReadOperation]
315 19
    public function rewind()
316
    {
317 19
        if (($this->page == $this->startPage) && count($this->collection)) {
318
            reset($this->collection);
319
        } else {
320 19
            $this->load();
321
        }
322 16
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327 3
    public function count()
328
    {
329 3
        return count($this->collection);
330
    }
331
332
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $items should have a doc-comment as per coding-style.
Loading history...
333
     * {@inheritdoc}
334
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
335
    public function pushAll(array $items)
336
    {
337
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
338
    }
339
340
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $item should have a doc-comment as per coding-style.
Loading history...
341
     * {@inheritdoc}
342
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
343
    public function push($item)
344
    {
345
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
346
    }
347
348
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $item should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $key should have a doc-comment as per coding-style.
Loading history...
349
     * {@inheritdoc}
350
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
351
    public function put($key, $item)
352
    {
353
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
354
    }
355
356
    /**
357
     * {@inheritdoc}
358
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
359
    public function all()
360
    {
361
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
362
    }
363
364
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $default should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $key should have a doc-comment as per coding-style.
Loading history...
365
     * {@inheritdoc}
366
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
367
    public function get($key, $default = null)
368
    {
369
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
370
    }
371
372
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $key should have a doc-comment as per coding-style.
Loading history...
373
     * {@inheritdoc}
374
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
375
    public function has($key)
376
    {
377
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
378
    }
379
380
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $key should have a doc-comment as per coding-style.
Loading history...
381
     * {@inheritdoc}
382
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
383
    public function remove($key)
384
    {
385
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
386
    }
387
388
    /**
389
     * {@inheritdoc}
390
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
391
    public function clear()
392
    {
393
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
399
    public function keys()
400
    {
401
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
402
    }
403
404
    /**
405
     * {@inheritdoc}
406
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
407
    public function isEmpty()
408
    {
409
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
410
    }
411
412
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $callback should have a doc-comment as per coding-style.
Loading history...
413
     * {@inheritdoc}
414
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
415 1
    public function map($callback)
416
    {
417 1
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
418
    }
419
420
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $callback should have a doc-comment as per coding-style.
Loading history...
421
     * {@inheritdoc}
422
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
423
    public function filter($callback = null)
424
    {
425
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
426
    }
427
428
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $mode should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $groupBy should have a doc-comment as per coding-style.
Loading history...
429
     * {@inheritdoc}
430
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
431
    public function groupBy($groupBy, $mode = self::GROUPBY)
432
    {
433
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
434
    }
435
436
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
437
     * {@inheritdoc}
438
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
439
    public function contains($element)
440
    {
441
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
442
    }
443
444
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $strict should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
445
     * {@inheritdoc}
446
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
447
    public function indexOf($value, $strict = false)
448
    {
449
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
450
    }
451
452
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $items should have a doc-comment as per coding-style.
Loading history...
453
     * {@inheritdoc}
454
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
455
    public function merge($items)
456
    {
457
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
458
    }
459
460
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $callback should have a doc-comment as per coding-style.
Loading history...
461
     * {@inheritdoc}
462
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
463
    public function sort(callable $callback = null)
464
    {
465
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
466
    }
467
468
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $offset should have a doc-comment as per coding-style.
Loading history...
469
     * {@inheritdoc}
470
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
471
    public function offsetExists($offset)
472
    {
473
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
474
    }
475
476
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $offset should have a doc-comment as per coding-style.
Loading history...
477
     * {@inheritdoc}
478
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
479
    public function offsetGet($offset)
480
    {
481
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
482
    }
483
484
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $offset should have a doc-comment as per coding-style.
Loading history...
485
     * {@inheritdoc}
486
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
487
    public function offsetSet($offset, $value)
488
    {
489
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
490
    }
491
492
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $offset should have a doc-comment as per coding-style.
Loading history...
493
     * {@inheritdoc}
494
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag in function comment
Loading history...
495
    public function offsetUnset($offset)
496
    {
497
        throw new BadMethodCallException('Collection methods are not supported by the Walker');
498
    }
499
}
500