1: <?php namespace Illuminate\Support;
2:
3: use Closure;
4: use Countable;
5: use ArrayAccess;
6: use ArrayIterator;
7: use CachingIterator;
8: use JsonSerializable;
9: use IteratorAggregate;
10: use Illuminate\Support\Contracts\JsonableInterface;
11: use Illuminate\Support\Contracts\ArrayableInterface;
12:
13: class Collection implements ArrayAccess, ArrayableInterface, Countable, IteratorAggregate, JsonableInterface, JsonSerializable {
14:
15: /**
16: * The items contained in the collection.
17: *
18: * @var array
19: */
20: protected $items = array();
21:
22: /**
23: * Create a new collection.
24: *
25: * @param array $items
26: * @return void
27: */
28: public function __construct(array $items = array())
29: {
30: $this->items = $items;
31: }
32:
33: /**
34: * Create a new collection instance if the value isn't one already.
35: *
36: * @param mixed $items
37: * @return \Illuminate\Support\Collection
38: */
39: public static function make($items)
40: {
41: if (is_null($items)) return new static;
42:
43: if ($items instanceof Collection) return $items;
44:
45: return new static(is_array($items) ? $items : array($items));
46: }
47:
48: /**
49: * Get all of the items in the collection.
50: *
51: * @return array
52: */
53: public function all()
54: {
55: return $this->items;
56: }
57:
58: /**
59: * Collapse the collection items into a single array.
60: *
61: * @return \Illuminate\Support\Collection
62: */
63: public function collapse()
64: {
65: $results = array();
66:
67: foreach ($this->items as $values)
68: {
69: $results = array_merge($results, $values);
70: }
71:
72: return new static($results);
73: }
74:
75: /**
76: * Diff the collection with the given items.
77: *
78: * @param \Illuminate\Support\Collection|\Illuminate\Support\Contracts\ArrayableInterface|array $items
79: * @return \Illuminate\Support\Collection
80: */
81: public function diff($items)
82: {
83: return new static(array_diff($this->items, $this->getArrayableItems($items)));
84: }
85:
86: /**
87: * Execute a callback over each item.
88: *
89: * @param Closure $callback
90: * @return \Illuminate\Support\Collection
91: */
92: public function each(Closure $callback)
93: {
94: array_map($callback, $this->items);
95:
96: return $this;
97: }
98:
99: /**
100: * Fetch a nested element of the collection.
101: *
102: * @param string $key
103: * @return \Illuminate\Support\Collection
104: */
105: public function fetch($key)
106: {
107: return new static(array_fetch($this->items, $key));
108: }
109:
110: /**
111: * Run a filter over each of the items.
112: *
113: * @param Closure $callback
114: * @return \Illuminate\Support\Collection
115: */
116: public function filter(Closure $callback)
117: {
118: return new static(array_filter($this->items, $callback));
119: }
120:
121: /**
122: * Get the first item from the collection.
123: *
124: * @param \Closure $callback
125: * @param mixed $default
126: * @return mixed|null
127: */
128: public function first(Closure $callback = null, $default = null)
129: {
130: if (is_null($callback))
131: {
132: return count($this->items) > 0 ? reset($this->items) : null;
133: }
134: else
135: {
136: return array_first($this->items, $callback, $default);
137: }
138: }
139:
140: /**
141: * Get a flattened array of the items in the collection.
142: *
143: * @return array
144: */
145: public function flatten()
146: {
147: return new static(array_flatten($this->items));
148: }
149:
150: /**
151: * Remove an item from the collection by key.
152: *
153: * @param mixed $key
154: * @return void
155: */
156: public function forget($key)
157: {
158: unset($this->items[$key]);
159: }
160:
161: /**
162: * Get an item from the collection by key.
163: *
164: * @param mixed $key
165: * @param mixed $default
166: * @return mixed
167: */
168: public function get($key, $default = null)
169: {
170: if (array_key_exists($key, $this->items))
171: {
172: return $this->items[$key];
173: }
174:
175: return value($default);
176: }
177:
178: /**
179: * Group an associative array by a field or Closure value.
180: *
181: * @param callable|string $groupBy
182: * @return \Illuminate\Support\Collection
183: */
184: public function groupBy($groupBy)
185: {
186: $results = array();
187:
188: foreach ($this->items as $key => $value)
189: {
190: $key = is_callable($groupBy) ? $groupBy($value, $key) : data_get($value, $groupBy);
191:
192: $results[$key][] = $value;
193: }
194:
195: return new static($results);
196: }
197:
198: /**
199: * Determine if an item exists in the collection by key.
200: *
201: * @param mixed $key
202: * @return bool
203: */
204: public function has($key)
205: {
206: return array_key_exists($key, $this->items);
207: }
208:
209: /**
210: * Concatenate values of a given key as a string.
211: *
212: * @param string $value
213: * @param string $glue
214: * @return string
215: */
216: public function implode($value, $glue = null)
217: {
218: if (is_null($glue)) return implode($this->lists($value));
219:
220: return implode($glue, $this->lists($value));
221: }
222:
223: /**
224: * Intersect the collection with the given items.
225: *
226: * @param \Illuminate\Support\Collection|\Illuminate\Support\Contracts\ArrayableInterface|array $items
227: * @return \Illuminate\Support\Collection
228: */
229: public function intersect($items)
230: {
231: return new static(array_intersect($this->items, $this->getArrayableItems($items)));
232: }
233:
234: /**
235: * Determine if the collection is empty or not.
236: *
237: * @return bool
238: */
239: public function isEmpty()
240: {
241: return empty($this->items);
242: }
243:
244: /**
245: * Get the last item from the collection.
246: *
247: * @return mixed|null
248: */
249: public function last()
250: {
251: return count($this->items) > 0 ? end($this->items) : null;
252: }
253:
254: /**
255: * Get an array with the values of a given key.
256: *
257: * @param string $value
258: * @param string $key
259: * @return array
260: */
261: public function lists($value, $key = null)
262: {
263: return array_pluck($this->items, $value, $key);
264: }
265:
266: /**
267: * Run a map over each of the items.
268: *
269: * @param Closure $callback
270: * @return \Illuminate\Support\Collection
271: */
272: public function map(Closure $callback)
273: {
274: return new static(array_map($callback, $this->items, array_keys($this->items)));
275: }
276:
277: /**
278: * Merge the collection with the given items.
279: *
280: * @param \Illuminate\Support\Collection|\Illuminate\Support\Contracts\ArrayableInterface|array $items
281: * @return \Illuminate\Support\Collection
282: */
283: public function merge($items)
284: {
285: return new static(array_merge($this->items, $this->getArrayableItems($items)));
286: }
287:
288: /**
289: * Get and remove the last item from the collection.
290: *
291: * @return mixed|null
292: */
293: public function pop()
294: {
295: return array_pop($this->items);
296: }
297:
298: /**
299: * Push an item onto the beginning of the collection.
300: *
301: * @param mixed $value
302: * @return void
303: */
304: public function prepend($value)
305: {
306: array_unshift($this->items, $value);
307: }
308:
309: /**
310: * Push an item onto the end of the collection.
311: *
312: * @param mixed $value
313: * @return void
314: */
315: public function push($value)
316: {
317: $this->items[] = $value;
318: }
319:
320: /**
321: * Pulls an item from the collection.
322: *
323: * @param mixed $key
324: * @param mixed $default
325: * @return mixed
326: */
327: public function pull($key, $default = null)
328: {
329: return array_pull($this->items, $key, $default);
330: }
331:
332: /**
333: * Put an item in the collection by key.
334: *
335: * @param mixed $key
336: * @param mixed $value
337: * @return void
338: */
339: public function put($key, $value)
340: {
341: $this->items[$key] = $value;
342: }
343:
344: /**
345: * Get one or more items randomly from the collection.
346: *
347: * @param int $amount
348: * @return mixed
349: */
350: public function random($amount = 1)
351: {
352: $keys = array_rand($this->items, $amount);
353:
354: return is_array($keys) ? array_intersect_key($this->items, array_flip($keys)) : $this->items[$keys];
355: }
356:
357: /**
358: * Reduce the collection to a single value.
359: *
360: * @param callable $callback
361: * @param mixed $initial
362: * @return mixed
363: */
364: public function reduce($callback, $initial = null)
365: {
366: return array_reduce($this->items, $callback, $initial);
367: }
368:
369: /**
370: * Create a colleciton of all elements that do not pass a given truth test.
371: *
372: * @param \Closure|mixed $callback
373: * @return \Illuminate\Support\Collection
374: */
375: public function reject($callback)
376: {
377: $results = [];
378:
379: foreach ($this->items as $key => $value)
380: {
381: if ($callback instanceof Closure)
382: {
383: if ( ! $callback($value))
384: {
385: $results[$key] = $value;
386: }
387: }
388: elseif ($callback != $value)
389: {
390: $results[$key] = $value;
391: }
392: }
393:
394: return new static($results);
395: }
396:
397: /**
398: * Reverse items order.
399: *
400: * @return \Illuminate\Support\Collection
401: */
402: public function reverse()
403: {
404: return new static(array_reverse($this->items));
405: }
406:
407: /**
408: * Search the collection for a given value and return the corresponding key if successful.
409: *
410: * @param mixed $value
411: * @param bool $strict
412: * @return mixed
413: */
414: public function search($value, $strict = false)
415: {
416: return array_search($value, $this->items, $strict);
417: }
418:
419: /**
420: * Get and remove the first item from the collection.
421: *
422: * @return mixed|null
423: */
424: public function shift()
425: {
426: return array_shift($this->items);
427: }
428:
429: /**
430: * Slice the underlying collection array.
431: *
432: * @param int $offset
433: * @param int $length
434: * @param bool $preserveKeys
435: * @return \Illuminate\Support\Collection
436: */
437: public function slice($offset, $length = null, $preserveKeys = false)
438: {
439: return new static(array_slice($this->items, $offset, $length, $preserveKeys));
440: }
441:
442: /**
443: * Chunk the underlying collection array.
444: *
445: * @param int $size
446: * @param bool $preserveKeys
447: * @return \Illuminate\Support\Collection
448: */
449: public function chunk($size, $preserveKeys = false)
450: {
451: $chunks = new static;
452:
453: foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk)
454: {
455: $chunks->push(new static($chunk));
456: }
457:
458: return $chunks;
459: }
460:
461: /**
462: * Sort through each item with a callback.
463: *
464: * @param Closure $callback
465: * @return \Illuminate\Support\Collection
466: */
467: public function sort(Closure $callback)
468: {
469: uasort($this->items, $callback);
470:
471: return $this;
472: }
473:
474: /**
475: * Sort the collection using the given Closure.
476: *
477: * @param \Closure|string $callback
478: * @param int $options
479: * @param bool $descending
480: * @return \Illuminate\Support\Collection
481: */
482: public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
483: {
484: $results = array();
485:
486: if (is_string($callback)) $callback =
487: $this->valueRetriever($callback);
488:
489: // First we will loop through the items and get the comparator from a callback
490: // function which we were given. Then, we will sort the returned values and
491: // and grab the corresponding values for the sorted keys from this array.
492: foreach ($this->items as $key => $value)
493: {
494: $results[$key] = $callback($value);
495: }
496:
497: $descending ? arsort($results, $options)
498: : asort($results, $options);
499:
500: // Once we have sorted all of the keys in the array, we will loop through them
501: // and grab the corresponding model so we can set the underlying items list
502: // to the sorted version. Then we'll just return the collection instance.
503: foreach (array_keys($results) as $key)
504: {
505: $results[$key] = $this->items[$key];
506: }
507:
508: $this->items = $results;
509:
510: return $this;
511: }
512:
513: /**
514: * Sort the collection in descending order using the given Closure.
515: *
516: * @param \Closure|string $callback
517: * @param int $options
518: * @return \Illuminate\Support\Collection
519: */
520: public function sortByDesc($callback, $options = SORT_REGULAR)
521: {
522: return $this->sortBy($callback, $options, true);
523: }
524:
525: /**
526: * Splice portion of the underlying collection array.
527: *
528: * @param int $offset
529: * @param int $length
530: * @param mixed $replacement
531: * @return \Illuminate\Support\Collection
532: */
533: public function splice($offset, $length = 0, $replacement = array())
534: {
535: return new static(array_splice($this->items, $offset, $length, $replacement));
536: }
537:
538: /**
539: * Get the sum of the given values.
540: *
541: * @param \Closure $callback
542: * @param string $callback
543: * @return mixed
544: */
545: public function sum($callback)
546: {
547: if (is_string($callback))
548: {
549: $callback = $this->valueRetriever($callback);
550: }
551:
552: return $this->reduce(function($result, $item) use ($callback)
553: {
554: return $result += $callback($item);
555:
556: }, 0);
557: }
558:
559: /**
560: * Take the first or last {$limit} items.
561: *
562: * @param int $limit
563: * @return \Illuminate\Support\Collection
564: */
565: public function take($limit = null)
566: {
567: if ($limit < 0) return $this->slice($limit, abs($limit));
568:
569: return $this->slice(0, $limit);
570: }
571:
572: /**
573: * Transform each item in the collection using a callback.
574: *
575: * @param Closure $callback
576: * @return \Illuminate\Support\Collection
577: */
578: public function transform(Closure $callback)
579: {
580: $this->items = array_map($callback, $this->items);
581:
582: return $this;
583: }
584:
585: /**
586: * Return only unique items from the collection array.
587: *
588: * @return \Illuminate\Support\Collection
589: */
590: public function unique()
591: {
592: return new static(array_unique($this->items));
593: }
594:
595: /**
596: * Reset the keys on the underlying array.
597: *
598: * @return \Illuminate\Support\Collection
599: */
600: public function values()
601: {
602: $this->items = array_values($this->items);
603:
604: return $this;
605: }
606:
607: /**
608: * Get a value retrieving callback.
609: *
610: * @param string $value
611: * @return \Closure
612: */
613: protected function valueRetriever($value)
614: {
615: return function($item) use ($value)
616: {
617: return data_get($item, $value);
618: };
619: }
620:
621: /**
622: * Get the collection of items as a plain array.
623: *
624: * @return array
625: */
626: public function toArray()
627: {
628: return array_map(function($value)
629: {
630: return $value instanceof ArrayableInterface ? $value->toArray() : $value;
631:
632: }, $this->items);
633: }
634:
635: /**
636: * Convert the object into something JSON serializable.
637: *
638: * @return array
639: */
640: public function jsonSerialize()
641: {
642: return $this->toArray();
643: }
644:
645: /**
646: * Get the collection of items as JSON.
647: *
648: * @param int $options
649: * @return string
650: */
651: public function toJson($options = 0)
652: {
653: return json_encode($this->toArray(), $options);
654: }
655:
656: /**
657: * Get an iterator for the items.
658: *
659: * @return ArrayIterator
660: */
661: public function getIterator()
662: {
663: return new ArrayIterator($this->items);
664: }
665:
666: /**
667: * Get a CachingIterator instance.
668: *
669: * @return \CachingIterator
670: */
671: public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
672: {
673: return new CachingIterator($this->getIterator(), $flags);
674: }
675:
676: /**
677: * Count the number of items in the collection.
678: *
679: * @return int
680: */
681: public function count()
682: {
683: return count($this->items);
684: }
685:
686: /**
687: * Determine if an item exists at an offset.
688: *
689: * @param mixed $key
690: * @return bool
691: */
692: public function offsetExists($key)
693: {
694: return array_key_exists($key, $this->items);
695: }
696:
697: /**
698: * Get an item at a given offset.
699: *
700: * @param mixed $key
701: * @return mixed
702: */
703: public function offsetGet($key)
704: {
705: return $this->items[$key];
706: }
707:
708: /**
709: * Set the item at a given offset.
710: *
711: * @param mixed $key
712: * @param mixed $value
713: * @return void
714: */
715: public function offsetSet($key, $value)
716: {
717: if (is_null($key))
718: {
719: $this->items[] = $value;
720: }
721: else
722: {
723: $this->items[$key] = $value;
724: }
725: }
726:
727: /**
728: * Unset the item at a given offset.
729: *
730: * @param string $key
731: * @return void
732: */
733: public function offsetUnset($key)
734: {
735: unset($this->items[$key]);
736: }
737:
738: /**
739: * Convert the collection to its string representation.
740: *
741: * @return string
742: */
743: public function __toString()
744: {
745: return $this->toJson();
746: }
747:
748: /**
749: * Results array of items from Collection or ArrayableInterface.
750: *
751: * @param \Illuminate\Support\Collection|\Illuminate\Support\Contracts\ArrayableInterface|array $items
752: * @return array
753: */
754: protected function getArrayableItems($items)
755: {
756: if ($items instanceof Collection)
757: {
758: $items = $items->all();
759: }
760: elseif ($items instanceof ArrayableInterface)
761: {
762: $items = $items->toArray();
763: }
764:
765: return $items;
766: }
767:
768: }
769: