Mutable::update()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

Changes 4
Bugs 0 Features 1
Metric Value
cc 5
eloc 14
nc 4
nop 2
dl 0
loc 24
ccs 11
cts 12
cp 0.9167
crap 5.0144
rs 9.4888
c 4
b 0
f 1
1
<?php
2
3
namespace Terranet\Administrator\Collection;
4
5
use Closure;
6
use Illuminate\Support\Collection as BaseCollection;
7
use Illuminate\Support\Str;
8
use Terranet\Administrator\Contracts\Module\Sortable;
9
use Terranet\Administrator\Exception;
10
use Terranet\Administrator\Field\Text;
11
12
class Mutable extends BaseCollection
13
{
14
    /**
15
     * Push an item onto the end of the collection.
16
     *
17
     * @param  mixed  $element
18
     * @param  null|Closure  $callback
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
     * @return $this
41
     * @throws Exception
42
     */
43
    public function insert($element, $position, Closure $callback = null): self
44
    {
45 8
        $element = $this->createElement($element);
46
47 8
        if ($callback) {
48
            $callback($element);
49 8
        }
50 1
51
        if (\is_string($position)) {
52
            $this->push($element);
53 8
54 3
            return $this->move($element->id(), $position);
55
        }
56 3
57
        if ($position >= $this->count()) {
58
            return $this->push($element);
59 8
        }
60 4
61
        if (0 === $position) {
62
            return $this->prepend($element);
63 5
        }
64 3
65
        $items = [];
66
        foreach ($this->all() as $index => $value) {
67 3
            if ($index === $position) {
68 3
                array_push($items, $element);
69 3
            }
70 3
71
            array_push($items, $value);
72
        }
73 3
        $this->items = $items;
74
75 3
        return $this;
76
    }
77 3
78
    /**
79
     * Get all items except for those with the specified keys.
80
     *
81
     * @param  array|mixed|string  $keys
82
     * @return static
83
     */
84
    public function except($keys)
85
    {
86
        if ($keys instanceof self) {
87 11
            $keys = $keys->all();
88
        } elseif (!\is_array($keys)) {
89 11
            $keys = \func_get_args();
90
        }
91 11
92 10
        $items = $this->filter(function ($element) use ($keys) {
93
            return !\in_array($element->id(), $keys, true);
94
        })->all();
95
96 11
        $this->items = array_values($items);
97 11
98
        return $this;
99 11
    }
100
101 11
    /**
102
     * Retrieve only visible items.
103
     *
104
     * @param  string  $page
105
     * @return Mutable
106
     */
107
    public function visibleOnPage(string $page)
108
    {
109
        return $this->filter(function ($item) use ($page) {
110
            // @var Generic|Translatable $item
111
            return (($item instanceof Group) || $item->isVisibleOnPage($page)) && $item->visibleWhen();
112
        });
113
    }
114
115
    /**
116
     * Update elements behaviour.
117
     *
118
     * @param  string  $id
119
     * @param  Closure  $callback
120
     * @return $this
121
     * @throws Exception
122
     */
123
    public function update(string $id, Closure $callback): self
124
    {
125
        if (Str::contains($id, ',')) {
126
            collect(explode(',', $id))
127
                ->map('trim')
128
                ->each(function ($element) use ($callback) {
129 4
                    $this->update($element, $callback);
130
                });
131 4
132 1
            return $this;
133 1
        }
134
135 1
        $element = $this->find($id);
136 1
137
        if ($element && $callback) {
138 1
            $newElement = $callback($element);
139
            if ($newElement !== $element) {
140
                $position = $this->position($id);
141 4
                $this->except($id);
142
                $this->insert($newElement, $position);
143 4
            }
144 4
        }
145 4
146
        return $this;
147
    }
148
149
    /**
150
     * Replace the field.
151
     *
152 4
     * @param  mixed  $id
153
     * @param $value
154
     * @return $this|BaseCollection
155
     * @throws Exception
156
     */
157
    public function switch($id, $value)
158
    {
159
        if ($position = $this->position($id)) {
160
            $this->offsetSet($position, $value);
161
        }
162
163
        return $this;
164 2
    }
165
166 2
    /**
167 2
     * Update many elements at once.
168
     *
169
     * @param  array  $ids
170 2
     * @return $this
171
     * @throws Exception
172
     */
173
    public function updateMany(array $ids = []): self
174
    {
175
        foreach ($ids as $id => $callback) {
176
            $this->update($id, $callback);
177
        }
178
179
        return $this;
180
    }
181
182
    /**
183
     * Move element.
184
     *
185
     * @param $id
186
     * @param  int|mixed|string  $position
187 8
     * @return static
188
     * @throws Exception
189 8
     * @example: move('user_id', 4);
190 1
     * @example: move('user_id', 'before:name");
191
     * @example: move('user_id', 'after:id");
192
     */
193 7
    public function move(string $id, $position): self
194 3
    {
195
        if (is_numeric($position)) {
196
            return $this->toPosition($id, $position);
197 4
        }
198 3
199
        if (Str::startsWith($position, 'before:')) {
200
            return $this->moveBefore($id, substr($position, 7));
201 1
        }
202
203
        if (Str::startsWith($position, 'after:')) {
204
            return $this->moveAfter($id, substr($position, 6));
205
        }
206
207
        throw new Exception("Unknown moving direction: {$position}");
208
    }
209
210
    /**
211
     * Move element before another one.
212
     *
213
     * @param  string  $id
214 3
     * @param $target
215
     * @return $this
216 3
     * @throws Exception
217 3
     */
218 3
    public function moveBefore(string $id, $target)
219
    {
220 3
        if ($element = $this->find($id)) {
221 3
            $this->except($id);
222
            $targetPosition = $this->position($target);
223
224
            if ($targetPosition >= 0) {
225 3
                $this->insert($element, $targetPosition)->all();
226
            }
227
        }
228
229
        return $this;
230
    }
231
232
    /**
233
     * Move element after another one.
234
     *
235
     * @param  string  $id
236
     * @param $target
237
     * @return $this
238 3
     * @throws Exception
239
     */
240 3
    public function moveAfter(string $id, $target): self
241 3
    {
242
        if ($element = $this->find($id)) {
243 3
            $this->except($id);
244
245 3
            $targetPosition = $this->position($target);
246 3
247
            if ($targetPosition >= 0) {
248
                $this->insert($element, $targetPosition + 1)->all();
249
            }
250 3
        }
251
252
        return $this;
253
    }
254
255
    /**
256
     * Add a new elements group.
257
     *
258
     * @param  string  $id
259
     * @param  Closure  $callback
260
     * @return $this
261 1
     */
262
    public function group(string $id, Closure $callback): self
263 1
    {
264
        $group = new Group($id);
265 1
266
        $callback($group);
267 1
268
        $this->push($group);
269 1
270
        return $this;
271
    }
272
273
    /**
274
     * Join existing elements to a group.
275
     *
276
     * @param  array  $elements
277
     * @param  string  $groupId
278
     * @param  null|int|string  $position
279
     * @return $this
280
     * @throws Exception
281
     */
282
    public function stack(array $elements, string $groupId, $position = null): self
283 2
    {
284
        $group = new Group($groupId);
285 2
286
        $this->filter(function ($element) use ($elements) {
287
            return \in_array($element->id(), $elements, true);
288 2
        })->each(function ($element) use ($group) {
289
            $group->push($element);
290 2
            $this->items = $this->except($element->id())->all();
291 2
        });
292 2
293
        if ($position) {
294 2
            $this->insert($group, $position);
295 1
        } else {
296
            $this->push($group);
297 1
        }
298
299
        return $this;
300 2
    }
301
302
    /**
303
     * Build a collection.
304
     *
305
     * @param $decorator
306
     * @return static
307
     */
308
    public function build($decorator)
309
    {
310
        return $this->map(function ($element) use ($decorator) {
311
            if ($element instanceof Group) {
312
                $element->map(function ($e) use ($decorator) {
313
                    return $decorator->make($e);
314
                });
315
316
                return $element;
317
            }
318
319
            return $decorator->make($element);
320
        });
321
    }
322
323
    /**
324
     * Find an element.
325
     *
326
     * @param  string  $id
327
     * @return mixed
328
     * @throws Exception
329
     */
330
    public function find(string $id)
331
    {
332
        return $this->first(function ($element) use ($id) {
333
            return $element && $element->id() === $id;
334 16
        });
335
    }
336
337 16
    /**
338 16
     * Find an element position.
339
     *
340 16
     * @param  string  $id
341 1
     * @return null|int|string
342
     * @throws Exception
343
     */
344 16
    public function position(string $id): int
345
    {
346
        $i = 0;
347
        foreach ($this->all() as $item) {
348
            if ($item->id() === $id) {
349
                return $i;
350
            }
351
352
            ++$i;
353
        }
354
355
        return $this->notFound($id);
356 7
    }
357
358 7
    /**
359 7
     * Make elements sortable.
360 7
     *
361 7
     * @param  mixed string|array $keys
362
     * @param  \Closure  $callback
363
     * @return Mutable
364 3
     * @example: sortable(['title' => function($query) {  }])
365
     * @example: sortable(['title'])
366
     */
367 1
    public function sortable($keys, \Closure $callback = null)
368
    {
369
        if (\is_array($keys)) {
370
            foreach ($keys as $id => $callback) {
371
                if (\is_string($id)) {
372
                    $this->sortable($id, $callback);
373
                } else {
374
                    $this->sortable($callback);
375
                }
376
            }
377
378
            return $this;
379
        }
380
381
        $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

381
        $module = /** @scrutinizer ignore-call */ app('scaffold.module');
Loading history...
382
        if ($module instanceof Sortable && method_exists($module, 'addSortable')) {
383
            $module->addSortable($keys, $callback);
384
        }
385
386
        return $this;
387
    }
388
389
    /**
390
     * Remove column from Sortable collection.
391
     *
392
     * @param  array|string  $keys
393
     * @return self
394
     */
395
    public function disableSorting($keys): self
396
    {
397
        if (!\is_array($keys)) {
398
            $keys = \func_get_args();
399
        }
400
401
        $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

401
        $module = /** @scrutinizer ignore-call */ app('scaffold.module');
Loading history...
402
        if ($module instanceof Sortable && method_exists($module, 'removeSortable')) {
403
            foreach ($keys as $key) {
404
                $module->removeSortable($key);
405
            }
406
        }
407
408
        return $this;
409
    }
410
411
    /**
412
     * Move an element to a position.
413
     *
414
     * @param  string  $id
415
     * @param  int|string  $position
416
     * @return static
417
     * @throws Exception
418
     */
419
    protected function toPosition(string $id, $position): self
420
    {
421
        $element = $this->find($id);
422
423
        return $this
424
            ->except($id)
425
            ->insert($element, $position);
426
    }
427
428
    /**
429
     * @param  string  $id
430
     * @throws Exception
431
     */
432
    protected function notFound(string $id)
433
    {
434
        throw new Exception(sprintf('Element [%s] does not exist.', $id));
435 1
    }
436
437 1
    /**
438
     * Create element object from string.
439
     *
440 1
     * @param $element
441 1
     * @return mixed
442
     */
443
    protected function createElement($element)
444
    {
445
        if (\is_string($element)) {
446
            $element = Text::make($element, $element);
447
        }
448
449 2
        return $element;
450
    }
451
}
452