Completed
Push — master ( e23ec4...bcf727 )
by Terzi
04:42
created

Mutable::notFound()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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, ',')) {
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:')) {
194 3
            return $this->moveBefore($id, substr($position, 7));
195
        }
196
197 4
        if (starts_with($position, 'after:')) {
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 join(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');
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');
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