Test Failed
Push — master ( 470d4b...33c92c )
by Terzi
04:31
created

src/Collection/Mutable.php (2 issues)

Labels
Severity
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
     * @return $this
19
     */
20
    public function push($element, Closure $callback = null): self
21 29
    {
22
        $element = $this->createElement($element);
23 29
24
        if ($callback) {
25 29
            $callback($element);
26 2
        }
27
28
        parent::push($element);
29 29
30
        return $this;
31 29
    }
32
33
    /**
34
     * Insert an element into collection at specified position.
35
     *
36
     * @param $element
37
     * @param $position
38
     * @param  null|Closure  $callback
39
     * @return $this
40
     * @throws Exception
41
     */
42
    public function insert($element, $position, Closure $callback = null): self
43
    {
44
        $element = $this->createElement($element);
45 8
46
        if ($callback) {
47 8
            $callback($element);
48
        }
49 8
50 1
        if (\is_string($position)) {
51
            $this->push($element);
52
53 8
            return $this->move($element->id(), $position);
54 3
        }
55
56 3
        if ($position >= $this->count()) {
57
            return $this->push($element);
58
        }
59 8
60 4
        if (0 === $position) {
61
            return $this->prepend($element);
62
        }
63 5
64 3
        $items = [];
65
        foreach ($this->all() as $index => $value) {
66
            if ($index === $position) {
67 3
                array_push($items, $element);
68 3
            }
69 3
70 3
            array_push($items, $value);
71
        }
72
        $this->items = $items;
73 3
74
        return $this;
75 3
    }
76
77 3
    /**
78
     * Get all items except for those with the specified keys.
79
     *
80
     * @param  array|mixed|string  $keys
81
     * @return static
82
     */
83
    public function except($keys)
84
    {
85
        if ($keys instanceof self) {
86
            $keys = $keys->all();
87 11
        } elseif (!\is_array($keys)) {
88
            $keys = \func_get_args();
89 11
        }
90
91 11
        $items = $this->filter(function ($element) use ($keys) {
92 10
            return !\in_array($element->id(), $keys, true);
93
        })->all();
94
95
        $this->items = array_values($items);
96 11
97 11
        return $this;
98
    }
99 11
100
    /**
101 11
     * Retrieve only visible items.
102
     *
103
     * @param  string  $page
104
     * @return Mutable
105
     */
106
    public function visibleOnPage(string $page)
107
    {
108
        return $this->filter(function ($item) use ($page) {
109
            // @var Generic|Translatable $item
110
            return (($item instanceof Group) || $item->isVisibleOnPage($page)) && $item->visibleWhen();
111
        });
112
    }
113
114
    /**
115
     * Update elements behaviour.
116
     *
117
     * @param  string  $id
118
     * @param  Closure  $callback
119
     * @return $this
120
     * @throws Exception
121
     */
122
    public function update(string $id, Closure $callback): self
123
    {
124
        if (str_contains($id, ',')) {
0 ignored issues
show
The function str_contains 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

124
        if (/** @scrutinizer ignore-call */ str_contains($id, ',')) {
Loading history...
125
            collect(explode(',', $id))
126
                ->map('trim')
127
                ->each(function ($element) use ($callback) {
128
                    $this->update($element, $callback);
129 4
                });
130
131 4
            return $this;
132 1
        }
133 1
134
        $element = $this->find($id);
135 1
136 1
        if ($element && $callback) {
137
            $newElement = $callback($element);
138 1
            if ($newElement !== $element) {
139
                $position = $this->position($id);
140
                $this->except($id);
141 4
                $this->insert($newElement, $position);
142
            }
143 4
        }
144 4
145 4
        return $this;
146
    }
147
148
    /**
149
     * Replace the field.
150
     *
151
     * @param  mixed  $id
152 4
     * @param $value
153
     * @return $this|BaseCollection
154
     * @throws Exception
155
     */
156
    public function switch($id, $value)
157
    {
158
        if ($position = $this->position($id)) {
159
            $this->offsetSet($position, $value);
160
        }
161
162
        return $this;
163
    }
164 2
165
    /**
166 2
     * Update many elements at once.
167 2
     *
168
     * @param  array  $ids
169
     * @return $this
170 2
     * @throws Exception
171
     */
172
    public function updateMany(array $ids = []): self
173
    {
174
        foreach ($ids as $id => $callback) {
175
            $this->update($id, $callback);
176
        }
177
178
        return $this;
179
    }
180
181
    /**
182
     * Move element.
183
     *
184
     * @param $id
185
     * @param  int|mixed|string  $position
186
     * @return static
187 8
     * @throws Exception
188
     * @example: move('user_id', 4);
189 8
     * @example: move('user_id', 'before:name");
190 1
     * @example: move('user_id', 'after:id");
191
     */
192
    public function move(string $id, $position): self
193 7
    {
194 3
        if (is_numeric($position)) {
195
            return $this->toPosition($id, $position);
196
        }
197 4
198 3
        if (starts_with($position, 'before:')) {
0 ignored issues
show
The function starts_with 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

198
        if (/** @scrutinizer ignore-call */ starts_with($position, 'before:')) {
Loading history...
199
            return $this->moveBefore($id, substr($position, 7));
200
        }
201 1
202
        if (starts_with($position, 'after:')) {
203
            return $this->moveAfter($id, substr($position, 6));
204
        }
205
206
        throw new Exception("Unknown moving direction: {$position}");
207
    }
208
209
    /**
210
     * Move element before another one.
211
     *
212
     * @param  string  $id
213
     * @param $target
214 3
     * @return $this
215
     * @throws Exception
216 3
     */
217 3
    public function moveBefore(string $id, $target)
218 3
    {
219
        if ($element = $this->find($id)) {
220 3
            $this->except($id);
221 3
            $targetPosition = $this->position($target);
222
223
            if ($targetPosition >= 0) {
224
                $this->insert($element, $targetPosition)->all();
225 3
            }
226
        }
227
228
        return $this;
229
    }
230
231
    /**
232
     * Move element after another one.
233
     *
234
     * @param  string  $id
235
     * @param $target
236
     * @return $this
237
     * @throws Exception
238 3
     */
239
    public function moveAfter(string $id, $target): self
240 3
    {
241 3
        if ($element = $this->find($id)) {
242
            $this->except($id);
243 3
244
            $targetPosition = $this->position($target);
245 3
246 3
            if ($targetPosition >= 0) {
247
                $this->insert($element, $targetPosition + 1)->all();
248
            }
249
        }
250 3
251
        return $this;
252
    }
253
254
    /**
255
     * Add a new elements group.
256
     *
257
     * @param  string  $id
258
     * @param  Closure  $callback
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
     * @return $this
279
     * @throws Exception
280
     */
281
    public function stack(array $elements, string $groupId, $position = null): self
282
    {
283 2
        $group = new Group($groupId);
284
285 2
        $this->filter(function ($element) use ($elements) {
286
            return \in_array($element->id(), $elements, true);
287
        })->each(function ($element) use ($group) {
288 2
            $group->push($element);
289
            $this->items = $this->except($element->id())->all();
290 2
        });
291 2
292 2
        if ($position) {
293
            $this->insert($group, $position);
294 2
        } else {
295 1
            $this->push($group);
296
        }
297 1
298
        return $this;
299
    }
300 2
301
    /**
302
     * Build a collection.
303
     *
304
     * @param $decorator
305
     * @return static
306
     */
307
    public function build($decorator)
308
    {
309
        return $this->map(function ($element) use ($decorator) {
310
            if ($element instanceof Group) {
311
                $element->map(function ($e) use ($decorator) {
312
                    return $decorator->make($e);
313
                });
314
315
                return $element;
316
            }
317
318
            return $decorator->make($element);
319
        });
320
    }
321
322
    /**
323
     * Find an element.
324
     *
325
     * @param  string  $id
326
     * @return mixed
327
     * @throws Exception
328
     */
329
    public function find(string $id)
330
    {
331
        return $this->first(function ($element) use ($id) {
332
            return $element && $element->id() === $id;
333
        });
334 16
    }
335
336
    /**
337 16
     * Find an element position.
338 16
     *
339
     * @param  string  $id
340 16
     * @return null|int|string
341 1
     * @throws Exception
342
     */
343
    public function position(string $id): int
344 16
    {
345
        $i = 0;
346
        foreach ($this->all() as $item) {
347
            if ($item->id() === $id) {
348
                return $i;
349
            }
350
351
            ++$i;
352
        }
353
354
        return $this->notFound($id);
355
    }
356 7
357
    /**
358 7
     * Make elements sortable.
359 7
     *
360 7
     * @param  mixed string|array $keys
361 7
     * @param  \Closure  $callback
362
     * @return Mutable
363
     * @example: sortable(['title' => function($query) {  }])
364 3
     * @example: sortable(['title'])
365
     */
366
    public function sortable($keys, \Closure $callback = null)
367 1
    {
368
        if (\is_array($keys)) {
369
            foreach ($keys as $id => $callback) {
370
                if (\is_string($id)) {
371
                    $this->sortable($id, $callback);
372
                } else {
373
                    $this->sortable($callback);
374
                }
375
            }
376
377
            return $this;
378
        }
379
380
        $module = app('scaffold.module');
381
        if ($module instanceof Sortable && method_exists($module, 'addSortable')) {
382
            $module->addSortable($keys, $callback);
383
        }
384
385
        return $this;
386
    }
387
388
    /**
389
     * Remove column from Sortable collection.
390
     *
391
     * @param  array|string  $keys
392
     * @return self
393
     */
394
    public function disableSorting($keys): self
395
    {
396
        if (!\is_array($keys)) {
397
            $keys = \func_get_args();
398
        }
399
400
        $module = app('scaffold.module');
401
        if ($module instanceof Sortable && method_exists($module, 'removeSortable')) {
402
            foreach ($keys as $key) {
403
                $module->removeSortable($key);
404
            }
405
        }
406
407
        return $this;
408
    }
409
410
    /**
411
     * Move an element to a position.
412
     *
413
     * @param  string  $id
414
     * @param  int|string  $position
415
     * @return static
416
     * @throws Exception
417
     */
418
    protected function toPosition(string $id, $position): self
419
    {
420
        $element = $this->find($id);
421
422
        return $this
423
            ->except($id)
424
            ->insert($element, $position);
425
    }
426
427
    /**
428
     * @param  string  $id
429
     * @throws Exception
430
     */
431
    protected function notFound(string $id)
432
    {
433
        throw new Exception(sprintf('Element [%s] does not exist.', $id));
434
    }
435 1
436
    /**
437 1
     * Create element object from string.
438
     *
439
     * @param $element
440 1
     * @return mixed
441 1
     */
442
    protected function createElement($element)
443
    {
444
        if (\is_string($element)) {
445
            $element = Text::make($element, $element);
446
        }
447
448
        return $element;
449 2
    }
450
}
451