Passed
Push — master ( 59f035...20aba6 )
by Alexander
01:35
created

IterableDataReader::matchFilter()   C

Complexity

Conditions 15
Paths 15

Size

Total Lines 45
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 15.0044

Importance

Changes 0
Metric Value
eloc 38
c 0
b 0
f 0
dl 0
loc 45
rs 5.9166
ccs 36
cts 37
cp 0.973
cc 15
nc 15
nop 2
crap 15.0044

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Yiisoft\Data\Reader;
5
6
use Yiisoft\Arrays\ArrayHelper;
7
use Yiisoft\Data\Reader\Filter\All;
8
use Yiisoft\Data\Reader\Filter\Any;
9
use Yiisoft\Data\Reader\Filter\FilterInterface;
10
use Yiisoft\Data\Reader\Filter\Equals;
11
use Yiisoft\Data\Reader\Filter\GreaterThan;
12
use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual;
13
use Yiisoft\Data\Reader\Filter\LessThan;
14
use Yiisoft\Data\Reader\Filter\LessThanOrEqual;
15
use Yiisoft\Data\Reader\Filter\In;
16
use Yiisoft\Data\Reader\Filter\Like;
17
use Yiisoft\Data\Reader\Filter\Not;
18
19
final class IterableDataReader implements DataReaderInterface, SortableDataInterface, FilterableDataInterface, OffsetableDataInterface, CountableDataInterface
20
{
21
    private $data;
22
    private $sort;
23
24
    /**
25
     * @var FilterInterface
26
     */
27
    private $filter;
28
29
    private $limit = self::DEFAULT_LIMIT;
30
    private $offset = 0;
31
32 34
    public function __construct(iterable $data)
33
    {
34 34
        $this->data = $data;
35 34
    }
36
37 7
    public function withSort(?Sort $sort): self
38
    {
39 7
        $new = clone $this;
40 7
        $new->sort = $sort;
41 7
        return $new;
42
    }
43
44 5
    public function getSort(): ?Sort
45
    {
46 5
        return $this->sort;
47
    }
48
49
    /**
50
     * Sorts data items according to the given sort definition.
51
     * @param iterable $items the items to be sorted
52
     * @param Sort $sort the sort definition
53
     * @return array the sorted items
54
     */
55 4
    private function sortItems(iterable $items, Sort $sort): iterable
56
    {
57 4
        $criteria = $sort->getCriteria();
58 4
        if ($criteria !== []) {
59 4
            $items = $this->iterableToArray($items);
60 4
            ArrayHelper::multisort($items, array_keys($criteria), array_values($criteria));
61
        }
62
63 4
        return $items;
64
    }
65
66 12
    private function matchFilter(array $item, array $filter): bool
67
    {
68 12
        $operation = array_shift($filter);
69 12
        $arguments = $filter;
70
71
        switch ($operation) {
72 12
            case Not::getOperator():
73 1
                return !$this->matchFilter($item, $arguments[0]);
74 12
            case Any::getOperator():
75 1
                foreach ($arguments[0] as $subFilter) {
76 1
                    if ($this->matchFilter($item, $subFilter)) {
77 1
                        return true;
78
                    }
79
                }
80 1
                return false;
81 12
            case All::getOperator():
82 1
                foreach ($arguments[0] as $subFilter) {
83 1
                    if (!$this->matchFilter($item, $subFilter)) {
84 1
                        return false;
85
                    }
86
                }
87 1
                return true;
88 12
            case Equals::getOperator():
89 3
                [$field, $value] = $arguments;
90 3
                return $item[$field] == $value;
91 9
            case GreaterThan::getOperator():
92 3
                [$field, $value] = $arguments;
93 3
                return $item[$field] > $value;
94 7
            case GreaterThanOrEqual::getOperator():
95 1
                [$field, $value] = $arguments;
96 1
                return $item[$field] >= $value;
97 6
            case LessThan::getOperator():
98 1
                [$field, $value] = $arguments;
99 1
                return $item[$field] < $value;
100 5
            case LessThanOrEqual::getOperator():
101 1
                [$field, $value] = $arguments;
102 1
                return $item[$field] <= $value;
103 4
            case In::getOperator():
104 1
                [$field, $values] = $arguments;
105 1
                return in_array($item[$field], $values, false);
106 3
            case Like::getOperator():
107 3
                [$field, $value] = $arguments;
108 3
                return stripos($item[$field], $value) !== false;
109
            default:
110
                throw new \RuntimeException("Operation \"$operation\" is not supported");
111
        }
112
    }
113
114 13
    public function withFilter(?FilterInterface $filter): self
115
    {
116 13
        $new = clone $this;
117 13
        $new->filter = $filter;
118 13
        return $new;
119
    }
120
121 9
    public function withLimit(int $limit): self
122
    {
123 9
        $new = clone $this;
124 9
        $new->limit = $limit;
125 9
        return $new;
126
    }
127
128 24
    public function read(): iterable
129
    {
130 24
        $filter = null;
131 24
        if ($this->filter !== null) {
132 12
            $filter = $this->filter->toArray();
133
        }
134
135 24
        $data = [];
136 24
        $skipped = 0;
137
138 24
        $sortedData = $this->sort === null
139 20
            ? $this->data
140 24
            : $this->sortItems($this->data, $this->sort);
141
142 24
        foreach ($sortedData as $item) {
143
            // do not return more than limit items
144 24
            if (count($data) === $this->limit) {
145 5
                break;
146
            }
147
148
            // skip offset items
149 24
            if ($skipped < $this->offset) {
150 3
                $skipped++;
151 3
                continue;
152
            }
153
154
            // filter items
155 24
            if ($filter === null || $this->matchFilter($item, $filter)) {
156 24
                $data[] = $item;
157
            }
158
        }
159
160 24
        return $data;
161
    }
162
163 4
    public function withOffset(int $offset): self
164
    {
165 4
        $new = clone $this;
166 4
        $new->offset = $offset;
167 4
        return $new;
168
    }
169
170 5
    public function count(): int
171
    {
172 5
        return count($this->read());
173
    }
174
175 4
    private function iterableToArray(iterable $iterable): array
176
    {
177 4
        return $iterable instanceof \Traversable ? iterator_to_array($iterable, true) : (array)$iterable;
178
    }
179
}
180