Passed
Push — master ( d7ffae...bb227e )
by Alexander
01:29
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 32
    public function __construct(iterable $data)
33
    {
34 32
        $this->data = $data;
35 32
    }
36
37 6
    public function withSort(?Sort $sort): self
38
    {
39 6
        $new = clone $this;
40 6
        $new->sort = $sort;
41 6
        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 3
    private function sortItems(iterable $items, Sort $sort): iterable
56
    {
57 3
        $criteria = $sort->getCriteria();
58 3
        if ($criteria !== []) {
59 3
            $items = $this->iterableToArray($items);
60 3
            ArrayHelper::multisort($items, array_keys($criteria), array_values($criteria));
61
        }
62
63 3
        return $items;
64
    }
65
66 11
    private function matchFilter(array $item, array $filter): bool
67
    {
68 11
        $operation = array_shift($filter);
69 11
        $arguments = $filter;
70
71
        switch ($operation) {
72 11
            case Not::getOperator():
73 1
                return !$this->matchFilter($item, $arguments[0]);
74 11
            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 11
            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 11
            case Equals::getOperator():
89 3
                [$field, $value] = $arguments;
90 3
                return $item[$field] == $value;
91 8
            case GreaterThan::getOperator():
92 3
                [$field, $value] = $arguments;
93 3
                return $item[$field] > $value;
94 6
            case GreaterThanOrEqual::getOperator():
95 1
                [$field, $value] = $arguments;
96 1
                return $item[$field] >= $value;
97 5
            case LessThan::getOperator():
98 1
                [$field, $value] = $arguments;
99 1
                return $item[$field] < $value;
100 4
            case LessThanOrEqual::getOperator():
101 1
                [$field, $value] = $arguments;
102 1
                return $item[$field] <= $value;
103 3
            case In::getOperator():
104 1
                [$field, $values] = $arguments;
105 1
                return in_array($item[$field], $values, false);
106 2
            case Like::getOperator():
107 2
                [$field, $value] = $arguments;
108 2
                return stripos($item[$field], $value) !== false;
109
            default:
110
                throw new \RuntimeException("Operation \"$operation\" is not supported");
111
        }
112
    }
113
114 12
    public function withFilter(?FilterInterface $filter): self
115
    {
116 12
        $new = clone $this;
117 12
        $new->filter = $filter;
118 12
        return $new;
119
    }
120
121 8
    public function withLimit(int $limit): self
122
    {
123 8
        $new = clone $this;
124 8
        $new->limit = $limit;
125 8
        return $new;
126
    }
127
128 18
    public function read(): iterable
129
    {
130 18
        $filter = null;
131 18
        if ($this->filter !== null) {
132 11
            $filter = $this->filter->toArray();
133
        }
134
135 18
        $data = [];
136 18
        $skipped = 0;
137
138 18
        foreach ($this->data as $item) {
139
            // do not return more than limit items
140 18
            if (count($data) === $this->limit) {
141 4
                break;
142
            }
143
144
            // skip offset items
145 18
            if ($skipped < $this->offset) {
146 3
                $skipped++;
147 3
                continue;
148
            }
149
150
            // filter items
151 18
            if ($filter === null || $this->matchFilter($item, $filter)) {
152 18
                $data[] = $item;
153
            }
154
        }
155
156 18
        if ($this->sort !== null) {
157 3
            $data = $this->sortItems($data, $this->sort);
158
        }
159
160 18
        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 4
    public function count(): int
171
    {
172 4
        return count($this->data);
173
    }
174
175 3
    private function iterableToArray(iterable $iterable): array
176
    {
177 3
        return $iterable instanceof \Traversable ? iterator_to_array($iterable, true) : (array)$iterable;
178
    }
179
}
180