Completed
Push — master ( 01d429...8f4868 )
by Terzi
04:30
created

Mutable::moveBefore()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

373
        app('scaffold.module')->/** @scrutinizer ignore-call */ addSortable(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
374
            $keys,
375
            $callback
376
        );
377
378
        return $this;
379
    }
380
381
    /**
382
     * Remove column from Sortable collection.
383
     *
384
     * @return self
385
     */
386
    public function disableSorting($keys): self
387
    {
388
        if (!is_array($keys)) {
389
            $keys = func_get_args();
390
        }
391
392
        foreach ($keys as $key) {
393
            app('scaffold.module')->removeSortable($key);
0 ignored issues
show
Bug introduced by
The method removeSortable() does not exist on Illuminate\Foundation\Application. ( Ignorable by Annotation )

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

393
            app('scaffold.module')->/** @scrutinizer ignore-call */ removeSortable($key);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
394
        }
395
396
        return $this;
397
    }
398
399
    /**
400
     * Move an element to a position.
401
     *
402
     * @param string $id
403
     * @param $position
404
     *
405
     * @return static
406
     */
407
    protected function toPosition(string $id, $position): self
408
    {
409
        $element = $this->find($id);
410
411
        return $this
412
            ->except($id)
413
            ->insert($element, $position);
414
    }
415
416
    /**
417
     * @param $id
418
     *
419
     * @throws Exception
420
     */
421
    protected function notFound($id)
422
    {
423
        throw new Exception(sprintf('Element [%s] does not exist.', $id));
424
    }
425
426
    /**
427
     * Create element object from string.
428
     *
429
     * @param $element
430
     *
431
     * @return mixed
432
     */
433
    protected function createElement($element)
434
    {
435
        if (is_string($element)) {
436
            $element = Text::make($element, $element);
437
        }
438
439
        return $element;
440
    }
441
}
442