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