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, ',')) { |
|
|
|
|
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:')) { |
|
|
|
|
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
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.