PluginProbe
PostNL for WooCommerce / 4.4.1
PostNL for WooCommerce v4.4.1
5.9.12 5.9.11 5.9.10 5.9.9 5.9.8 5.9.7 5.9.6 trunk 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.3.2 4.3.3 4.4.0 4.4.1 All 72 releases
woo-postnl / vendor / myparcelnl / sdk / src / Support / Collection.php

Collection.php in PostNL for WooCommerce 4.4.1, at vendor/myparcelnl/sdk/src/Support/Collection.php

1,895 lines 44.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types=1);
2
3 namespace MyParcelNL\Sdk\src\Support;
4
5 use stdClass;
6 use Countable;
7 use Exception;
8 use ArrayAccess;
9 use Traversable;
10 use ArrayIterator;
11 use CachingIterator;
12 use IteratorAggregate;
13
14 /**
15 * @property-read HigherOrderCollectionProxy $average
16 * @property-read HigherOrderCollectionProxy $avg
17 * @property-read HigherOrderCollectionProxy $contains
18 * @property-read HigherOrderCollectionProxy $each
19 * @property-read HigherOrderCollectionProxy $every
20 * @property-read HigherOrderCollectionProxy $filter
21 * @property-read HigherOrderCollectionProxy $first
22 * @property-read HigherOrderCollectionProxy $flatMap
23 * @property-read HigherOrderCollectionProxy $groupBy
24 * @property-read HigherOrderCollectionProxy $keyBy
25 * @property-read HigherOrderCollectionProxy $map
26 * @property-read HigherOrderCollectionProxy $max
27 * @property-read HigherOrderCollectionProxy $min
28 * @property-read HigherOrderCollectionProxy $partition
29 * @property-read HigherOrderCollectionProxy $reject
30 * @property-read HigherOrderCollectionProxy $sortBy
31 * @property-read HigherOrderCollectionProxy $sortByDesc
32 * @property-read HigherOrderCollectionProxy $sum
33 * @property-read HigherOrderCollectionProxy $unique
34 *
35 * Class Collection
36 * @see https://laravel.com/docs/7.x
37 */
38 class Collection implements ArrayAccess, Countable, IteratorAggregate
39 {
40 /**
41 * @var Helpers
42 */
43 private $helper;
44
45 /**
46 * The items contained in the collection.
47 *
48 * @var array
49 */
50 protected $items = [];
51
52 /**
53 * The methods that can be proxied.
54 *
55 * @var array
56 */
57 protected static $proxies = [
58 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first',
59 'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition',
60 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique',
61 ];
62
63 /**
64 * Create a new collection.
65 *
66 * @param mixed $items
67 * @return void
68 */
69 public function __construct($items = [])
70 {
71 $this->items = $this->getArrayableItems($items);
72 $this->helper = new Helpers();
73 }
74
75 /**
76 * Create a new collection instance if the value isn't one already.
77 *
78 * @param mixed $items
79 * @return static
80 */
81 public static function make($items = [])
82 {
83 return new static($items);
84 }
85
86 /**
87 * Wrap the given value in a collection if applicable.
88 *
89 * @param mixed $value
90 * @return static
91 */
92 public static function wrap($value)
93 {
94 return $value instanceof self
95 ? new static($value)
96 : new static(Arr::wrap($value));
97 }
98
99 /**
100 * Get the underlying items from the given collection if applicable.
101 *
102 * @param array|static $value
103 * @return array
104 */
105 public static function unwrap($value)
106 {
107 return $value instanceof self ? $value->all() : $value;
108 }
109
110 /**
111 * Create a new collection by invoking the callback a given amount of times.
112 *
113 * @param int $number
114 * @param callable $callback
115 * @return static
116 */
117 public static function times($number, callable $callback = null)
118 {
119 if ($number < 1) {
120 return new static;
121 }
122
123 if (is_null($callback)) {
124 return new static(range(1, $number));
125 }
126
127 return (new static(range(1, $number)))->map($callback);
128 }
129
130 /**
131 * Get all of the items in the collection.
132 *
133 * @return array
134 */
135 public function all()
136 {
137 return $this->items;
138 }
139
140 /**
141 * Get the average value of a given key.
142 *
143 * @param callable|string|null $callback
144 * @return mixed
145 */
146 public function avg($callback = null)
147 {
148 if ($count = $this->count()) {
149 return $this->sum($callback) / $count;
150 }
151 }
152
153 /**
154 * Alias for the "avg" method.
155 *
156 * @param callable|string|null $callback
157 * @return mixed
158 */
159 public function average($callback = null)
160 {
161 return $this->avg($callback);
162 }
163
164 /**
165 * Get the median of a given key.
166 *
167 * @param null $key
168 * @return mixed
169 */
170 public function median($key = null)
171 {
172 $count = $this->count();
173
174 if ($count == 0) {
175 return;
176 }
177
178 $values = (isset($key) ? $this->pluck($key) : $this);
179 $values->sort()->values();
180
181 $middle = (int) ($count / 2);
182
183 if ($count % 2) {
184 return $values->get($middle);
185 }
186
187 return (new static([
188 $values->get($middle - 1), $values->get($middle),
189 ]))->average();
190 }
191
192 /**
193 * Get the mode of a given key.
194 *
195 * @param mixed $key
196 * @return array|null|void
197 */
198 public function mode($key = null)
199 {
200 $count = $this->count();
201
202 if ($count == 0) {
203 return;
204 }
205
206 $collection = isset($key) ? $this->pluck($key) : $this;
207
208 $counts = new self;
209
210 $collection->each(function ($value) use ($counts) {
211 $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
212 });
213
214 $sorted = $counts->sort();
215
216 $highestValue = $sorted->last();
217
218 return $sorted->filter(function ($value) use ($highestValue) {
219 return $value == $highestValue;
220 })->sort()->keys()->all();
221 }
222
223 /**
224 * Collapse the collection of items into a single array.
225 *
226 * @return static
227 */
228 public function collapse()
229 {
230 return new static(Arr::collapse($this->items));
231 }
232
233 /**
234 * Determine if an item exists in the collection.
235 *
236 * @param mixed $key
237 * @param mixed $operator
238 * @param mixed $value
239 * @return bool
240 */
241 public function contains($key, $operator = null, $value = null)
242 {
243 if (func_num_args() === 1) {
244 if ($this->useAsCallable($key)) {
245 $placeholder = new stdClass;
246
247 return $this->first($key, $placeholder) !== $placeholder;
248 }
249
250 return in_array($key, $this->items);
251 }
252
253 return $this->contains($this->operatorForWhere(...func_get_args()));
254 }
255
256 /**
257 * Determine if an item exists in the collection using strict comparison.
258 *
259 * @param mixed $key
260 * @param mixed $value
261 * @return bool
262 */
263 public function containsStrict($key, $value = null)
264 {
265 if (func_num_args() === 2) {
266 return $this->contains(function ($item) use ($key, $value) {
267 return $this->helper->data_get($item, $key) === $value;
268 });
269 }
270
271 if ($this->useAsCallable($key)) {
272 return ! is_null($this->first($key));
273 }
274
275 return in_array($key, $this->items, true);
276 }
277
278 /**
279 * Cross join with the given lists, returning all possible permutations.
280 *
281 * @param mixed ...$lists
282 * @return static
283 */
284 public function crossJoin(...$lists)
285 {
286 return new static(Arr::crossJoin(
287 $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
288 ));
289 }
290
291 /**
292 * Dump the collection and end the script.
293 *
294 * @param array $args
295 * @return void
296 */
297 public function dd(...$args)
298 {
299 call_user_func_array([$this, 'dump'], $args);
300
301 die(1);
302 }
303
304 /**
305 * Dump the collection.
306 *
307 * @return $this
308 */
309 public function dump()
310 {
311 (new static(func_get_args()))
312 ->push($this)
313 ->each(function ($item) {
314 var_dump($item);
315 });
316
317 return $this;
318 }
319
320 /**
321 * Get the items in the collection that are not present in the given items.
322 *
323 * @param mixed $items
324 * @return static
325 */
326 public function diff($items)
327 {
328 return new static(array_diff($this->items, $this->getArrayableItems($items)));
329 }
330
331 /**
332 * Get the items in the collection that are not present in the given items.
333 *
334 * @param mixed $items
335 * @param callable $callback
336 * @return static
337 */
338 public function diffUsing($items, callable $callback)
339 {
340 return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
341 }
342
343 /**
344 * Get the items in the collection whose keys and values are not present in the given items.
345 *
346 * @param mixed $items
347 * @return static
348 */
349 public function diffAssoc($items)
350 {
351 return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
352 }
353
354 /**
355 * Get the items in the collection whose keys and values are not present in the given items.
356 *
357 * @param mixed $items
358 * @param callable $callback
359 * @return static
360 */
361 public function diffAssocUsing($items, callable $callback)
362 {
363 return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
364 }
365
366 /**
367 * Get the items in the collection whose keys are not present in the given items.
368 *
369 * @param mixed $items
370 * @return static
371 */
372 public function diffKeys($items)
373 {
374 return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
375 }
376
377 /**
378 * Get the items in the collection whose keys are not present in the given items.
379 *
380 * @param mixed $items
381 * @param callable $callback
382 * @return static
383 */
384 public function diffKeysUsing($items, callable $callback)
385 {
386 return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
387 }
388
389 /**
390 * Execute a callback over each item.
391 *
392 * @param callable $callback
393 * @return $this
394 */
395 public function each(callable $callback)
396 {
397 foreach ($this->items as $key => $item) {
398 if ($callback($item, $key) === false) {
399 break;
400 }
401 }
402
403 return $this;
404 }
405
406 /**
407 * Execute a callback over each nested chunk of items.
408 *
409 * @param callable $callback
410 * @return static
411 */
412 public function eachSpread(callable $callback)
413 {
414 return $this->each(function ($chunk, $key) use ($callback) {
415 $chunk[] = $key;
416
417 return $callback(...$chunk);
418 });
419 }
420
421 /**
422 * Determine if all items in the collection pass the given test.
423 *
424 * @param string|callable $key
425 * @param mixed $operator
426 * @param mixed $value
427 * @return bool
428 */
429 public function every($key, $operator = null, $value = null)
430 {
431 if (func_num_args() === 1) {
432 $callback = $this->valueRetriever($key);
433
434 foreach ($this->items as $k => $v) {
435 if (! $callback($v, $k)) {
436 return false;
437 }
438 }
439
440 return true;
441 }
442
443 return $this->every($this->operatorForWhere(...func_get_args()));
444 }
445
446 /**
447 * Get all items except for those with the specified keys.
448 *
449 * @param Collection|mixed $keys
450 * @return static
451 */
452 public function except($keys)
453 {
454 if ($keys instanceof self) {
455 $keys = $keys->all();
456 } elseif (! is_array($keys)) {
457 $keys = func_get_args();
458 }
459
460 return new static(Arr::except($this->items, $keys));
461 }
462
463 /**
464 * Run a filter over each of the items.
465 *
466 * @param callable|null $callback
467 * @return static
468 */
469 public function filter(callable $callback = null)
470 {
471 if ($callback) {
472 return new static(Arr::where($this->items, $callback));
473 }
474
475 return new static(array_filter($this->items));
476 }
477
478 /**
479 * Apply the callback if the value is truthy.
480 *
481 * @param bool|mixed $value
482 * @param callable|null $callback
483 * @param callable|null $default
484 * @return static|mixed
485 */
486 public function when($value, callable $callback = null, callable $default = null)
487 {
488 if (! $callback) {
489 return new HigherOrderWhenProxy($this, $value);
490 }
491
492 if ($value) {
493 return $callback($this, $value);
494 } elseif ($default) {
495 return $default($this, $value);
496 }
497
498 return $this;
499 }
500
501 /**
502 * Apply the callback if the value is falsy.
503 *
504 * @param bool $value
505 * @param callable $callback
506 * @param callable $default
507 * @return mixed
508 */
509 public function unless($value, callable $callback, callable $default = null)
510 {
511 return $this->when(! $value, $callback, $default);
512 }
513
514 /**
515 * Filter items by the given key value pair.
516 *
517 * @param string $key
518 * @param mixed $operator
519 * @param mixed $value
520 * @return static
521 */
522 public function where($key, $operator = null, $value = null)
523 {
524 return $this->filter($this->operatorForWhere(...func_get_args()));
525 }
526
527 /**
528 * Get an operator checker callback.
529 *
530 * @param string $key
531 * @param string $operator
532 * @param mixed $value
533 * @return \Closure
534 */
535 protected function operatorForWhere($key, $operator = null, $value = null)
536 {
537 if (func_num_args() === 2) {
538 $value = $operator;
539
540 $operator = '=';
541 }
542
543 return function ($item) use ($key, $operator, $value) {
544 $retrieved = $this->helper->data_get($item, $key);
545
546 $strings = array_filter([$retrieved, $value], function ($value) {
547 return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
548 });
549
550 if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
551 return in_array($operator, ['!=', '<>', '!==']);
552 }
553
554 switch ($operator) {
555 default:
556 case '=':
557 case '==': return $retrieved == $value;
558 case '!=':
559 case '<>': return $retrieved != $value;
560 case '<': return $retrieved < $value;
561 case '>': return $retrieved > $value;
562 case '<=': return $retrieved <= $value;
563 case '>=': return $retrieved >= $value;
564 case '===': return $retrieved === $value;
565 case '!==': return $retrieved !== $value;
566 }
567 };
568 }
569
570 /**
571 * Filter items by the given key value pair using strict comparison.
572 *
573 * @param string $key
574 * @param mixed $value
575 * @return static
576 */
577 public function whereStrict($key, $value)
578 {
579 return $this->where($key, '===', $value);
580 }
581
582 /**
583 * Filter items by the given key value pair.
584 *
585 * @param string $key
586 * @param mixed $values
587 * @param bool $strict
588 * @return static
589 */
590 public function whereIn($key, $values, $strict = false)
591 {
592 $values = $this->getArrayableItems($values);
593
594 return $this->filter(function ($item) use ($key, $values, $strict) {
595 return in_array($this->helper->data_get($item, $key), $values, $strict);
596 });
597 }
598
599 /**
600 * Filter items by the given key value pair using strict comparison.
601 *
602 * @param string $key
603 * @param mixed $values
604 * @return static
605 */
606 public function whereInStrict($key, $values)
607 {
608 return $this->whereIn($key, $values, true);
609 }
610
611 /**
612 * Filter items by the given key value pair.
613 *
614 * @param string $key
615 * @param mixed $values
616 * @param bool $strict
617 * @return static
618 */
619 public function whereNotIn($key, $values, $strict = false)
620 {
621 $values = $this->getArrayableItems($values);
622
623 return $this->reject(function ($item) use ($key, $values, $strict) {
624 return in_array($this->helper->data_get($item, $key), $values, $strict);
625 });
626 }
627
628 /**
629 * Filter items by the given key value pair using strict comparison.
630 *
631 * @param string $key
632 * @param mixed $values
633 * @return static
634 */
635 public function whereNotInStrict($key, $values)
636 {
637 return $this->whereNotIn($key, $values, true);
638 }
639
640 /**
641 * Filter the items, removing any items that don't match the given type.
642 *
643 * @param string $type
644 * @return static
645 */
646 public function whereInstanceOf($type)
647 {
648 return $this->filter(function ($value) use ($type) {
649 return $value instanceof $type;
650 });
651 }
652
653 /**
654 * Get the first item from the collection.
655 *
656 * @param callable|null $callback
657 * @param mixed $default
658 * @return mixed
659 */
660 public function first(callable $callback = null, $default = null)
661 {
662 return Arr::first($this->items, $callback, $default);
663 }
664
665 /**
666 * Get the first item by the given key value pair.
667 *
668 * @param string $key
669 * @param mixed $operator
670 * @param mixed $value
671 * @return static
672 */
673 public function firstWhere($key, $operator = null, $value = null)
674 {
675 return $this->first($this->operatorForWhere(...func_get_args()));
676 }
677
678 /**
679 * Get a flattened array of the items in the collection.
680 *
681 * @param int $depth
682 * @return static
683 */
684 public function flatten($depth = INF)
685 {
686 return new static(Arr::flatten($this->items, $depth));
687 }
688
689 /**
690 * Flip the items in the collection.
691 *
692 * @return static
693 */
694 public function flip()
695 {
696 return new static(array_flip($this->items));
697 }
698
699 /**
700 * Remove an item from the collection by key.
701 *
702 * @param string|array $keys
703 * @return $this
704 */
705 public function forget($keys)
706 {
707 foreach ((array) $keys as $key) {
708 $this->offsetUnset($key);
709 }
710
711 return $this;
712 }
713
714 /**
715 * Get an item from the collection by key.
716 *
717 * @param mixed $key
718 * @param mixed $default
719 * @return mixed
720 */
721 public function get($key, $default = null)
722 {
723 if ($this->offsetExists($key)) {
724 return $this->items[$key];
725 }
726
727 return $this->helper->value($default);
728 }
729
730 /**
731 * Group an associative array by a field or using a callback.
732 *
733 * @param callable|string $groupBy
734 * @param bool $preserveKeys
735 * @return static
736 */
737 public function groupBy($groupBy, $preserveKeys = false)
738 {
739 if (is_array($groupBy)) {
740 $nextGroups = $groupBy;
741
742 $groupBy = array_shift($nextGroups);
743 }
744
745 $groupBy = $this->valueRetriever($groupBy);
746
747 $results = [];
748
749 foreach ($this->items as $key => $value) {
750 $groupKeys = $groupBy($value, $key);
751
752 if (! is_array($groupKeys)) {
753 $groupKeys = [$groupKeys];
754 }
755
756 foreach ($groupKeys as $groupKey) {
757 $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
758
759 if (! array_key_exists($groupKey, $results)) {
760 $results[$groupKey] = new static;
761 }
762
763 $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
764 }
765 }
766
767 $result = new static($results);
768
769 if (! empty($nextGroups)) {
770 return $result->map->groupBy($nextGroups, $preserveKeys);
771 }
772
773 return $result;
774 }
775
776 /**
777 * Key an associative array by a field or using a callback.
778 *
779 * @param callable|string $keyBy
780 * @return static
781 */
782 public function keyBy($keyBy)
783 {
784 $keyBy = $this->valueRetriever($keyBy);
785
786 $results = [];
787
788 foreach ($this->items as $key => $item) {
789 $resolvedKey = $keyBy($item, $key);
790
791 if (is_object($resolvedKey)) {
792 $resolvedKey = (string) $resolvedKey;
793 }
794
795 $results[$resolvedKey] = $item;
796 }
797
798 return new static($results);
799 }
800
801 /**
802 * Determine if an item exists in the collection by key.
803 *
804 * @param mixed $key
805 * @return bool
806 */
807 public function has($key)
808 {
809 $keys = is_array($key) ? $key : func_get_args();
810
811 foreach ($keys as $value) {
812 if (! $this->offsetExists($value)) {
813 return false;
814 }
815 }
816
817 return true;
818 }
819
820 /**
821 * Concatenate values of a given key as a string.
822 *
823 * @param string $value
824 * @param string $glue
825 * @return string
826 */
827 public function implode($value, $glue = null)
828 {
829 $first = $this->first();
830
831 if (is_array($first) || is_object($first)) {
832 return implode($glue, $this->pluck($value)->all());
833 }
834
835 return implode($value, $this->items);
836 }
837
838 /**
839 * Intersect the collection with the given items.
840 *
841 * @param mixed $items
842 * @return static
843 */
844 public function intersect($items)
845 {
846 return new static(array_intersect($this->items, $this->getArrayableItems($items)));
847 }
848
849 /**
850 * Intersect the collection with the given items by key.
851 *
852 * @param mixed $items
853 * @return static
854 */
855 public function intersectByKeys($items)
856 {
857 return new static(array_intersect_key(
858 $this->items, $this->getArrayableItems($items)
859 ));
860 }
861
862 /**
863 * Determine if the collection is empty or not.
864 *
865 * @return bool
866 */
867 public function isEmpty()
868 {
869 return empty($this->items);
870 }
871
872 /**
873 * Determine if the collection is not empty.
874 *
875 * @return bool
876 */
877 public function isNotEmpty()
878 {
879 return ! $this->isEmpty();
880 }
881
882 /**
883 * Determine if the given value is callable, but not a string.
884 *
885 * @param mixed $value
886 * @return bool
887 */
888 protected function useAsCallable($value)
889 {
890 return ! is_string($value) && is_callable($value);
891 }
892
893 /**
894 * Get the keys of the collection items.
895 *
896 * @return static
897 */
898 public function keys()
899 {
900 return new static(array_keys($this->items));
901 }
902
903 /**
904 * Get the last item from the collection.
905 *
906 * @param callable|null $callback
907 * @param mixed $default
908 * @return mixed
909 */
910 public function last(callable $callback = null, $default = null)
911 {
912 return Arr::last($this->items, $callback, $default);
913 }
914
915 /**
916 * Get the values of a given key.
917 *
918 * @param string|array $value
919 * @param string|null $key
920 * @return static
921 */
922 public function pluck($value, $key = null)
923 {
924 return new static(Arr::pluck($this->items, $value, $key));
925 }
926
927 /**
928 * Run a map over each of the items.
929 *
930 * @param callable $callback
931 * @return static
932 */
933 public function map(callable $callback)
934 {
935 $keys = array_keys($this->items);
936
937 $items = array_map($callback, $this->items, $keys);
938
939 return new static(array_combine($keys, $items));
940 }
941
942 /**
943 * Run a map over each nested chunk of items.
944 *
945 * @param callable $callback
946 * @return static
947 */
948 public function mapSpread(callable $callback)
949 {
950 return $this->map(function ($chunk, $key) use ($callback) {
951 $chunk[] = $key;
952
953 return $callback(...$chunk);
954 });
955 }
956
957 /**
958 * Run a dictionary map over the items.
959 *
960 * The callback should return an associative array with a single key/value pair.
961 *
962 * @param callable $callback
963 * @return static
964 */
965 public function mapToDictionary(callable $callback)
966 {
967 $dictionary = [];
968
969 foreach ($this->items as $key => $item) {
970 $pair = $callback($item, $key);
971
972 $key = key($pair);
973
974 $value = reset($pair);
975
976 if (! isset($dictionary[$key])) {
977 $dictionary[$key] = [];
978 }
979
980 $dictionary[$key][] = $value;
981 }
982
983 return new static($dictionary);
984 }
985
986 /**
987 * Run a grouping map over the items.
988 *
989 * The callback should return an associative array with a single key/value pair.
990 *
991 * @param callable $callback
992 * @return static
993 */
994 public function mapToGroups(callable $callback)
995 {
996 $groups = $this->mapToDictionary($callback);
997
998 return $groups->map([$this, 'make']);
999 }
1000
1001 /**
1002 * Run an associative map over each of the items.
1003 *
1004 * The callback should return an associative array with a single key/value pair.
1005 *
1006 * @param callable $callback
1007 * @return static
1008 */
1009 public function mapWithKeys(callable $callback)
1010 {
1011 $result = [];
1012
1013 foreach ($this->items as $key => $value) {
1014 $assoc = $callback($value, $key);
1015
1016 foreach ($assoc as $mapKey => $mapValue) {
1017 $result[$mapKey] = $mapValue;
1018 }
1019 }
1020
1021 return new static($result);
1022 }
1023
1024 /**
1025 * Map a collection and flatten the result by a single level.
1026 *
1027 * @param callable $callback
1028 * @return static
1029 */
1030 public function flatMap(callable $callback)
1031 {
1032 return $this->map($callback)->collapse();
1033 }
1034
1035 /**
1036 * Map the values into a new class.
1037 *
1038 * @param string $class
1039 * @return static
1040 */
1041 public function mapInto($class)
1042 {
1043 return $this->map(function ($value, $key) use ($class) {
1044 return new $class($value, $key);
1045 });
1046 }
1047
1048 /**
1049 * Get the max value of a given key.
1050 *
1051 * @param callable|string|null $callback
1052 * @return mixed
1053 */
1054 public function max($callback = null)
1055 {
1056 $callback = $this->valueRetriever($callback);
1057
1058 return $this->filter(function ($value) {
1059 return ! is_null($value);
1060 })->reduce(function ($result, $item) use ($callback) {
1061 $value = $callback($item);
1062
1063 return is_null($result) || $value > $result ? $value : $result;
1064 });
1065 }
1066
1067 /**
1068 * Merge the collection with the given items.
1069 *
1070 * @param mixed $items
1071 * @return static
1072 */
1073 public function merge($items)
1074 {
1075 return new static(array_merge($this->items, $this->getArrayableItems($items)));
1076 }
1077
1078 /**
1079 * Create a collection by using this collection for keys and another for its values.
1080 *
1081 * @param mixed $values
1082 * @return static
1083 */
1084 public function combine($values)
1085 {
1086 return new static(array_combine($this->all(), $this->getArrayableItems($values)));
1087 }
1088
1089 /**
1090 * Union the collection with the given items.
1091 *
1092 * @param mixed $items
1093 * @return static
1094 */
1095 public function union($items)
1096 {
1097 return new static($this->items + $this->getArrayableItems($items));
1098 }
1099
1100 /**
1101 * Get the min value of a given key.
1102 *
1103 * @param callable|string|null $callback
1104 * @return mixed
1105 */
1106 public function min($callback = null)
1107 {
1108 $callback = $this->valueRetriever($callback);
1109
1110 return $this->filter(function ($value) {
1111 return ! is_null($value);
1112 })->reduce(function ($result, $item) use ($callback) {
1113 $value = $callback($item);
1114
1115 return is_null($result) || $value < $result ? $value : $result;
1116 });
1117 }
1118
1119 /**
1120 * Create a new collection consisting of every n-th element.
1121 *
1122 * @param int $step
1123 * @param int $offset
1124 * @return static
1125 */
1126 public function nth($step, $offset = 0)
1127 {
1128 $new = [];
1129
1130 $position = 0;
1131
1132 foreach ($this->items as $item) {
1133 if ($position % $step === $offset) {
1134 $new[] = $item;
1135 }
1136
1137 $position++;
1138 }
1139
1140 return new static($new);
1141 }
1142
1143 /**
1144 * Get the items with the specified keys.
1145 *
1146 * @param mixed $keys
1147 * @return static
1148 */
1149 public function only($keys)
1150 {
1151 if (is_null($keys)) {
1152 return new static($this->items);
1153 }
1154
1155 if ($keys instanceof self) {
1156 $keys = $keys->all();
1157 }
1158
1159 $keys = is_array($keys) ? $keys : func_get_args();
1160
1161 return new static(Arr::only($this->items, $keys));
1162 }
1163
1164 /**
1165 * "Paginate" the collection by slicing it into a smaller collection.
1166 *
1167 * @param int $page
1168 * @param int $perPage
1169 * @return static
1170 */
1171 public function forPage($page, $perPage)
1172 {
1173 $offset = max(0, ($page - 1) * $perPage);
1174
1175 return $this->slice($offset, $perPage);
1176 }
1177
1178 /**
1179 * Partition the collection into two arrays using the given callback or key.
1180 *
1181 * @param callable|string $key
1182 * @param mixed $operator
1183 * @param mixed $value
1184 * @return static
1185 */
1186 public function partition($key, $operator = null, $value = null)
1187 {
1188 $partitions = [new static, new static];
1189
1190 $callback = func_num_args() === 1
1191 ? $this->valueRetriever($key)
1192 : $this->operatorForWhere(...func_get_args());
1193
1194 foreach ($this->items as $key => $item) {
1195 $partitions[(int) ! $callback($item, $key)][$key] = $item;
1196 }
1197
1198 return new static($partitions);
1199 }
1200
1201 /**
1202 * Pass the collection to the given callback and return the result.
1203 *
1204 * @param callable $callback
1205 * @return mixed
1206 */
1207 public function pipe(callable $callback)
1208 {
1209 return $callback($this);
1210 }
1211
1212 /**
1213 * Get and remove the last item from the collection.
1214 *
1215 * @return mixed
1216 */
1217 public function pop()
1218 {
1219 return array_pop($this->items);
1220 }
1221
1222 /**
1223 * Push an item onto the beginning of the collection.
1224 *
1225 * @param mixed $value
1226 * @param mixed $key
1227 * @return $this
1228 */
1229 public function prepend($value, $key = null)
1230 {
1231 $this->items = Arr::prepend($this->items, $value, $key);
1232
1233 return $this;
1234 }
1235
1236 /**
1237 * Push an item onto the end of the collection.
1238 *
1239 * @param mixed $values [optional]
1240 * @return $this
1241 */
1242 public function push(...$values)
1243 {
1244 foreach ($values as $value) {
1245 $this->items[] = $value;
1246 }
1247
1248 return $this;
1249 }
1250
1251 /**
1252 * Push all of the given items onto the collection.
1253 *
1254 * @param \Traversable|array $source
1255 * @return $this
1256 */
1257 public function concat($source)
1258 {
1259 $result = new static($this);
1260
1261 foreach ($source as $item) {
1262 $result->push($item);
1263 }
1264
1265 return $result;
1266 }
1267
1268 /**
1269 * Get and remove an item from the collection.
1270 *
1271 * @param mixed $key
1272 * @param mixed $default
1273 * @return mixed
1274 */
1275 public function pull($key, $default = null)
1276 {
1277 return Arr::pull($this->items, $key, $default);
1278 }
1279
1280 /**
1281 * Put an item in the collection by key.
1282 *
1283 * @param mixed $key
1284 * @param mixed $value
1285 * @return $this
1286 */
1287 public function put($key, $value)
1288 {
1289 $this->offsetSet($key, $value);
1290
1291 return $this;
1292 }
1293
1294 /**
1295 * Get one or a specified number of items randomly from the collection.
1296 *
1297 * @param int|null $number
1298 * @return mixed
1299 *
1300 * @throws \InvalidArgumentException
1301 */
1302 public function random($number = null)
1303 {
1304 if (is_null($number)) {
1305 return Arr::random($this->items);
1306 }
1307
1308 return new static(Arr::random($this->items, $number));
1309 }
1310
1311 /**
1312 * Reduce the collection to a single value.
1313 *
1314 * @param callable $callback
1315 * @param mixed $initial
1316 * @return mixed
1317 */
1318 public function reduce(callable $callback, $initial = null)
1319 {
1320 return array_reduce($this->items, $callback, $initial);
1321 }
1322
1323 /**
1324 * Create a collection of all elements that do not pass a given truth test.
1325 *
1326 * @param callable|mixed $callback
1327 * @return static
1328 */
1329 public function reject($callback = null)
1330 {
1331 if ($this->useAsCallable($callback)) {
1332 return $this->filter(function ($value, $key) use ($callback) {
1333 return ! $callback($value, $key);
1334 });
1335 }
1336
1337 return $this->filter(function ($item) use ($callback) {
1338 return $item != $callback;
1339 });
1340 }
1341
1342 /**
1343 * Reverse items order.
1344 *
1345 * @return static
1346 */
1347 public function reverse()
1348 {
1349 return new static(array_reverse($this->items, true));
1350 }
1351
1352 /**
1353 * Search the collection for a given value and return the corresponding key if successful.
1354 *
1355 * @param mixed $value
1356 * @param bool $strict
1357 * @return mixed
1358 */
1359 public function search($value, $strict = false)
1360 {
1361 if (! $this->useAsCallable($value)) {
1362 return array_search($value, $this->items, $strict);
1363 }
1364
1365 foreach ($this->items as $key => $item) {
1366 if (call_user_func($value, $item, $key)) {
1367 return $key;
1368 }
1369 }
1370
1371 return false;
1372 }
1373
1374 /**
1375 * Get and remove the first item from the collection.
1376 *
1377 * @return mixed
1378 */
1379 public function shift()
1380 {
1381 return array_shift($this->items);
1382 }
1383
1384 /**
1385 * Shuffle the items in the collection.
1386 *
1387 * @param int $seed
1388 * @return static
1389 */
1390 public function shuffle($seed = null)
1391 {
1392 return new static(Arr::shuffle($this->items, $seed));
1393 }
1394
1395 /**
1396 * Slice the underlying collection array.
1397 *
1398 * @param int $offset
1399 * @param int $length
1400 * @return static
1401 */
1402 public function slice($offset, $length = null)
1403 {
1404 return new static(array_slice($this->items, $offset, $length, true));
1405 }
1406
1407 /**
1408 * Split a collection into a certain number of groups.
1409 *
1410 * @param int $numberOfGroups
1411 * @return static
1412 */
1413 public function split($numberOfGroups)
1414 {
1415 if ($this->isEmpty()) {
1416 return new static;
1417 }
1418
1419 $groupSize = ceil($this->count() / $numberOfGroups);
1420
1421 return $this->chunk($groupSize);
1422 }
1423
1424 /**
1425 * Chunk the underlying collection array.
1426 *
1427 * @param int $size
1428 * @return static
1429 */
1430 public function chunk($size)
1431 {
1432 if ($size <= 0) {
1433 return new static;
1434 }
1435
1436 $chunks = [];
1437
1438 foreach (array_chunk($this->items, $size, true) as $chunk) {
1439 $chunks[] = new static($chunk);
1440 }
1441
1442 return new static($chunks);
1443 }
1444
1445 /**
1446 * Sort through each item with a callback.
1447 *
1448 * @param callable|null $callback
1449 * @return static
1450 */
1451 public function sort($callback = null)
1452 {
1453 $items = $this->items;
1454
1455 $callback
1456 ? uasort($items, $callback)
1457 : asort($items);
1458
1459 return new static($items);
1460 }
1461
1462 /**
1463 * Sort the collection using the given callback.
1464 *
1465 * @param callable|string $callback
1466 * @param int $options
1467 * @param bool $descending
1468 * @return static
1469 */
1470 public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
1471 {
1472 $results = [];
1473
1474 $callback = $this->valueRetriever($callback);
1475
1476 // First we will loop through the items and get the comparator from a callback
1477 // function which we were given. Then, we will sort the returned values and
1478 // and grab the corresponding values for the sorted keys from this array.
1479 foreach ($this->items as $key => $value) {
1480 $results[$key] = $callback($value, $key);
1481 }
1482
1483 $descending ? arsort($results, $options)
1484 : asort($results, $options);
1485
1486 // Once we have sorted all of the keys in the array, we will loop through them
1487 // and grab the corresponding model so we can set the underlying items list
1488 // to the sorted version. Then we'll just return the collection instance.
1489 foreach (array_keys($results) as $key) {
1490 $results[$key] = $this->items[$key];
1491 }
1492
1493 return new static($results);
1494 }
1495
1496 /**
1497 * Sort the collection in descending order using the given callback.
1498 *
1499 * @param callable|string $callback
1500 * @param int $options
1501 * @return static
1502 */
1503 public function sortByDesc($callback, $options = SORT_REGULAR)
1504 {
1505 return $this->sortBy($callback, $options, true);
1506 }
1507
1508 /**
1509 * Sort the collection keys.
1510 *
1511 * @param int $options
1512 * @param bool $descending
1513 * @return static
1514 */
1515 public function sortKeys($options = SORT_REGULAR, $descending = false)
1516 {
1517 $items = $this->items;
1518
1519 $descending ? krsort($items, $options) : ksort($items, $options);
1520
1521 return new static($items);
1522 }
1523
1524 /**
1525 * Sort the collection keys in descending order.
1526 *
1527 * @param int $options
1528 * @return static
1529 */
1530 public function sortKeysDesc($options = SORT_REGULAR)
1531 {
1532 return $this->sortKeys($options, true);
1533 }
1534
1535 /**
1536 * Splice a portion of the underlying collection array.
1537 *
1538 * @param int $offset
1539 * @param int|null $length
1540 * @param mixed $replacement
1541 * @return static
1542 */
1543 public function splice($offset, $length = null, $replacement = [])
1544 {
1545 if (func_num_args() === 1) {
1546 return new static(array_splice($this->items, $offset));
1547 }
1548
1549 return new static(array_splice($this->items, $offset, $length, $replacement));
1550 }
1551
1552 /**
1553 * Get the sum of the given values.
1554 *
1555 * @param callable|string|null $callback
1556 * @return mixed
1557 */
1558 public function sum($callback = null)
1559 {
1560 if (is_null($callback)) {
1561 return array_sum($this->items);
1562 }
1563
1564 $callback = $this->valueRetriever($callback);
1565
1566 return $this->reduce(function ($result, $item) use ($callback) {
1567 return $result + $callback($item);
1568 }, 0);
1569 }
1570
1571 /**
1572 * Take the first or last {$limit} items.
1573 *
1574 * @param int $limit
1575 * @return static
1576 */
1577 public function take($limit)
1578 {
1579 if ($limit < 0) {
1580 return $this->slice($limit, abs($limit));
1581 }
1582
1583 return $this->slice(0, $limit);
1584 }
1585
1586 /**
1587 * Pass the collection to the given callback and then return it.
1588 *
1589 * @param callable $callback
1590 * @return $this
1591 */
1592 public function tap(callable $callback)
1593 {
1594 $callback(new static($this->items));
1595
1596 return $this;
1597 }
1598
1599 /**
1600 * Transform each item in the collection using a callback.
1601 *
1602 * @param callable $callback
1603 * @return $this
1604 */
1605 public function transform(callable $callback)
1606 {
1607 $this->items = $this->map($callback)->all();
1608
1609 return $this;
1610 }
1611
1612 /**
1613 * Return only unique items from the collection array.
1614 *
1615 * @param string|callable|null $key
1616 * @param bool $strict
1617 * @return static
1618 */
1619 public function unique($key = null, $strict = false)
1620 {
1621 $callback = $this->valueRetriever($key);
1622
1623 $exists = [];
1624
1625 return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
1626 if (in_array($id = $callback($item, $key), $exists, $strict)) {
1627 return true;
1628 }
1629
1630 $exists[] = $id;
1631 });
1632 }
1633
1634 /**
1635 * Return only unique items from the collection array using strict comparison.
1636 *
1637 * @param string|callable|null $key
1638 * @return static
1639 */
1640 public function uniqueStrict($key = null)
1641 {
1642 return $this->unique($key, true);
1643 }
1644
1645 /**
1646 * Reset the keys on the underlying array.
1647 *
1648 * @return static
1649 */
1650 public function values()
1651 {
1652 return new static(array_values($this->items));
1653 }
1654
1655 /**
1656 * Get a value retrieving callback.
1657 *
1658 * @param string $value
1659 * @return callable
1660 */
1661 protected function valueRetriever($value)
1662 {
1663 if ($this->useAsCallable($value)) {
1664 return $value;
1665 }
1666
1667 return function ($item) use ($value) {
1668 return $this->helper->data_get($item, $value);
1669 };
1670 }
1671
1672 /**
1673 * Zip the collection together with one or more arrays.
1674 *
1675 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1676 * => [[1, 4], [2, 5], [3, 6]]
1677 *
1678 * @param mixed ...$items
1679 * @return static
1680 */
1681 public function zip($items)
1682 {
1683 $arrayableItems = array_map(function ($items) {
1684 return $this->getArrayableItems($items);
1685 }, func_get_args());
1686
1687 $params = array_merge([function () {
1688 return new static(func_get_args());
1689 }, $this->items], $arrayableItems);
1690
1691 return new static(call_user_func_array('array_map', $params));
1692 }
1693
1694 /**
1695 * Pad collection to the specified length with a value.
1696 *
1697 * @param int $size
1698 * @param mixed $value
1699 * @return static
1700 */
1701 public function pad($size, $value)
1702 {
1703 return new static(array_pad($this->items, $size, $value));
1704 }
1705
1706 /**
1707 * Get the collection of items as a plain array.
1708 *
1709 * @return array
1710 */
1711 public function toArray()
1712 {
1713 return array_map(function ($value) {
1714 if (method_exists($value, 'toArray')) {
1715 return $value->toArray();
1716 }
1717 return $value;
1718 }, $this->items);
1719 }
1720
1721 /**
1722 * Convert the object into something JSON serializable.
1723 *
1724 * @return array
1725 */
1726 public function jsonSerialize()
1727 {
1728 return array_map(function ($value) {
1729 if (method_exists($value, 'toArray')) {
1730 return $value->toArray();
1731 }
1732
1733 return $value;
1734 }, $this->items);
1735 }
1736
1737 /**
1738 * Get the collection of items as JSON.
1739 *
1740 * @param int $options
1741 * @return string
1742 */
1743 public function toJson($options = 0)
1744 {
1745 return json_encode($this->jsonSerialize(), $options);
1746 }
1747
1748 /**
1749 * Get an iterator for the items.
1750 *
1751 * @return \ArrayIterator
1752 */
1753 public function getIterator()
1754 {
1755 return new ArrayIterator($this->items);
1756 }
1757
1758 /**
1759 * Get a CachingIterator instance.
1760 *
1761 * @param int $flags
1762 * @return \CachingIterator
1763 */
1764 public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1765 {
1766 return new CachingIterator($this->getIterator(), $flags);
1767 }
1768
1769 /**
1770 * Count the number of items in the collection.
1771 *
1772 * @return int
1773 */
1774 public function count()
1775 {
1776 return count($this->items);
1777 }
1778
1779 /**
1780 * Get a base Support collection instance from this collection.
1781 *
1782 * @return Collection
1783 */
1784 public function toBase()
1785 {
1786 return new self($this);
1787 }
1788
1789 /**
1790 * Determine if an item exists at an offset.
1791 *
1792 * @param mixed $key
1793 * @return bool
1794 */
1795 public function offsetExists($key)
1796 {
1797 return array_key_exists($key, $this->items);
1798 }
1799
1800 /**
1801 * Get an item at a given offset.
1802 *
1803 * @param mixed $key
1804 * @return mixed
1805 */
1806 public function offsetGet($key)
1807 {
1808 return $this->items[$key];
1809 }
1810
1811 /**
1812 * Set the item at a given offset.
1813 *
1814 * @param mixed $key
1815 * @param mixed $value
1816 * @return void
1817 */
1818 public function offsetSet($key, $value)
1819 {
1820 if (is_null($key)) {
1821 $this->items[] = $value;
1822 } else {
1823 $this->items[$key] = $value;
1824 }
1825 }
1826
1827 /**
1828 * Unset the item at a given offset.
1829 *
1830 * @param string $key
1831 * @return void
1832 */
1833 public function offsetUnset($key)
1834 {
1835 unset($this->items[$key]);
1836 }
1837
1838 /**
1839 * Convert the collection to its string representation.
1840 *
1841 * @return string
1842 */
1843 public function __toString()
1844 {
1845 return $this->toJson();
1846 }
1847
1848 /**
1849 * Results array of items from Collection or Arrayable.
1850 *
1851 * @param mixed $items
1852 * @return array
1853 */
1854 protected function getArrayableItems($items)
1855 {
1856 if (is_array($items)) {
1857 return $items;
1858 } elseif ($items instanceof self) {
1859 return $items->all();
1860 } elseif ($items instanceof Traversable) {
1861 return iterator_to_array($items);
1862 }
1863
1864 return (array) $items;
1865 }
1866
1867 /**
1868 * Add a method to the list of proxied methods.
1869 *
1870 * @param string $method
1871 * @return void
1872 */
1873 public static function proxy($method)
1874 {
1875 static::$proxies[] = $method;
1876 }
1877
1878 /**
1879 * Dynamically access collection proxies.
1880 *
1881 * @param string $key
1882 * @return mixed
1883 *
1884 * @throws \Exception
1885 */
1886 public function __get($key)
1887 {
1888 if (! in_array($key, static::$proxies)) {
1889 throw new Exception("Property [{$key}] does not exist on this collection instance.");
1890 }
1891
1892 return new HigherOrderCollectionProxy($this, $key);
1893 }
1894 }
1895