Completed
Push — master ( 2ee94f...e70fcb )
by Terzi
06:41
created

Mutable::update()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.2

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 4
nop 2
dl 0
loc 24
ccs 12
cts 15
cp 0.8
crap 5.2
rs 9.4888
c 0
b 0
f 0
1
<?php
2
3
namespace Terranet\Administrator\Collection;
4
5
use Closure;
6
use Illuminate\Support\Collection as BaseCollection;
7
use Terranet\Administrator\Contracts\Module\Sortable;
8
use Terranet\Administrator\Exception;
9
use Terranet\Administrator\Field\Text;
10
11
class Mutable extends BaseCollection
12
{
13
    /**
14
     * Push an item onto the end of the collection.
15
     *
16
     * @param  mixed $element
17
     * @param null|Closure $callback
18
     *
19
     * @return $this
20
     */
21 29
    public function push($element, Closure $callback = null): self
22
    {
23 29
        $element = $this->createElement($element);
24
25 29
        if ($callback) {
26 2
            $callback($element);
27
        }
28
29 29
        parent::push($element);
30
31 29
        return $this;
32
    }
33
34
    /**
35
     * Insert an element into collection at specified position.
36
     *
37
     * @param $element
38
     * @param $position
39
     * @param null|Closure $callback
40
     *
41
     * @throws Exception
42
     *
43
     * @return $this
44
     */
45 8
    public function insert($element, $position, Closure $callback = null): self
46
    {
47 8
        $element = $this->createElement($element);
48
49 8
        if ($callback) {
50 1
            $callback($element);
51
        }
52
53 8
        if (\is_string($position)) {
54 3
            $this->push($element);
55
56 3
            return $this->move($element->id(), $position);
57
        }
58
59 8
        if ($position >= $this->count()) {
60 4
            return $this->push($element);
61
        }
62
63 5
        if (0 === $position) {
64 3
            return $this->prepend($element);
65
        }
66
67 3
        $items = [];
68 3
        foreach ($this->all() as $index => $value) {
69 3
            if ($index === $position) {
70 3
                array_push($items, $element);
71
            }
72
73 3
            array_push($items, $value);
74
        }
75 3
        $this->items = $items;
76
77 3
        return $this;
78
    }
79
80
    /**
81
     * Get all items except for those with the specified keys.
82
     *
83
     * @param  array|mixed|string $keys
84
     *
85
     * @return static
86
     */
87 11
    public function except($keys)
88
    {
89 11
        if ($keys instanceof self) {
90
            $keys = $keys->all();
91 11
        } elseif (!\is_array($keys)) {
92 10
            $keys = \func_get_args();
93
        }
94
95
        $items = $this->filter(function ($element) use ($keys) {
96 11
            return !\in_array($element->id(), $keys, true);
97 11
        })->all();
98
99 11
        $this->items = array_values($items);
100
101 11
        return $this;
102
    }
103
104
    /**
105
     * Retrieve only visible items.
106
     *
107
     * @param string $page
108
     *
109
     * @return Mutable
110
     */
111
    public function visibleOnPage(string $page)
112
    {
113
        return $this->filter(function ($item) use ($page) {
114
            // @var Generic|Translatable $item
115
            return (($item instanceof Group) || $item->isVisibleOnPage($page)) && $item->visibleWhen();
116
        });
117
    }
118
119
    /**
120
     * Update elements behaviour.
121
     *
122
     * @param string $id
123
     * @param Closure $callback
124
     *
125
     * @throws Exception
126
     *
127
     * @return $this
128
     */
129 4
    public function update(string $id, Closure $callback): self
130
    {
131 4
        if (str_contains($id, ',')) {
0 ignored issues
show
Deprecated Code introduced by
The function str_contains() has been deprecated: Str::contains() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

131
        if (/** @scrutinizer ignore-deprecated */ str_contains($id, ',')) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
132 1
            collect(explode(',', $id))
133 1
                ->map('trim')
134
                ->each(function ($element) use ($callback) {
135 1
                    $this->update($element, $callback);
136 1
                });
137
138 1
            return $this;
139
        }
140
141 4
        $element = $this->find($id);
142
143 4
        if ($element && $callback) {
144 4
            $newElement = $callback($element);
145 4
            if ($newElement !== $element) {
146
                $position = $this->position($id);
147
                $this->except($id);
148
                $this->insert($newElement, $position);
149
            }
150
        }
151
152 4
        return $this;
153
    }
154
155
    /**
156
     * Update many elements at once.
157
     *
158
     * @param array $ids
159
     *
160
     * @throws Exception
161
     *
162
     * @return $this
163
     */
164 2
    public function updateMany(array $ids = []): self
165
    {
166 2
        foreach ($ids as $id => $callback) {
167 2
            $this->update($id, $callback);
168
        }
169
170 2
        return $this;
171
    }
172
173
    /**
174
     * Move element.
175
     *
176
     * @param $id
177
     * @param int|mixed|string $position
178
     *
179
     * @throws Exception
180
     *
181
     * @return static
182
     *
183
     * @example: move('user_id', 4);
184
     * @example: move('user_id', 'before:name");
185
     * @example: move('user_id', 'after:id");
186
     */
187 8
    public function move(string $id, $position): self
188
    {
189 8
        if (is_numeric($position)) {
190 1
            return $this->toPosition($id, $position);
191
        }
192
193 7
        if (starts_with($position, 'before:')) {
0 ignored issues
show
Deprecated Code introduced by
The function starts_with() has been deprecated: Str::startsWith() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

193
        if (/** @scrutinizer ignore-deprecated */ starts_with($position, 'before:')) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
194 3
            return $this->moveBefore($id, substr($position, 7));
195
        }
196
197 4
        if (starts_with($position, 'after:')) {
0 ignored issues
show
Deprecated Code introduced by
The function starts_with() has been deprecated: Str::startsWith() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

197
        if (/** @scrutinizer ignore-deprecated */ starts_with($position, 'after:')) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
198 3
            return $this->moveAfter($id, substr($position, 6));
199
        }
200
201 1
        throw new Exception("Unknown moving direction: {$position}");
202
    }
203
204
    /**
205
     * Move element before another one.
206
     *
207
     * @param string $id
208
     * @param $target
209
     *
210
     * @throws Exception
211
     *
212
     * @return $this
213
     */
214 3
    public function moveBefore(string $id, $target)
215
    {
216 3
        if ($element = $this->find($id)) {
217 3
            $this->except($id);
218 3
            $targetPosition = $this->position($target);
219
220 3
            if ($targetPosition >= 0) {
221 3
                $this->insert($element, $targetPosition)->all();
222
            }
223
        }
224
225 3
        return $this;
226
    }
227
228
    /**
229
     * Move element after another one.
230
     *
231
     * @param string $id
232
     * @param $target
233
     *
234
     * @throws Exception
235
     *
236
     * @return $this
237
     */
238 3
    public function moveAfter(string $id, $target): self
239
    {
240 3
        if ($element = $this->find($id)) {
241 3
            $this->except($id);
242
243 3
            $targetPosition = $this->position($target);
244
245 3
            if ($targetPosition >= 0) {
246 3
                $this->insert($element, $targetPosition + 1)->all();
247
            }
248
        }
249
250 3
        return $this;
251
    }
252
253
    /**
254
     * Add a new elements group.
255
     *
256
     * @param string $id
257
     * @param Closure $callback
258
     *
259
     * @return $this
260
     */
261 1
    public function group(string $id, Closure $callback): self
262
    {
263 1
        $group = new Group($id);
264
265 1
        $callback($group);
266
267 1
        $this->push($group);
268
269 1
        return $this;
270
    }
271
272
    /**
273
     * Join existing elements to a group.
274
     *
275
     * @param array $elements
276
     * @param string $groupId
277
     * @param null|int|string $position
278
     *
279
     * @throws Exception
280
     *
281
     * @return $this
282
     */
283 2
    public function stack(array $elements, string $groupId, $position = null): self
284
    {
285 2
        $group = new Group($groupId);
286
287
        $this->filter(function ($element) use ($elements) {
288 2
            return \in_array($element->id(), $elements, true);
289
        })->each(function ($element) use ($group) {
290 2
            $group->push($element);
291 2
            $this->items = $this->except($element->id())->all();
292 2
        });
293
294 2
        if ($position) {
295 1
            $this->insert($group, $position);
296
        } else {
297 1
            $this->push($group);
298
        }
299
300 2
        return $this;
301
    }
302
303
    /**
304
     * Build a collection.
305
     *
306
     * @param $decorator
307
     *
308
     * @return static
309
     */
310
    public function build($decorator)
311
    {
312
        return $this->map(function ($element) use ($decorator) {
313
            if ($element instanceof Group) {
314
                $element->map(function ($e) use ($decorator) {
315
                    return $decorator->make($e);
316
                });
317
318
                return $element;
319
            }
320
321
            return $decorator->make($element);
322
        });
323
    }
324
325
    /**
326
     * Find an element.
327
     *
328
     * @param string $id
329
     *
330
     * @throws Exception
331
     *
332
     * @return mixed
333
     */
334 16
    public function find(string $id)
335
    {
336
        $element = $this->first(function ($element) use ($id) {
337 16
            return $element && $element->id() === $id;
338 16
        });
339
340 16
        if (!$element) {
341 1
            $this->notFound($id);
342
        }
343
344 16
        return $element;
345
    }
346
347
    /**
348
     * Find an element position.
349
     *
350
     * @param string $id
351
     *
352
     * @throws Exception
353
     *
354
     * @return null|int|string
355
     */
356 7
    public function position(string $id): int
357
    {
358 7
        $i = 0;
359 7
        foreach ($this->all() as $item) {
360 7
            if ($item->id() === $id) {
361 7
                return $i;
362
            }
363
364 3
            ++$i;
365
        }
366
367 1
        return $this->notFound($id);
368
    }
369
370
    /**
371
     * Make elements sortable.
372
     *
373
     * @param mixed string|array $keys
374
     * @param \Closure $callback
375
     * @example: sortable(['title'])
376
     * @example: sortable(['title' => function($query) {  }])
377
     *
378
     * @return Mutable
379
     */
380
    public function sortable($keys, \Closure $callback = null)
381
    {
382
        if (\is_array($keys)) {
383
            foreach ($keys as $id => $callback) {
384
                if (\is_string($id)) {
385
                    $this->sortable($id, $callback);
386
                } else {
387
                    $this->sortable($callback);
388
                }
389
            }
390
391
            return $this;
392
        }
393
394
        $module = app('scaffold.module');
0 ignored issues
show
Bug introduced by
The function app was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

394
        $module = /** @scrutinizer ignore-call */ app('scaffold.module');
Loading history...
395
        if ($module instanceof Sortable && method_exists($module, 'addSortable')) {
396
            $module->addSortable($keys, $callback);
397
        }
398
399
        return $this;
400
    }
401
402
    /**
403
     * Remove column from Sortable collection.
404
     *
405
     * @param array|string $keys
406
     *
407
     * @return self
408
     */
409
    public function disableSorting($keys): self
410
    {
411
        if (!\is_array($keys)) {
412
            $keys = \func_get_args();
413
        }
414
415
        $module = app('scaffold.module');
0 ignored issues
show
Bug introduced by
The function app was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

415
        $module = /** @scrutinizer ignore-call */ app('scaffold.module');
Loading history...
416
        if ($module instanceof Sortable && method_exists($module, 'removeSortable')) {
417
            foreach ($keys as $key) {
418
                $module->removeSortable($key);
419
            }
420
        }
421
422
        return $this;
423
    }
424
425
    /**
426
     * Move an element to a position.
427
     *
428
     * @param string $id
429
     * @param int|string $position
430
     *
431
     * @throws Exception
432
     *
433
     * @return static
434
     */
435 1
    protected function toPosition(string $id, $position): self
436
    {
437 1
        $element = $this->find($id);
438
439
        return $this
440 1
            ->except($id)
441 1
            ->insert($element, $position);
442
    }
443
444
    /**
445
     * @param string $id
446
     *
447
     * @throws Exception
448
     */
449 2
    protected function notFound(string $id)
450
    {
451 2
        throw new Exception(sprintf('Element [%s] does not exist.', $id));
452
    }
453
454
    /**
455
     * Create element object from string.
456
     *
457
     * @param $element
458
     *
459
     * @return mixed
460
     */
461 29
    protected function createElement($element)
462
    {
463 29
        if (\is_string($element)) {
464 2
            $element = Text::make($element, $element);
465
        }
466
467 29
        return $element;
468
    }
469
}
470