PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.30
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.30
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Database / Orm / Collection.php

Collection.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.30, at vendor/wpfluent/framework/src/WPFluent/Database/Orm/Collection.php

663 lines 16.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\Framework\Database\Orm;
4
5 use LogicException;
6 use FluentBoards\Framework\Support\Arr;
7 use FluentBoards\Framework\Support\Str;
8 use FluentBoards\Framework\Support\ArrayableInterface;
9 use FluentBoards\Framework\Support\Collection as BaseCollection;
10
11 class Collection extends BaseCollection
12 {
13 /**
14 * Find a model in the collection by key.
15 *
16 * @param mixed $key
17 * @param mixed $default
18 * @return \FluentBoards\Framework\Database\Orm\Model|static|null
19 */
20 public function find($key, $default = null)
21 {
22 if ($key instanceof Model) {
23 $key = $key->getKey();
24 }
25
26 if ($key instanceof ArrayableInterface) {
27 $key = $key->toArray();
28 }
29
30 if (is_array($key)) {
31 if ($this->isEmpty()) {
32 return new static;
33 }
34
35 return $this->whereIn($this->first()->getKeyName(), $key);
36 }
37
38 return Arr::first($this->items, function ($model) use ($key) {
39 return $model->getKey() == $key;
40 }, $default);
41 }
42
43 /**
44 * Load a set of relationships onto the collection.
45 *
46 * @param array|string $relations
47 * @return $this
48 */
49 public function load($relations)
50 {
51 if ($this->isNotEmpty()) {
52 if (is_string($relations)) {
53 $relations = func_get_args();
54 }
55
56 $query = $this->first()->newQueryWithoutRelationships()->with($relations);
57
58 $this->items = $query->eagerLoadRelations($this->items);
59 }
60
61 return $this;
62 }
63
64 /**
65 * Load a set of aggregations over relationship's column onto the collection.
66 *
67 * @param array|string $relations
68 * @param string $column
69 * @param string $function
70 * @return $this
71 */
72 public function loadAggregate($relations, $column, $function = null)
73 {
74 if ($this->isEmpty()) {
75 return $this;
76 }
77
78 $models = $this->first()->newModelQuery()
79 ->whereKey($this->modelKeys())
80 ->select($this->first()->getKeyName())
81 ->withAggregate($relations, $column, $function)
82 ->get()
83 ->keyBy($this->first()->getKeyName());
84
85 $attributes = Arr::except(
86 array_keys($models->first()->getAttributes()),
87 $models->first()->getKeyName()
88 );
89
90 $this->each(function ($model) use ($models, $attributes) {
91 $extraAttributes = Arr::only($models->get($model->getKey())->getAttributes(), $attributes);
92
93 $model->forceFill($extraAttributes)
94 ->syncOriginalAttributes($attributes)
95 ->mergeCasts($models->get($model->getKey())->getCasts());
96 });
97
98 return $this;
99 }
100
101 /**
102 * Load a set of relationship counts onto the collection.
103 *
104 * @param array|string $relations
105 * @return $this
106 */
107 public function loadCount($relations)
108 {
109 return $this->loadAggregate($relations, '*', 'count');
110 }
111
112 /**
113 * Load a set of relationship's max column values onto the collection.
114 *
115 * @param array|string $relations
116 * @param string $column
117 * @return $this
118 */
119 public function loadMax($relations, $column)
120 {
121 return $this->loadAggregate($relations, $column, 'max');
122 }
123
124 /**
125 * Load a set of relationship's min column values onto the collection.
126 *
127 * @param array|string $relations
128 * @param string $column
129 * @return $this
130 */
131 public function loadMin($relations, $column)
132 {
133 return $this->loadAggregate($relations, $column, 'min');
134 }
135
136 /**
137 * Load a set of relationship's column summations onto the collection.
138 *
139 * @param array|string $relations
140 * @param string $column
141 * @return $this
142 */
143 public function loadSum($relations, $column)
144 {
145 return $this->loadAggregate($relations, $column, 'sum');
146 }
147
148 /**
149 * Load a set of relationship's average column values onto the collection.
150 *
151 * @param array|string $relations
152 * @param string $column
153 * @return $this
154 */
155 public function loadAvg($relations, $column)
156 {
157 return $this->loadAggregate($relations, $column, 'avg');
158 }
159
160 /**
161 * Load a set of related existences onto the collection.
162 *
163 * @param array|string $relations
164 * @return $this
165 */
166 public function loadExists($relations)
167 {
168 return $this->loadAggregate($relations, '*', 'exists');
169 }
170
171 /**
172 * Load a set of relationships onto the collection if they are not already eager loaded.
173 *
174 * @param array|string $relations
175 * @return $this
176 */
177 public function loadMissing($relations)
178 {
179 if (is_string($relations)) {
180 $relations = func_get_args();
181 }
182
183 foreach ($relations as $key => $value) {
184 if (is_numeric($key)) {
185 $key = $value;
186 }
187
188 $segments = explode('.', explode(':', $key)[0]);
189
190 if (Str::contains($key, ':')) {
191 $segments[count($segments) - 1] .= ':'.explode(':', $key)[1];
192 }
193
194 $path = [];
195
196 foreach ($segments as $segment) {
197 $path[] = [$segment => $segment];
198 }
199
200 if (is_callable($value)) {
201 $path[count($segments) - 1][end($segments)] = $value;
202 }
203
204 $this->loadMissingRelation($this, $path);
205 }
206
207 return $this;
208 }
209
210 /**
211 * Load a relationship path if it is not already eager loaded.
212 *
213 * @param \FluentBoards\Framework\Database\Orm\Collection $models
214 * @param array $path
215 * @return void
216 */
217 protected function loadMissingRelation(self $models, array $path)
218 {
219 $relation = array_shift($path);
220
221 $name = explode(':', key($relation))[0];
222
223 if (is_string(reset($relation))) {
224 $relation = reset($relation);
225 }
226
227 $models->filter(function ($model) use ($name) {
228 return ! is_null($model) && ! $model->relationLoaded($name);
229 })->load($relation);
230
231 if (empty($path)) {
232 return;
233 }
234
235 $models = $models->pluck($name)->whereNotNull();
236
237 if ($models->first() instanceof BaseCollection) {
238 $models = $models->collapse();
239 }
240
241 $this->loadMissingRelation(new static($models), $path);
242 }
243
244 /**
245 * Load a set of relationships onto the mixed relationship collection.
246 *
247 * @param string $relation
248 * @param array $relations
249 * @return $this
250 */
251 public function loadMorph($relation, $relations)
252 {
253 $this->pluck($relation)
254 ->filter()
255 ->groupBy(function ($model) {
256 return get_class($model);
257 })
258 ->each(function ($models, $className) use ($relations) {
259 static::make($models)->load($relations[$className] ?? []);
260 });
261
262 return $this;
263 }
264
265 /**
266 * Load a set of relationship counts onto the mixed relationship collection.
267 *
268 * @param string $relation
269 * @param array $relations
270 * @return $this
271 */
272 public function loadMorphCount($relation, $relations)
273 {
274 $this->pluck($relation)
275 ->filter()
276 ->groupBy(function ($model) {
277 return get_class($model);
278 })
279 ->each(function ($models, $className) use ($relations) {
280 static::make($models)->loadCount($relations[$className] ?? []);
281 });
282
283 return $this;
284 }
285
286 /**
287 * Determine if a key exists in the collection.
288 *
289 * @param mixed $key
290 * @param mixed $operator
291 * @param mixed $value
292 * @return bool
293 */
294 public function contains($key, $operator = null, $value = null)
295 {
296 if (func_num_args() > 1 || $this->useAsCallable($key)) {
297 return parent::contains(...func_get_args());
298 }
299
300 if ($key instanceof Model) {
301 return parent::contains(function ($model) use ($key) {
302 return $model->is($key);
303 });
304 }
305
306 return parent::contains(function ($model) use ($key) {
307 return $model->getKey() == $key;
308 });
309 }
310
311 /**
312 * Get the array of primary keys.
313 *
314 * @return array
315 */
316 public function modelKeys()
317 {
318 return array_map(function ($model) {
319 return $model->getKey();
320 }, $this->items);
321 }
322
323 /**
324 * Merge the collection with the given items.
325 *
326 * @param \ArrayAccess|array $items
327 * @return static
328 */
329 public function merge($items)
330 {
331 $dictionary = $this->getDictionary();
332
333 foreach ($items as $item) {
334 $dictionary[$item->getKey()] = $item;
335 }
336
337 return new static(array_values($dictionary));
338 }
339
340 /**
341 * Run a map over each of the items.
342 *
343 * @param callable $callback
344 * @return \FluentBoards\Framework\Support\Collection|static
345 */
346 public function map(callable $callback)
347 {
348 $result = parent::map($callback);
349
350 return $result->contains(function ($item) {
351 return ! $item instanceof Model;
352 }) ? $result->toBase() : $result;
353 }
354
355 /**
356 * Run an associative map over each of the items.
357 *
358 * The callback should return an associative array with a single key / value pair.
359 *
360 * @param callable $callback
361 * @return \FluentBoards\Framework\Support\Collection|static
362 */
363 public function mapWithKeys(callable $callback)
364 {
365 $result = parent::mapWithKeys($callback);
366
367 return $result->contains(function ($item) {
368 return ! $item instanceof Model;
369 }) ? $result->toBase() : $result;
370 }
371
372 /**
373 * Reload a fresh model instance from the database for all the entities.
374 *
375 * @param array|string $with
376 * @return static
377 */
378 public function fresh($with = [])
379 {
380 if ($this->isEmpty()) {
381 return new static;
382 }
383
384 $model = $this->first();
385
386 $freshModels = $model->newQueryWithoutScopes()
387 ->with(is_string($with) ? func_get_args() : $with)
388 ->whereIn($model->getKeyName(), $this->modelKeys())
389 ->get()
390 ->getDictionary();
391
392 return $this->filter(function ($model) use ($freshModels) {
393 return $model->exists && isset($freshModels[$model->getKey()]);
394 })
395 ->map(function ($model) use ($freshModels) {
396 return $freshModels[$model->getKey()];
397 });
398 }
399
400 /**
401 * Diff the collection with the given items.
402 *
403 * @param \ArrayAccess|array $items
404 * @return static
405 */
406 public function diff($items)
407 {
408 $diff = new static;
409
410 $dictionary = $this->getDictionary($items);
411
412 foreach ($this->items as $item) {
413 if (! isset($dictionary[$item->getKey()])) {
414 $diff->add($item);
415 }
416 }
417
418 return $diff;
419 }
420
421 /**
422 * Intersect the collection with the given items.
423 *
424 * @param \ArrayAccess|array $items
425 * @return static
426 */
427 public function intersect($items)
428 {
429 $intersect = new static;
430
431 if (empty($items)) {
432 return $intersect;
433 }
434
435 $dictionary = $this->getDictionary($items);
436
437 foreach ($this->items as $item) {
438 if (isset($dictionary[$item->getKey()])) {
439 $intersect->add($item);
440 }
441 }
442
443 return $intersect;
444 }
445
446 /**
447 * Return only unique items from the collection.
448 *
449 * @param string|callable|null $key
450 * @param bool $strict
451 * @return static
452 */
453 public function unique($key = null, $strict = false)
454 {
455 if (! is_null($key)) {
456 return parent::unique($key, $strict);
457 }
458
459 return new static(array_values($this->getDictionary()));
460 }
461
462 /**
463 * Returns only the models from the collection with the specified keys.
464 *
465 * @param mixed $keys
466 * @return static
467 */
468 public function only($keys)
469 {
470 if (is_null($keys)) {
471 return new static($this->items);
472 }
473
474 $dictionary = Arr::only($this->getDictionary(), $keys);
475
476 return new static(array_values($dictionary));
477 }
478
479 /**
480 * Returns all models in the collection except the models with specified keys.
481 *
482 * @param mixed $keys
483 * @return static
484 */
485 public function except($keys)
486 {
487 $dictionary = Arr::except($this->getDictionary(), $keys);
488
489 return new static(array_values($dictionary));
490 }
491
492 /**
493 * Make the given, typically visible, attributes hidden across the entire collection.
494 *
495 * @param array|string $attributes
496 * @return $this
497 */
498 public function makeHidden($attributes)
499 {
500 return $this->each->makeHidden($attributes);
501 }
502
503 /**
504 * Make the given, typically hidden, attributes visible across the entire collection.
505 *
506 * @param array|string $attributes
507 * @return $this
508 */
509 public function makeVisible($attributes)
510 {
511 return $this->each->makeVisible($attributes);
512 }
513
514 /**
515 * Append an attribute across the entire collection.
516 *
517 * @param array|string $attributes
518 * @return $this
519 */
520 public function append($attributes)
521 {
522 return $this->each->append($attributes);
523 }
524
525 /**
526 * Get a dictionary keyed by primary keys.
527 *
528 * @param \ArrayAccess|array|null $items
529 * @return array
530 */
531 public function getDictionary($items = null)
532 {
533 $items = is_null($items) ? $this->items : $items;
534
535 $dictionary = [];
536
537 foreach ($items as $value) {
538 $dictionary[$value->getKey()] = $value;
539 }
540
541 return $dictionary;
542 }
543
544 /**
545 * The following methods are intercepted to always return base collections.
546 */
547
548 /**
549 * Get an array with the values of a given key.
550 *
551 * @param string|array $value
552 * @param string|null $key
553 * @return \FluentBoards\Framework\Support\Collection
554 */
555 public function pluck($value, $key = null)
556 {
557 return $this->toBase()->pluck($value, $key);
558 }
559
560 /**
561 * Get the keys of the collection items.
562 *
563 * @return \FluentBoards\Framework\Support\Collection
564 */
565 public function keys()
566 {
567 return $this->toBase()->keys();
568 }
569
570 /**
571 * Zip the collection together with one or more arrays.
572 *
573 * @param mixed ...$items
574 * @return \FluentBoards\Framework\Support\Collection
575 */
576 public function zip($items)
577 {
578 return $this->toBase()->zip(...func_get_args());
579 }
580
581 /**
582 * Collapse the collection of items into a single array.
583 *
584 * @return \FluentBoards\Framework\Support\Collection
585 */
586 public function collapse()
587 {
588 return $this->toBase()->collapse();
589 }
590
591 /**
592 * Get a flattened array of the items in the collection.
593 *
594 * @param int $depth
595 * @return \FluentBoards\Framework\Support\Collection
596 */
597 public function flatten($depth = INF)
598 {
599 return $this->toBase()->flatten($depth);
600 }
601
602 /**
603 * Flip the items in the collection.
604 *
605 * @return \FluentBoards\Framework\Support\Collection
606 */
607 public function flip()
608 {
609 return $this->toBase()->flip();
610 }
611
612 /**
613 * Pad collection to the specified length with a value.
614 *
615 * @param int $size
616 * @param mixed $value
617 * @return \FluentBoards\Framework\Support\Collection
618 */
619 public function pad($size, $value)
620 {
621 return $this->toBase()->pad($size, $value);
622 }
623
624 /**
625 * Get the comparison function to detect duplicates.
626 *
627 * @param bool $strict
628 * @return \Closure
629 */
630 protected function duplicateComparator($strict)
631 {
632 return function ($a, $b) {
633 return $a->is($b);
634 };
635 }
636
637 /**
638 * Get the Orm query builder from the collection.
639 *
640 * @return \FluentBoards\Framework\Database\Orm\Builder
641 *
642 * @throws \LogicException
643 */
644 public function toQuery()
645 {
646 $model = $this->first();
647
648 if (! $model) {
649 throw new LogicException('Unable to create query for empty collection.');
650 }
651
652 $class = get_class($model);
653
654 if ($this->filter(function ($model) use ($class) {
655 return ! $model instanceof $class;
656 })->isNotEmpty()) {
657 throw new LogicException('Unable to create query for collection with mixed types.');
658 }
659
660 return $model->newModelQuery()->whereKey($this->modelKeys());
661 }
662 }
663