Completed
Push — master ( 35ae7b...e23ec4 )
by Terzi
07:13 queued 02:37
created

Mutable::build()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 1
nop 1
dl 0
loc 12
ccs 0
cts 2
cp 0
crap 6
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\Generic;
10
use Terranet\Administrator\Field\Text;
11
use Terranet\Administrator\Field\Translatable;
12
13
class Mutable extends BaseCollection
14
{
15
    /**
16
     * Push an item onto the end of the collection.
17
     *
18
     * @param  mixed $element
19
     * @param null|Closure $callback
20
     *
21 29
     * @return $this
22
     */
23 29
    public function push($element, Closure $callback = null): self
24
    {
25 29
        $element = $this->createElement($element);
26 2
27
        if ($callback) {
28
            $callback($element);
29 29
        }
30
31 29
        parent::push($element);
32
33
        return $this;
34
    }
35
36
    /**
37
     * Insert an element into collection at specified position.
38
     *
39
     * @param $element
40
     * @param $position
41
     * @param null|Closure $callback
42
     *
43
     * @throws Exception
44
     *
45 8
     * @return $this
46
     */
47 8
    public function insert($element, $position, Closure $callback = null): self
48
    {
49 8
        $element = $this->createElement($element);
50 1
51
        if ($callback) {
52
            $callback($element);
53 8
        }
54 3
55
        if (\is_string($position)) {
56 3
            $this->push($element);
57
58
            return $this->move($element->id(), $position);
59 8
        }
60 4
61
        if ($position >= $this->count()) {
62
            return $this->push($element);
63 5
        }
64 3
65
        if (0 === $position) {
66
            return $this->prepend($element);
67 3
        }
68 3
69 3
        $items = [];
70 3
        foreach ($this->all() as $index => $value) {
71
            if ($index === $position) {
72
                array_push($items, $element);
73 3
            }
74
75 3
            array_push($items, $value);
76
        }
77 3
        $this->items = $items;
78
79
        return $this;
80
    }
81
82
    /**
83
     * Get all items except for those with the specified keys.
84
     *
85
     * @param  array|mixed|string $keys
86
     *
87 11
     * @return static
88
     */
89 11
    public function except($keys)
90
    {
91 11
        if ($keys instanceof self) {
92 10
            $keys = $keys->all();
93
        } elseif (!\is_array($keys)) {
94
            $keys = \func_get_args();
95
        }
96 11
97 11
        $items = $this->filter(function ($element) use ($keys) {
98
            return !\in_array($element->id(), $keys, true);
99 11
        })->all();
100
101 11
        $this->items = array_values($items);
102
103
        return $this;
104
    }
105
106
    /**
107
     * Retrieve only visible items.
108
     *
109
     * @param string $page
110
     *
111
     * @return Mutable
112
     */
113
    public function visibleOnPage(string $page)
114
    {
115
        return $this->filter(function ($item) use ($page) {
116
            // @var Generic|Translatable $item
117
            return (($item instanceof Group) || $item->isVisibleOnPage($page)) && $item->visibleWhen();
118
        });
119
    }
120
121
    /**
122
     * Update elements behaviour.
123
     *
124
     * @param string $id
125
     * @param Closure $callback
126 4
     *
127
     * @throws Exception
128 4
     *
129 1
     * @return $this
130 1
     */
131
    public function update(string $id, Closure $callback): self
132 1
    {
133 1
        if (str_contains($id, ',')) {
134
            collect(explode(',', $id))
135 1
                ->map('trim')
136
                ->each(function ($element) use ($callback) {
137
                    $this->update($element, $callback);
138 4
                });
139
140 4
            return $this;
141 4
        }
142 4
143
        $element = $this->find($id);
144
145
        if ($element && $callback) {
146
            $newElement = $callback($element);
147
            if ($newElement !== $element) {
148
                $position = $this->position($id);
149 4
                $this->except($id);
150
                $this->insert($newElement, $position);
151
            }
152
        }
153
154
        return $this;
155
    }
156
157
    /**
158
     * Update many elements at once.
159 2
     *
160
     * @param array $ids
161 2
     *
162 2
     * @throws Exception
163
     *
164
     * @return $this
165 2
     */
166
    public function updateMany(array $ids = []): self
167
    {
168
        foreach ($ids as $id => $callback) {
169
            $this->update($id, $callback);
170
        }
171
172
        return $this;
173
    }
174
175
    /**
176
     * Move element.
177
     *
178
     * @param $id
179
     * @param int|mixed|string $position
180
     *
181
     * @throws Exception
182 8
     *
183
     * @return static
184 8
     *
185 1
     * @example: move('user_id', 4);
186
     * @example: move('user_id', 'before:name");
187
     * @example: move('user_id', 'after:id");
188 7
     */
189 3
    public function move(string $id, $position): self
190
    {
191
        if (is_numeric($position)) {
192 4
            return $this->toPosition($id, $position);
193 3
        }
194
195
        if (starts_with($position, 'before:')) {
196 1
            return $this->moveBefore($id, substr($position, 7));
197
        }
198
199
        if (starts_with($position, 'after:')) {
200
            return $this->moveAfter($id, substr($position, 6));
201
        }
202
203
        throw new Exception("Unknown moving direction: {$position}");
204
    }
205
206
    /**
207 3
     * Move element before another one.
208
     *
209 3
     * @param string $id
210 3
     * @param $target
211 3
     *
212
     * @throws Exception
213 3
     *
214 3
     * @return $this
215
     */
216
    public function moveBefore(string $id, $target)
217
    {
218 3
        if ($element = $this->find($id)) {
219
            $this->except($id);
220
            $targetPosition = $this->position($target);
221
222
            if ($targetPosition >= 0) {
223
                $this->insert($element, $targetPosition)->all();
224
            }
225
        }
226
227
        return $this;
228
    }
229
230
    /**
231 3
     * Move element after another one.
232
     *
233 3
     * @param string $id
234 3
     * @param $target
235
     *
236 3
     * @throws Exception
237
     *
238 3
     * @return $this
239 3
     */
240
    public function moveAfter(string $id, $target): self
241
    {
242
        if ($element = $this->find($id)) {
243 3
            $this->except($id);
244
245
            $targetPosition = $this->position($target);
246
247
            if ($targetPosition >= 0) {
248
                $this->insert($element, $targetPosition + 1)->all();
249
            }
250
        }
251
252
        return $this;
253
    }
254 1
255
    /**
256 1
     * Add a new elements group.
257
     *
258 1
     * @param string $id
259
     * @param Closure $callback
260 1
     *
261
     * @return $this
262 1
     */
263
    public function group(string $id, Closure $callback): self
264
    {
265
        $group = new Group($id);
266
267
        $callback($group);
268
269
        $this->push($group);
270
271
        return $this;
272
    }
273
274
    /**
275
     * Join existing elements to a group.
276 2
     *
277
     * @param array $elements
278 2
     * @param string $groupId
279
     * @param null|int|string $position
280
     *
281 2
     * @throws Exception
282
     *
283 2
     * @return $this
284 2
     */
285 2
    public function join(array $elements, string $groupId, $position = null): self
286
    {
287 2
        $group = new Group($groupId);
288 1
289
        $this->filter(function ($element) use ($elements) {
290 1
            return \in_array($element->id(), $elements, true);
291
        })->each(function ($element) use ($group) {
292
            $group->push($element);
293 2
            $this->items = $this->except($element->id())->all();
294
        });
295
296
        if ($position) {
297
            $this->insert($group, $position);
298
        } else {
299
            $this->push($group);
300
        }
301
302
        return $this;
303
    }
304
305
    /**
306
     * Build a collection.
307
     *
308
     * @param $decorator
309
     *
310
     * @return static
311
     */
312
    public function build($decorator)
313
    {
314
        return $this->map(function ($element) use ($decorator) {
315
            if ($element instanceof Group) {
316
                $element->map(function ($e) use ($decorator) {
317
                    return $decorator->make($e);
318
                });
319
320
                return $element;
321
            }
322
323
            return $decorator->make($element);
324
        });
325
    }
326
327 16
    /**
328
     * Find an element.
329
     *
330 16
     * @param string $id
331 16
     *
332
     * @throws Exception
333 16
     *
334 1
     * @return mixed
335
     */
336
    public function find(string $id)
337 16
    {
338
        $element = $this->first(function ($element) use ($id) {
339
            return $element && $element->id() === $id;
340
        });
341
342
        if (!$element) {
343
            $this->notFound($id);
344
        }
345
346
        return $element;
347
    }
348
349 7
    /**
350
     * Find an element position.
351 7
     *
352 7
     * @param string $id
353 7
     *
354 7
     * @throws Exception
355
     *
356
     * @return null|int|string
357 3
     */
358
    public function position(string $id): int
359
    {
360 1
        $i = 0;
361
        foreach ($this->all() as $item) {
362
            if ($item->id() === $id) {
363
                return $i;
364
            }
365
366
            ++$i;
367
        }
368
369
        return $this->notFound($id);
370
    }
371
372
    /**
373
     * Make elements sortable.
374
     *
375
     * @param mixed string|array $keys
376
     * @param \Closure $callback
377
     * @example: sortable(['title'])
378
     * @example: sortable(['title' => function($query) {  }])
379
     *
380
     * @return Mutable
381
     */
382
    public function sortable($keys, \Closure $callback = null)
383
    {
384
        if (\is_array($keys)) {
385
            foreach ($keys as $id => $callback) {
386
                if (\is_string($id)) {
387
                    $this->sortable($id, $callback);
388
                } else {
389
                    $this->sortable($callback);
390
                }
391
            }
392
393
            return $this;
394
        }
395
396
        $module = app('scaffold.module');
397
        if ($module instanceof Sortable && method_exists($module, 'addSortable')) {
398
            $module->addSortable($keys, $callback);
399
        }
400
401
        return $this;
402
    }
403
404
    /**
405
     * Remove column from Sortable collection.
406
     *
407
     * @param array|string $keys
408
     *
409
     * @return self
410
     */
411
    public function disableSorting($keys): self
412
    {
413
        if (!\is_array($keys)) {
414
            $keys = \func_get_args();
415
        }
416
417
        $module = app('scaffold.module');
418
        if ($module instanceof Sortable && method_exists($module, 'removeSortable')) {
419
            foreach ($keys as $key) {
420
                $module->removeSortable($key);
421
            }
422
        }
423
424
        return $this;
425
    }
426
427
    /**
428 1
     * Move an element to a position.
429
     *
430 1
     * @param string $id
431
     * @param int|string $position
432
     *
433 1
     * @throws Exception
434 1
     *
435
     * @return static
436
     */
437
    protected function toPosition(string $id, $position): self
438
    {
439
        $element = $this->find($id);
440
441
        return $this
442 2
            ->except($id)
443
            ->insert($element, $position);
444 2
    }
445
446
    /**
447
     * @param string $id
448
     *
449
     * @throws Exception
450
     */
451
    protected function notFound(string $id)
452
    {
453
        throw new Exception(sprintf('Element [%s] does not exist.', $id));
454 29
    }
455
456 29
    /**
457 2
     * Create element object from string.
458
     *
459
     * @param $element
460 29
     *
461
     * @return mixed
462
     */
463
    protected function createElement($element)
464
    {
465
        if (\is_string($element)) {
466
            $element = Text::make($element, $element);
467
        }
468
469
        return $element;
470
    }
471
}
472