Completed
Push — master ( cb1b62...52c941 )
by Travis
03:25
created

Collection   C

Complexity

Total Complexity 73

Size/Duplication

Total Lines 390
Duplicated Lines 9.23 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 75.32%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 73
c 2
b 0
f 0
lcom 1
cbo 1
dl 36
loc 390
ccs 119
cts 158
cp 0.7532
rs 5.5447

18 Methods

Rating   Name   Duplication   Size   Complexity  
B __get() 0 20 8
B __call() 0 22 5
A insertAfter() 0 16 3
A toSelectArray() 0 14 3
B magicWhere() 0 32 4
B determineMagicWhereDetails() 0 31 2
A checkMagicWhereFinalOperator() 0 8 2
A checkMagicWherePosition() 0 8 2
A checkMagicWhereNot() 0 8 2
B getWhere() 0 24 5
A handleMultiTap() 0 14 3
A tapThroughObjects() 0 14 2
A whereObject() 0 16 4
B getWhereIn() 11 11 5
B getWhereBetween() 8 14 6
B getWhereLike() 11 11 5
B getWhereNull() 6 11 7
B getWhereDefault() 0 11 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
3
namespace NukaCode\Database;
4
5
use Illuminate\Database\Eloquent\Collection as BaseCollection;
6
7
/**
8
 * Class Collection
9
 *
10
 * This class adds some magic to the collection class.
11
 * It allows you to tab through collections into other object or collections.
12
 * It also allows you to run a getWhere on a collection to find objects.
13
 *
14
 * @package NukaCode\Core\Database
15
 */
16
class Collection extends BaseCollection
17
{
18
    /**
19
     * Dynamically retrieve attributes on the model.
20
     *
21
     * @param  string $key
22
     *
23
     * @return mixed
24
     */
25 1
    public function __get($key)
26
    {
27 1
        $newCollection = new self();
28
29 1
        foreach ($this->items as $item) {
30 1
            if ($item instanceof self) { // This item is a collection.
31
                foreach ($item as $subItem) {
32
                    $newCollection->put($newCollection->count(), $subItem->$key);
33
                }
34 1
            } elseif (is_object($item) && ! $item instanceof self && $item->$key instanceof self) { // Next tap is a collection.
35
                foreach ($item->$key as $subItem) {
36
                    $newCollection->put($newCollection->count(), $subItem);
37
                }
38
            } else { // This item is an object.
39 1
                $newCollection->put($newCollection->count(), $item->$key);
40
            }
41 1
        }
42
43 1
        return $newCollection;
44
    }
45
46
    /**
47
     * Allow a method to be run on the entire collection.
48
     *
49
     * @param string $method
50
     * @param array  $args
51
     *
52
     * @return Collection
53
     */
54 24
    public function __call($method, $args)
55
    {
56
        // Look for magic where calls.
57 24
        if (strstr($method, 'getWhere')) {
58 24
            return $this->magicWhere(snake_case($method), $args);
59
        }
60
61
        // No data in the collection.
62
        if ($this->count() <= 0) {
63
            return $this;
64
        }
65
66
        // Run the command on each object in the collection.
67
        foreach ($this->items as $item) {
68
            if (! is_object($item)) {
69
                continue;
70
            }
71
            call_user_func_array([$item, $method], $args);
72
        }
73
74
        return $this;
75
    }
76
77
    /**
78
     * Insert into an object
79
     *
80
     * Should be able to do this with methods
81
     * that already exist on collection.
82
     *
83
     * @param mixed $value
84
     * @param int   $afterKey
85
     *
86
     * @return Collection
87
     */
88
    public function insertAfter($value, $afterKey)
89
    {
90
        $new_object = new self();
91
92
        foreach ((array)$this->items as $k => $v) {
93
            if ($afterKey == $k) {
94
                $new_object->add($value);
95
            }
96
97
            $new_object->add($v);
98
        }
99
100
        $this->items = $new_object->items;
101
102
        return $this;
103
    }
104
105
    /**
106
     * Turn a collection into a drop down for an html select element.
107
     *
108
     * @param  string $firstOptionText Text for the first object in the select array.
109
     * @param  string $id              The column to use for the id column in the option element.
110
     * @param  string $name            The column to use for the name column in the option element.
111
     *
112
     * @return array                    The new select element array.
113
     */
114
    public function toSelectArray($firstOptionText = 'Select one', $id = 'id', $name = 'name')
115
    {
116
        $selectArray = [];
117
118
        if ($firstOptionText != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $firstOptionText of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
119
            $selectArray[0] = $firstOptionText;
120
        }
121
122
        foreach ($this->items as $item) {
123
            $selectArray[$item->{$id}] = $item->{$name};
124
        }
125
126
        return $selectArray;
127
    }
128
129
    /**
130
     * Turn the magic getWhere into a real where query.
131
     *
132
     * @param $method
133
     * @param $args
134
     *
135
     * @return Collection
136
     */
137 24
    private function magicWhere($method, $args)
138
    {
139 24
        $whereStatement = explode('_', $method);
140
141
        // Get where
142 24
        if (count($whereStatement) == 2) {
143 2
            return $this->getWhere($args[0], '=', $args[1]);
144
        }
145
146
        $operators = [
147 22
            'in', 'between', 'like', 'null',
148 22
            'not',
149 22
            'first', 'last',
150 22
            'many',
151 22
        ];
152
153
        // If an operator is found then add operators.
154 22
        if (array_intersect($whereStatement, $operators)) {
155 22
            list($operator, $firstOrLast, $inverse) = $this->determineMagicWhereDetails($whereStatement);
156
157 22
            $column = $args[0];
158 22
            $value  = (isset($args[1]) ? $args[1] : null);
159
160 22
            return $this->getWhere(
161 22
                $column,
162 22
                $operator,
163 22
                $value,
164 22
                $inverse,
165
                $firstOrLast
166 22
            );
167
        }
168
    }
169
170
    /**
171
     * @param $whereStatement
172
     *
173
     * @return array
174
     */
175 22
    private function determineMagicWhereDetails($whereStatement)
176
    {
177 22
        $finalOperator = '=';
178 22
        $position      = null;
179 22
        $not           = false;
180
181 22
        foreach ($whereStatement as $operator) {
182 22
            $finalOperator = $this->checkMagicWhereFinalOperator($operator, $finalOperator);
183 22
            $position      = $this->checkMagicWherePosition($operator, $position);
184 22
            $not           = $this->checkMagicWhereNot($operator, $not);
185 22
        }
186
187 22
        return [$finalOperator, $position, $not];
188
189
        // This is not working at the moment
190
        // todo riddles - fix this
191
        //if ($finalOperator == 'many') {
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
192
        //    $where = null;
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
193
        //    foreach ($args[0] as $column => $value) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
194
        //        $where = $this->getWhere(
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
195
        //            $column,            // Column
196
        //            $finalOperator,    // Operator
197
        //            $value,             // Value
198
        //            $not,               // Inverse
199
        //            $position            // First or last
200
        //        );
201
        //    }
202
        //
203
        //    return $where;
204
        //}
205
    }
206
207 22
    private function checkMagicWhereFinalOperator($operator, $finalOperator)
208
    {
209 22
        if (in_array($operator, ['in', 'between', 'like', 'null', '='])) {
210 20
            return $operator;
211
        }
212
213 22
        return $finalOperator;
214
    }
215
216 22
    private function checkMagicWherePosition($operator, $position)
217
    {
218 22
        if (in_array($operator, ['first', 'last'])) {
219 4
            return $operator;
220
        }
221
222 22
        return $position;
223
    }
224
225 22
    private function checkMagicWhereNot($operator, $not)
226
    {
227 22
        if (in_array($operator, ['not'])) {
228 10
            return true;
229
        }
230
231 22
        return $not;
232
    }
233
234
    /**
235
     * Search a collection for the value specified.
236
     *
237
     * @param  string  $column   The column to search.
238
     * @param  string  $operator The operation to use during search.
239
     * @param  mixed   $value    The value to search for.
240
     * @param  boolean $inverse  Invert the results.
241
     * @param  string  $position Return the first or last object in the collection.
242
     *
243
     * @return self                 Return the filtered collection.
244
     */
245 24
    protected function getWhere($column, $operator, $value = null, $inverse = false, $position = null)
246
    {
247 24
        $output = clone $this;
248 24
        foreach ($output->items as $key => $item) {
249 24
            if (strstr($column, '->')) {
250 12
                $forget = $this->handleMultiTap($item, $column, $value, $operator, $inverse);
251 12
            } else {
252
                // No tap direct object access
253 12
                $forget = $this->whereObject($item, $column, $operator, $value, $inverse);
254
            }
255
256 24
            if ($forget == true) {
257 24
                $output->forget($key);
258 24
                continue;
259
            }
260 24
        }
261
262
        // Handel first and last.
263 24
        if (! is_null($position)) {
264 4
            return $output->$position();
265
        }
266
267 20
        return $output;
268
    }
269
270
    /**
271
     * @param $item
272
     * @param $column
273
     * @param $value
274
     * @param $operator
275
     * @param $inverse
276
     *
277
     * @return bool
278
     */
279 12
    private function handleMultiTap($item, $column, $value, $operator, $inverse)
280
    {
281 12
        list($objectToSearch, $columnToSearch) = $this->tapThroughObjects($column, $item);
282
283 12
        if ($objectToSearch instanceof self) {
284 12
            foreach ($objectToSearch as $subObject) {
285
                // The column has a tap that ends in a collection.
286 12
                return $this->whereObject($subObject, $columnToSearch, $operator, $value, $inverse);
287
            }
288
        } else {
289
            // The column has a tap that ends in direct access
290
            return $this->whereObject($objectToSearch, $columnToSearch, $operator, $value, $inverse);
291
        }
292
    }
293
294
    /**
295
     * @param $column
296
     * @param $item
297
     *
298
     * @return mixed
299
     */
300 12
    private function tapThroughObjects($column, $item)
301
    {
302 12
        $taps = explode('->', $column);
303
304 12
        $objectToSearch = $item;
305 12
        $columnToSearch = array_pop($taps);
306
307 12
        foreach ($taps as $tapKey => $tap) {
308
            // Keep tapping till we hit the last object.
309 12
            $objectToSearch = $objectToSearch->$tap;
310 12
        }
311
312 12
        return [$objectToSearch, $columnToSearch];
313
    }
314
315
    /**
316
     * Compare the object and column passed with the value using the operator
317
     *
318
     * @param  object  $object   The object we are searching.
319
     * @param  string  $column   The column to compare.
320
     * @param  string  $operator What type of comparison operation to perform.
321
     * @param  mixed   $value    The value to search for.
322
     * @param  boolean $inverse  Invert the results.
323
     *
324
     * @return boolean              Return true if the object should be removed from the collection.
325
     */
326 24
    private function whereObject($object, $column, $operator, $value = null, $inverse = false)
327
    {
328
        // Remove the object is the column does not exits.
329
        // Only do this if we aren't looking for null
330 24
        if (! $object->$column && $operator != 'null') {
331 12
            return true;
332
        }
333
334 24
        $method = 'getWhere' . ucfirst($operator);
335
336 24
        if (method_exists($this, $method)) {
337 20
            return $this->{$method}($object, $column, $value, $inverse);
338
        }
339
340 4
        return $this->getWhereDefault($object, $column, $value, $inverse);
341
    }
342
343 8 View Code Duplication
    private function getWhereIn($object, $column, $value, $inverse)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
    {
345 8
        if (! in_array($object->$column, $value) && $inverse == false) {
346 6
            return true;
347
        }
348 8
        if (in_array($object->$column, $value) && $inverse == true) {
349 2
            return true;
350
        }
351
352 8
        return false;
353
    }
354
355 4
    private function getWhereBetween($object, $column, $value, $inverse)
356
    {
357 4
        if ($inverse == false) {
358 2 View Code Duplication
            if ($object->$column < $value[0] || $object->$column > $value[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
359 2
                return true;
360
            }
361 2 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
362 2
            if ($object->$column >= $value[0] && $object->$column <= $value[1]) {
363 2
                return true;
364
            }
365
        }
366
367 4
        return false;
368
    }
369
370 4 View Code Duplication
    private function getWhereLike($object, $column, $value, $inverse)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
    {
372 4
        if (! strstr($object->$column, $value) && $inverse == false) {
373 2
            return true;
374
        }
375 4
        if (strstr($object->$column, $value) && $inverse == true) {
376 2
            return true;
377
        }
378
379 4
        return false;
380
    }
381
382 4
    private function getWhereNull($object, $column, $value, $inverse)
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
383
    {
384 4 View Code Duplication
        if ((! is_null($object->$column) || $object->$column != null) && $inverse == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
385 2
            return true;
386 1
        }
387 4 View Code Duplication
        if ((is_null($object->$column) || $object->$column == null) && $inverse == true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
388 2
            return true;
389
        }
390
391 4
        return false;
392
    }
393
394 4
    private function getWhereDefault($object, $column, $value, $inverse)
395
    {
396 4
        if ($object->$column != $value && $inverse == false) {
397 2
            return true;
398
        }
399 4
        if ($object->$column == $value && $inverse == true) {
400 2
            return true;
401
        }
402
403 4
        return false;
404
    }
405
}
406