Passed
Pull Request — master (#36)
by Alexander
13:00
created

SelectDataReader::readOne()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 6
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 12
ccs 0
cts 10
cp 0
crap 20
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Cycle\DataReader;
6
7
use Countable;
8
use Cycle\ORM\Select;
9
use InvalidArgumentException;
10
use IteratorAggregate;
11
use Spiral\Database\Query\SelectQuery;
12
use Spiral\Pagination\PaginableInterface;
13
use Yiisoft\Data\Reader\CountableDataInterface;
14
use Yiisoft\Data\Reader\DataReaderInterface;
15
use Yiisoft\Data\Reader\OffsetableDataInterface;
16
use Yiisoft\Data\Reader\Sort;
17
use Yiisoft\Data\Reader\SortableDataInterface;
18
use Yiisoft\Yii\Cycle\DataReader\Cache\CachedCount;
19
use Yiisoft\Yii\Cycle\DataReader\Cache\CachedCollection;
20
21
final class SelectDataReader implements
22
    DataReaderInterface,
23
    OffsetableDataInterface,
24
    CountableDataInterface,
25
    SortableDataInterface,
26
    IteratorAggregate
27
{
28
    /** @var Select|SelectQuery */
29
    private $query;
30
    private ?int $limit = null;
31
    private ?int $offset = null;
32
    private ?Sort $sorting = null;
33
    private CachedCount $countCache;
34
    private CachedCollection $itemsCache;
35
    private CachedCollection $oneItemCache;
36
37
    /**
38
     * @param Select|SelectQuery $query
39
     */
40
    public function __construct($query)
41
    {
42
        if (!$query instanceof Countable) {
0 ignored issues
show
introduced by
$query is always a sub-type of Countable.
Loading history...
43
            throw new InvalidArgumentException(sprintf('Query should implement %s interface', Countable::class));
44
        }
45
        if (!$query instanceof PaginableInterface) {
0 ignored issues
show
introduced by
$query is always a sub-type of Spiral\Pagination\PaginableInterface.
Loading history...
46
            throw new InvalidArgumentException(
47
                sprintf('Query should implement %s interface', PaginableInterface::class)
48
            );
49
        }
50
        $this->query = clone $query;
51
        $this->countCache = new CachedCount($this->query);
52
        $this->itemsCache = new CachedCollection();
53
        $this->oneItemCache = new CachedCollection();
54
    }
55
56
    public function getSort(): ?Sort
57
    {
58
        return $this->sorting;
59
    }
60
61
    public function withLimit(int $limit): self
62
    {
63
        $clone = clone $this;
64
        $clone->setLimit($limit);
65
        return $clone;
66
    }
67
68
    public function withOffset(int $offset): self
69
    {
70
        $clone = clone $this;
71
        $clone->setOffset($offset);
72
        return $clone;
73
    }
74
75
    public function withSort(?Sort $sorting): self
76
    {
77
        $clone = clone $this;
78
        $clone->setSort($sorting);
79
        return $clone;
80
    }
81
82
    public function count(): int
83
    {
84
        return $this->countCache->getCount();
85
    }
86
87
    public function read(): iterable
88
    {
89
        if ($this->itemsCache->getCollection() !== null) {
90
            return $this->itemsCache->getCollection();
91
        }
92
        $query = $this->buildQuery();
93
        $this->itemsCache->setCollection($query->fetchAll());
94
        return $this->itemsCache->getCollection();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->itemsCache->getCollection() returns the type null which is incompatible with the type-hinted return iterable.
Loading history...
Bug introduced by
Are you sure the usage of $this->itemsCache->getCollection() targeting Yiisoft\Yii\Cycle\DataRe...ection::getCollection() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
95
    }
96
97
    /**
98
     * @return mixed
99
     */
100
    public function readOne()
101
    {
102
        if (!$this->oneItemCache->isCollected()) {
103
            $item = $this->itemsCache->isCollected()
104
                // get first item from cached collection
105
                ? $this->itemsCache->getGenerator()->current()
106
                // read data with limit 1
107
                : $this->withLimit(1)->getIterator()->current();
108
            $this->oneItemCache->setCollection($item === null ? [] : [$item]);
109
        }
110
111
        return $this->oneItemCache->getGenerator()->current();
112
    }
113
114
    /**
115
     * Get Iterator without caching
116
     */
117
    public function getIterator(): \Generator
118
    {
119
        if ($this->itemsCache->getCollection() !== null) {
120
            yield from $this->itemsCache->getCollection();
121
        } else {
122
            yield from $this->buildQuery()->getIterator();
123
        }
124
    }
125
126
    private function setSort(?Sort $sorting): void
127
    {
128
        if ($this->sorting !== $sorting) {
129
            $this->sorting = $sorting;
130
            $this->itemsCache = new CachedCollection();
131
            $this->oneItemCache = new CachedCollection();
132
        }
133
    }
134
135
    private function setLimit(?int $limit): void
136
    {
137
        if ($this->limit !== $limit) {
138
            $this->limit = $limit;
139
            $this->itemsCache = new CachedCollection();
140
        }
141
    }
142
143
    private function setOffset(?int $offset): void
144
    {
145
        if ($this->offset !== $offset) {
146
            $this->offset = $offset;
147
            $this->itemsCache = new CachedCollection();
148
            $this->oneItemCache = new CachedCollection();
149
        }
150
    }
151
152
    /**
153
     * @return Select|SelectQuery
154
     */
155
    private function buildQuery()
156
    {
157
        $newQuery = clone $this->query;
158
        if ($this->offset !== null) {
159
            $newQuery->offset($this->offset);
160
        }
161
        if ($this->sorting !== null) {
162
            $newQuery->orderBy($this->sorting->getOrder());
163
        }
164
        if ($this->limit !== null) {
165
            $newQuery->limit($this->limit);
166
        }
167
        return $newQuery;
168
    }
169
}
170