Passed
Push — dev_2x ( fedccd...fb9ebe )
by Adrian
02:16
created

Collection   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 301
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 61
eloc 99
c 2
b 0
f 0
dl 0
loc 301
rs 3.52

40 Methods

Rating   Name   Duplication   Size   Complexity  
A findByPk() 0 9 3
A change() 0 4 1
A get() 0 3 1
A first() 0 3 1
A setState() 0 5 2
A getIterator() 0 3 1
A offsetExists() 0 3 1
A key() 0 3 1
A count() 0 3 1
A reduce() 0 3 1
A toArray() 0 12 4
A partition() 0 3 1
A matching() 0 3 1
A add() 0 10 2
A getChanges() 0 10 2
A offsetGet() 0 3 1
A offsetUnset() 0 3 1
A indexOf() 0 3 1
A last() 0 3 1
A remove() 0 10 2
A current() 0 3 1
A clear() 0 3 1
A getElementPK() 0 3 2
A set() 0 3 1
A forAll() 0 3 1
A filter() 0 3 1
A pluck() 0 14 3
A slice() 0 3 1
A ensureHydratedElement() 0 15 4
A isEmpty() 0 3 1
A removeElement() 0 13 3
A map() 0 3 1
A getKeys() 0 3 1
A getValues() 0 3 1
A exists() 0 3 1
A containsKey() 0 3 1
A next() 0 3 1
A __construct() 0 6 1
A offsetSet() 0 3 1
A contains() 0 13 5

How to fix   Complexity   

Complex Class

Complex classes like Collection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Collection, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\Collection;
5
6
use Closure;
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Criteria;
9
use Doctrine\Common\Collections\Selectable;
10
use Exception;
11
use Sirius\Orm\Contract\EntityInterface;
12
use Sirius\Orm\Contract\HydratorInterface;
13
use Sirius\Orm\Entity\StateEnum;
14
use Traversable;
15
16
class Collection implements \Doctrine\Common\Collections\Collection, Selectable
17
{
18
    protected $changes = [
19
        'removed' => [],
20
        'added'   => []
21
    ];
22
23
    /**
24
     * @var HydratorInterface|null
25
     */
26
    protected $hydrator;
27
28
    /**
29
     * @var ArrayCollection
30
     */
31
    protected $collection;
32
33
    public function __construct(array $elements = [], HydratorInterface $hydrator = null)
34
    {
35
        $this->hydrator           = $hydrator;
36
        $this->collection         = new ArrayCollection($elements);
37
        $this->changes['removed'] = new ArrayCollection();
38
        $this->changes['added']   = new ArrayCollection();
39
    }
40
41
    protected function ensureHydratedElement($element)
42
    {
43
        if (! $this->hydrator) {
44
            return $element;
45
        }
46
47
        if ($element instanceof EntityInterface) {
48
            return $element;
49
        }
50
51
        if (is_array($element)) {
52
            return $this->hydrator->hydrate((array)$element);
53
        }
54
55
        throw new \InvalidArgumentException('You can only add arrays or entities to collections');
56
    }
57
58
    protected function getElementPK($element)
59
    {
60
        return $this->hydrator ? $this->hydrator->getPk($element) : null;
61
    }
62
63
    public function contains($element)
64
    {
65
        $pk = $this->getElementPK($this->ensureHydratedElement($element));
66
        if ($pk === null || $pk === []) {
67
            return false;
68
        }
69
        foreach ($this as $element) {
0 ignored issues
show
introduced by
$element is overwriting one of the parameters of this function.
Loading history...
70
            if ($pk == $this->getElementPK($element)) {
71
                return true;
72
            }
73
        }
74
75
        return false;
76
    }
77
78
    public function add($element)
79
    {
80
        $element = $this->ensureHydratedElement($element);
81
        if (! $this->contains($element)) {
82
            $this->change('added', $element);
83
84
            return $this->collection->add($element);
85
        }
86
87
        return true;
88
    }
89
90
    public function remove($key)
91
    {
92
        $removed = $this->collection->remove($key);
93
        if ($removed) {
94
            $this->change('removed', $removed);
95
        }
96
97
        $this->collection = new ArrayCollection($this->collection->getValues());
98
99
        return $removed;
100
    }
101
102
    public function removeElement($element)
103
    {
104
        $element = $this->ensureHydratedElement($element);
105
        if (! $this->contains($element)) {
106
            return true;
107
        }
108
        $removed = $this->collection->removeElement($this->findByPk($this->hydrator->getPk($element)));
0 ignored issues
show
Bug introduced by
The method getPk() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
        $removed = $this->collection->removeElement($this->findByPk($this->hydrator->/** @scrutinizer ignore-call */ getPk($element)));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
Are you sure the usage of $this->findByPk($this->hydrator->getPk($element)) targeting Sirius\Orm\Collection\Collection::findByPk() 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...
109
        if ($removed) {
110
            $this->change('removed', $element);
111
        }
112
        $this->collection = new ArrayCollection($this->collection->getValues());
113
114
        return true;
115
    }
116
117
    public function findByPk($pk)
118
    {
119
        foreach ($this as $element) {
120
            if ($pk == $this->hydrator->getPk($element)) {
121
                return $element;
122
            }
123
        }
124
125
        return null;
126
    }
127
128
    public function pluck($names)
129
    {
130
        return $this->map(function ($item) use ($names) {
131
            if (! is_array($names)) {
132
                return $this->hydrator->get($item, $names);
133
            }
134
135
            $result = [];
136
            foreach ($names as $name) {
137
                $result[$name] = $this->hydrator->get($item, $name);
138
            }
139
140
            return $result;
141
        })->getValues();
142
    }
143
144
    public function reduce(\Closure $callback, $accumulator)
145
    {
146
        return array_reduce($this->getValues(), $callback, $accumulator);
147
    }
148
149
    public function getChanges(): array
150
    {
151
        $changes = [];
152
        foreach (array_keys($this->changes) as $t) {
153
            /** @var ArrayCollection $changeCollection */
154
            $changeCollection = $this->changes[$t];
155
            $changes[$t]      = $changeCollection->getValues();
156
        }
157
158
        return $changes;
159
    }
160
161
    public function toArray()
162
    {
163
        $result = [];
164
        foreach ($this as $element) {
165
            if (is_object($element) && method_exists($element, 'toArray')) {
166
                $result[] = $element->toArray();
167
            } else {
168
                $result[] = $element;
169
            }
170
        }
171
172
        return $result;
173
    }
174
175
    public function setState($state)
176
    {
177
        if ($state == StateEnum::SYNCHRONIZED) {
178
            $this->changes['removed'] = new ArrayCollection();
179
            $this->changes['added']   = new ArrayCollection();
180
        }
181
    }
182
183
    protected function change($type, $element)
184
    {
185
        $changeCollection = $this->changes[$type];
186
        $changeCollection->add($element);
187
    }
188
189
    public function clear()
190
    {
191
        $this->collection->clear();
192
    }
193
194
    public function isEmpty()
195
    {
196
        return $this->collection->isEmpty();
197
    }
198
199
    public function containsKey($key)
200
    {
201
        return $this->collection->containsKey($key);
202
    }
203
204
    public function get($key)
205
    {
206
        return $this->collection->get($key);
207
    }
208
209
    public function getKeys()
210
    {
211
        return $this->collection->getKeys();
212
    }
213
214
    public function getValues()
215
    {
216
        return $this->collection->getValues();
217
    }
218
219
    public function set($key, $value)
220
    {
221
        $this->collection->set($key, $value);
222
    }
223
224
    public function first()
225
    {
226
        return $this->collection->first();
227
    }
228
229
    public function last()
230
    {
231
        return $this->collection->last();
232
    }
233
234
    public function key()
235
    {
236
        return $this->collection->key();
237
    }
238
239
    public function current()
240
    {
241
        return $this->collection->current();
242
    }
243
244
    public function next()
245
    {
246
        return $this->collection->next();
247
    }
248
249
    public function exists(Closure $p)
250
    {
251
        return $this->collection->exists($p);
252
    }
253
254
    public function filter(Closure $p)
255
    {
256
        return $this->collection->filter($p);
257
    }
258
259
    public function forAll(Closure $p)
260
    {
261
        return $this->collection->forAll($p);
262
    }
263
264
    public function map(Closure $func)
265
    {
266
        return $this->collection->map($func);
267
    }
268
269
    public function partition(Closure $p)
270
    {
271
        return $this->collection->partition($p);
272
    }
273
274
    public function indexOf($element)
275
    {
276
        return $this->collection->indexOf($element);
277
    }
278
279
    public function slice($offset, $length = null)
280
    {
281
        return $this->collection->slice($offset, $length);
282
    }
283
284
    public function getIterator()
285
    {
286
        return $this->collection->getIterator();
287
    }
288
289
    public function offsetExists($offset)
290
    {
291
        return $this->collection->offsetExists($offset);
292
    }
293
294
    public function offsetGet($offset)
295
    {
296
        return $this->collection->offsetGet($offset);
297
    }
298
299
    public function offsetSet($offset, $value)
300
    {
301
        $this->collection->offsetSet($offset, $value);
302
    }
303
304
    public function offsetUnset($offset)
305
    {
306
        $this->collection->offsetUnset($offset);
307
    }
308
309
    public function count()
310
    {
311
        return $this->collection->count();
312
    }
313
314
    public function matching(Criteria $criteria)
315
    {
316
        return $this->collection->matching($criteria);
317
    }
318
}
319