Completed
Push — master ( b7bc4d...4f203a )
by Travis
02:28
created

Collection::tapThroughObjects()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
crap 2
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
35
                continue;
36
            }
37
38 1
            if (is_object($item) && ! $item instanceof self && $item->$key instanceof self) { // Next tap is a collection.
39
                foreach ($item->$key as $subItem) {
40
                    $newCollection->put($newCollection->count(), $subItem);
41
                }
42
43
                continue;
44
            }
45
46 1
            $newCollection->put($newCollection->count(), $item->$key);
47 1
        }
48
49 1
        return $newCollection;
50
    }
51
52
    /**
53
     * Allow a method to be run on the entire collection.
54
     *
55
     * @param string $method
56
     * @param array  $args
57
     *
58
     * @return Collection
59
     */
60 24
    public function __call($method, $args)
61
    {
62
        // Look for magic where calls.
63 24
        if (strstr($method, 'getWhere')) {
64 24
            return $this->magicWhere(snake_case($method), $args);
65
        }
66
67
        // No data in the collection.
68
        if ($this->count() <= 0) {
69
            return $this;
70
        }
71
72
        // Run the command on each object in the collection.
73
        foreach ($this->items as $item) {
74
            if (! is_object($item)) {
75
                continue;
76
            }
77
            call_user_func_array([$item, $method], $args);
78
        }
79
80
        return $this;
81
    }
82
83
    /**
84
     * Insert into an object
85
     *
86
     * Should be able to do this with methods
87
     * that already exist on collection.
88
     *
89
     * @param mixed $value
90
     * @param int   $afterKey
91
     *
92
     * @return Collection
93
     */
94
    public function insertAfter($value, $afterKey)
95
    {
96
        $new_object = new self();
97
98
        foreach ((array)$this->items as $k => $v) {
99
            if ($afterKey == $k) {
100
                $new_object->add($value);
101
            }
102
103
            $new_object->add($v);
104
        }
105
106
        $this->items = $new_object->items;
107
108
        return $this;
109
    }
110
111
    /**
112
     * Turn a collection into a drop down for an html select element.
113
     *
114
     * @param  string $firstOptionText Text for the first object in the select array.
115
     * @param  string $id              The column to use for the id column in the option element.
116
     * @param  string $name            The column to use for the name column in the option element.
117
     *
118
     * @return array                    The new select element array.
119
     */
120
    public function toSelectArray($firstOptionText = 'Select one', $id = 'id', $name = 'name')
121
    {
122
        $selectArray = [];
123
124
        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...
125
            $selectArray[0] = $firstOptionText;
126
        }
127
128
        foreach ($this->items as $item) {
129
            $selectArray[$item->{$id}] = $item->{$name};
130
        }
131
132
        return $selectArray;
133
    }
134
135
    /**
136
     * Turn the magic getWhere into a real where query.
137
     *
138
     * @param $method
139
     * @param $args
140
     *
141
     * @return Collection
142
     */
143 24
    private function magicWhere($method, $args)
144
    {
145 24
        $whereStatement = explode('_', $method);
146
147
        // Get where
148 24
        if (count($whereStatement) == 2) {
149 2
            return $this->getWhere($args[0], '=', $args[1]);
150
        }
151
152
        $operators = [
153 22
            'in', 'between', 'like', 'null',
154 22
            'not',
155 22
            'first', 'last',
156 22
            'many',
157 22
        ];
158
159
        // If an operator is found then add operators.
160 22
        if (array_intersect($whereStatement, $operators)) {
161 22
            list($operator, $firstOrLast, $inverse) = $this->determineMagicWhereDetails($whereStatement);
162
163 22
            $column = $args[0];
164 22
            $value  = (isset($args[1]) ? $args[1] : null);
165
166 22
            return $this->getWhere(
167 22
                $column,
168 22
                $operator,
169 22
                $value,
170 22
                $inverse,
171
                $firstOrLast
172 22
            );
173
        }
174
    }
175
176
    /**
177
     * @param $whereStatement
178
     *
179
     * @return array
180
     */
181 22
    private function determineMagicWhereDetails($whereStatement)
182
    {
183 22
        $finalOperator = '=';
184 22
        $position      = null;
185 22
        $not           = false;
186
187 22
        foreach ($whereStatement as $operator) {
188 22
            $finalOperator = $this->checkMagicWhereFinalOperator($operator, $finalOperator);
189 22
            $position      = $this->checkMagicWherePosition($operator, $position);
190 22
            $not           = $this->checkMagicWhereNot($operator, $not);
191 22
        }
192
193 22
        return [$finalOperator, $position, $not];
194
195
        // This is not working at the moment
196
        // todo riddles - fix this
197
        //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...
198
        //    $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...
199
        //    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...
200
        //        $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...
201
        //            $column,            // Column
202
        //            $finalOperator,    // Operator
203
        //            $value,             // Value
204
        //            $not,               // Inverse
205
        //            $position            // First or last
206
        //        );
207
        //    }
208
        //
209
        //    return $where;
210
        //}
211
    }
212
213 22
    private function checkMagicWhereFinalOperator($operator, $finalOperator)
214
    {
215 22
        if (in_array($operator, ['in', 'between', 'like', 'null', '='])) {
216 20
            return $operator;
217
        }
218
219 22
        return $finalOperator;
220
    }
221
222 22
    private function checkMagicWherePosition($operator, $position)
223
    {
224 22
        if (in_array($operator, ['first', 'last'])) {
225 4
            return $operator;
226
        }
227
228 22
        return $position;
229
    }
230
231 22
    private function checkMagicWhereNot($operator, $not)
232
    {
233 22
        if (in_array($operator, ['not'])) {
234 10
            return true;
235
        }
236
237 22
        return $not;
238
    }
239
240
    /**
241
     * Search a collection for the value specified.
242
     *
243
     * @param  string  $column   The column to search.
244
     * @param  string  $operator The operation to use during search.
245
     * @param  mixed   $value    The value to search for.
246
     * @param  boolean $inverse  Invert the results.
247
     * @param  string  $position Return the first or last object in the collection.
248
     *
249
     * @return self                 Return the filtered collection.
250
     */
251 24
    protected function getWhere($column, $operator, $value = null, $inverse = false, $position = null)
252
    {
253 24
        $output = clone $this;
254 24
        foreach ($output->items as $key => $item) {
255 24
            if (strstr($column, '->')) {
256 12
                $forget = $this->handleMultiTap($item, $column, $value, $operator, $inverse);
257 12
            } else {
258
                // No tap direct object access
259 12
                $forget = $this->whereObject($item, $column, $operator, $value, $inverse);
260
            }
261
262 24
            if ($forget == true) {
263 24
                $output->forget($key);
264 24
                continue;
265
            }
266 24
        }
267
268
        // Handel first and last.
269 24
        if (! is_null($position)) {
270 4
            return $output->$position();
271
        }
272
273 20
        return $output;
274
    }
275
276
    /**
277
     * @param $item
278
     * @param $column
279
     * @param $value
280
     * @param $operator
281
     * @param $inverse
282
     *
283
     * @return bool
284
     */
285 12
    private function handleMultiTap($item, $column, $value, $operator, $inverse)
286
    {
287 12
        list($objectToSearch, $columnToSearch) = $this->tapThroughObjects($column, $item);
288
289 12
        if ($objectToSearch instanceof self) {
290 12
            foreach ($objectToSearch as $subObject) {
291
                // The column has a tap that ends in a collection.
292 12
                return $this->whereObject($subObject, $columnToSearch, $operator, $value, $inverse);
293
            }
294
        } else {
295
            // The column has a tap that ends in direct access
296
            return $this->whereObject($objectToSearch, $columnToSearch, $operator, $value, $inverse);
297
        }
298
    }
299
300
    /**
301
     * @param $column
302
     * @param $item
303
     *
304
     * @return mixed
305
     */
306 12
    private function tapThroughObjects($column, $item)
307
    {
308 12
        $taps = explode('->', $column);
309
310 12
        $objectToSearch = $item;
311 12
        $columnToSearch = array_pop($taps);
312
313 12
        foreach ($taps as $tapKey => $tap) {
314
            // Keep tapping till we hit the last object.
315 12
            $objectToSearch = $objectToSearch->$tap;
316 12
        }
317
318 12
        return [$objectToSearch, $columnToSearch];
319
    }
320
321
    /**
322
     * Compare the object and column passed with the value using the operator
323
     *
324
     * @param  object  $object   The object we are searching.
325
     * @param  string  $column   The column to compare.
326
     * @param  string  $operator What type of comparison operation to perform.
327
     * @param  mixed   $value    The value to search for.
328
     * @param  boolean $inverse  Invert the results.
329
     *
330
     * @return boolean              Return true if the object should be removed from the collection.
331
     */
332 24
    private function whereObject($object, $column, $operator, $value = null, $inverse = false)
333
    {
334
        // Remove the object is the column does not exits.
335
        // Only do this if we aren't looking for null
336 24
        if (! $object->$column && $operator != 'null') {
337 12
            return true;
338
        }
339
340 24
        $method = 'getWhere' . ucfirst($operator);
341
342 24
        if (method_exists($this, $method)) {
343 20
            return $this->{$method}($object, $column, $value, $inverse);
344
        }
345
346 4
        return $this->getWhereDefault($object, $column, $value, $inverse);
347
    }
348
349 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...
350
    {
351 8
        if (! in_array($object->$column, $value) && $inverse == false) {
352 6
            return true;
353
        }
354 8
        if (in_array($object->$column, $value) && $inverse == true) {
355 2
            return true;
356
        }
357
358 8
        return false;
359
    }
360
361 4
    private function getWhereBetween($object, $column, $value, $inverse)
362
    {
363 4
        if ($inverse == false) {
364 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...
365 2
                return true;
366
            }
367 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...
368 2
            if ($object->$column >= $value[0] && $object->$column <= $value[1]) {
369 2
                return true;
370
            }
371
        }
372
373 4
        return false;
374
    }
375
376 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...
377
    {
378 4
        if (! strstr($object->$column, $value) && $inverse == false) {
379 2
            return true;
380
        }
381 4
        if (strstr($object->$column, $value) && $inverse == true) {
382 2
            return true;
383
        }
384
385 4
        return false;
386 1
    }
387
388 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...
389
    {
390 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...
391 2
            return true;
392
        }
393 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...
394 2
            return true;
395
        }
396
397 4
        return false;
398
    }
399
400 4
    private function getWhereDefault($object, $column, $value, $inverse)
401
    {
402 4
        if ($object->$column != $value && $inverse == false) {
403 2
            return true;
404
        }
405 4
        if ($object->$column == $value && $inverse == true) {
406 2
            return true;
407
        }
408
409 4
        return false;
410
    }
411
}
412