CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.3 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.3
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
      • Session
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Constraint
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • Collection

Interfaces

  • CollectionInterface

Traits

  • CollectionTrait
  • ExtractTrait
  1: <?php
  2: /**
  3:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5:  *
  6:  * Licensed under The MIT License
  7:  * For full copyright and license information, please see the LICENSE.txt
  8:  * Redistributions of files must retain the above copyright notice.
  9:  *
 10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 11:  * @link          http://cakephp.org CakePHP(tm) Project
 12:  * @since         3.0.0
 13:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 14:  */
 15: namespace Cake\Collection;
 16: 
 17: use AppendIterator;
 18: use ArrayIterator;
 19: use Cake\Collection\Iterator\BufferedIterator;
 20: use Cake\Collection\Iterator\ExtractIterator;
 21: use Cake\Collection\Iterator\FilterIterator;
 22: use Cake\Collection\Iterator\InsertIterator;
 23: use Cake\Collection\Iterator\MapReduce;
 24: use Cake\Collection\Iterator\NestIterator;
 25: use Cake\Collection\Iterator\ReplaceIterator;
 26: use Cake\Collection\Iterator\SortIterator;
 27: use Cake\Collection\Iterator\StoppableIterator;
 28: use Cake\Collection\Iterator\TreeIterator;
 29: use Cake\Collection\Iterator\UnfoldIterator;
 30: use Cake\Collection\Iterator\ZipIterator;
 31: use Countable;
 32: use LimitIterator;
 33: use LogicException;
 34: use RecursiveIteratorIterator;
 35: use Traversable;
 36: 
 37: /**
 38:  * Offers a handful of method to manipulate iterators
 39:  */
 40: trait CollectionTrait
 41: {
 42: 
 43:     use ExtractTrait;
 44: 
 45:     /**
 46:      * {@inheritDoc}
 47:      *
 48:      */
 49:     public function each(callable $c)
 50:     {
 51:         foreach ($this->unwrap() as $k => $v) {
 52:             $c($v, $k);
 53:         }
 54: 
 55:         return $this;
 56:     }
 57: 
 58:     /**
 59:      * {@inheritDoc}
 60:      *
 61:      * @return \Cake\Collection\Iterator\FilterIterator
 62:      */
 63:     public function filter(callable $c = null)
 64:     {
 65:         if ($c === null) {
 66:             $c = function ($v) {
 67:                 return (bool)$v;
 68:             };
 69:         }
 70: 
 71:         return new FilterIterator($this->unwrap(), $c);
 72:     }
 73: 
 74:     /**
 75:      * {@inheritDoc}
 76:      *
 77:      * @return \Cake\Collection\Iterator\FilterIterator
 78:      */
 79:     public function reject(callable $c)
 80:     {
 81:         return new FilterIterator($this->unwrap(), function ($key, $value, $items) use ($c) {
 82:             return !$c($key, $value, $items);
 83:         });
 84:     }
 85: 
 86:     /**
 87:      * {@inheritDoc}
 88:      *
 89:      */
 90:     public function every(callable $c)
 91:     {
 92:         $return = false;
 93:         foreach ($this->unwrap() as $key => $value) {
 94:             $return = true;
 95:             if (!$c($value, $key)) {
 96:                 return false;
 97:             }
 98:         }
 99: 
100:         return $return;
101:     }
102: 
103:     /**
104:      * {@inheritDoc}
105:      *
106:      */
107:     public function some(callable $c)
108:     {
109:         foreach ($this->unwrap() as $key => $value) {
110:             if ($c($value, $key) === true) {
111:                 return true;
112:             }
113:         }
114: 
115:         return false;
116:     }
117: 
118:     /**
119:      * {@inheritDoc}
120:      *
121:      */
122:     public function contains($value)
123:     {
124:         foreach ($this->unwrap() as $v) {
125:             if ($value === $v) {
126:                 return true;
127:             }
128:         }
129: 
130:         return false;
131:     }
132: 
133:     /**
134:      * {@inheritDoc}
135:      *
136:      * @return \Cake\Collection\Iterator\ReplaceIterator
137:      */
138:     public function map(callable $c)
139:     {
140:         return new ReplaceIterator($this->unwrap(), $c);
141:     }
142: 
143:     /**
144:      * {@inheritDoc}
145:      *
146:      */
147:     public function reduce(callable $c, $zero = null)
148:     {
149:         $isFirst = false;
150:         if (func_num_args() < 2) {
151:             $isFirst = true;
152:         }
153: 
154:         $result = $zero;
155:         foreach ($this->unwrap() as $k => $value) {
156:             if ($isFirst) {
157:                 $result = $value;
158:                 $isFirst = false;
159:                 continue;
160:             }
161:             $result = $c($result, $value, $k);
162:         }
163: 
164:         return $result;
165:     }
166: 
167:     /**
168:      * {@inheritDoc}
169:      *
170:      */
171:     public function extract($matcher)
172:     {
173:         $extractor = new ExtractIterator($this->unwrap(), $matcher);
174:         if (is_string($matcher) && strpos($matcher, '{*}') !== false) {
175:             $extractor = $extractor
176:                 ->filter(function ($data) {
177:                     return $data !== null && ($data instanceof Traversable || is_array($data));
178:                 })
179:                 ->unfold();
180:         }
181: 
182:         return $extractor;
183:     }
184: 
185:     /**
186:      * {@inheritDoc}
187:      *
188:      */
189:     public function max($callback, $type = SORT_NUMERIC)
190:     {
191:         return (new SortIterator($this->unwrap(), $callback, SORT_DESC, $type))->first();
192:     }
193: 
194:     /**
195:      * {@inheritDoc}
196:      *
197:      */
198:     public function min($callback, $type = SORT_NUMERIC)
199:     {
200:         return (new SortIterator($this->unwrap(), $callback, SORT_ASC, $type))->first();
201:     }
202: 
203:     /**
204:      * {@inheritDoc}
205:      *
206:      */
207:     public function sortBy($callback, $dir = SORT_DESC, $type = SORT_NUMERIC)
208:     {
209:         return new SortIterator($this->unwrap(), $callback, $dir, $type);
210:     }
211: 
212:     /**
213:      * {@inheritDoc}
214:      *
215:      */
216:     public function groupBy($callback)
217:     {
218:         $callback = $this->_propertyExtractor($callback);
219:         $group = [];
220:         foreach ($this as $value) {
221:             $group[$callback($value)][] = $value;
222:         }
223: 
224:         return new Collection($group);
225:     }
226: 
227:     /**
228:      * {@inheritDoc}
229:      *
230:      */
231:     public function indexBy($callback)
232:     {
233:         $callback = $this->_propertyExtractor($callback);
234:         $group = [];
235:         foreach ($this as $value) {
236:             $group[$callback($value)] = $value;
237:         }
238: 
239:         return new Collection($group);
240:     }
241: 
242:     /**
243:      * {@inheritDoc}
244:      *
245:      */
246:     public function countBy($callback)
247:     {
248:         $callback = $this->_propertyExtractor($callback);
249: 
250:         $mapper = function ($value, $key, $mr) use ($callback) {
251:             $mr->emitIntermediate($value, $callback($value));
252:         };
253: 
254:         $reducer = function ($values, $key, $mr) {
255:             $mr->emit(count($values), $key);
256:         };
257: 
258:         return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer));
259:     }
260: 
261:     /**
262:      * {@inheritDoc}
263:      *
264:      */
265:     public function sumOf($matcher = null)
266:     {
267:         if ($matcher === null) {
268:             return array_sum($this->toList());
269:         }
270: 
271:         $callback = $this->_propertyExtractor($matcher);
272:         $sum = 0;
273:         foreach ($this as $k => $v) {
274:             $sum += $callback($v, $k);
275:         }
276: 
277:         return $sum;
278:     }
279: 
280:     /**
281:      * {@inheritDoc}
282:      *
283:      */
284:     public function shuffle()
285:     {
286:         $elements = $this->toArray();
287:         shuffle($elements);
288: 
289:         return new Collection($elements);
290:     }
291: 
292:     /**
293:      * {@inheritDoc}
294:      *
295:      */
296:     public function sample($size = 10)
297:     {
298:         return new Collection(new LimitIterator($this->shuffle(), 0, $size));
299:     }
300: 
301:     /**
302:      * {@inheritDoc}
303:      *
304:      */
305:     public function take($size = 1, $from = 0)
306:     {
307:         return new Collection(new LimitIterator($this->unwrap(), $from, $size));
308:     }
309: 
310:     /**
311:      * {@inheritDoc}
312:      *
313:      */
314:     public function skip($howMany)
315:     {
316:         return new Collection(new LimitIterator($this->unwrap(), $howMany));
317:     }
318: 
319:     /**
320:      * {@inheritDoc}
321:      *
322:      */
323:     public function match(array $conditions)
324:     {
325:         return $this->filter($this->_createMatcherFilter($conditions));
326:     }
327: 
328:     /**
329:      * {@inheritDoc}
330:      *
331:      */
332:     public function firstMatch(array $conditions)
333:     {
334:         return $this->match($conditions)->first();
335:     }
336: 
337:     /**
338:      * {@inheritDoc}
339:      *
340:      */
341:     public function first()
342:     {
343:         foreach ($this->take(1) as $result) {
344:             return $result;
345:         }
346:     }
347: 
348:     /**
349:      * {@inheritDoc}
350:      *
351:      */
352:     public function last()
353:     {
354:         $iterator = $this->unwrap();
355:         $count = $iterator instanceof Countable ?
356:             count($iterator) :
357:             iterator_count($iterator);
358: 
359:         if ($count === 0) {
360:             return null;
361:         }
362: 
363:         foreach ($this->take(1, $count - 1) as $last) {
364:             return $last;
365:         }
366:     }
367: 
368:     /**
369:      * {@inheritDoc}
370:      *
371:      */
372:     public function append($items)
373:     {
374:         $list = new AppendIterator();
375:         $list->append($this->unwrap());
376:         $list->append((new Collection($items))->unwrap());
377: 
378:         return new Collection($list);
379:     }
380: 
381:     /**
382:      * {@inheritDoc}
383:      *
384:      */
385:     public function combine($keyPath, $valuePath, $groupPath = null)
386:     {
387:         $options = [
388:             'keyPath' => $this->_propertyExtractor($keyPath),
389:             'valuePath' => $this->_propertyExtractor($valuePath),
390:             'groupPath' => $groupPath ? $this->_propertyExtractor($groupPath) : null
391:         ];
392: 
393:         $mapper = function ($value, $key, $mapReduce) use ($options) {
394:             $rowKey = $options['keyPath'];
395:             $rowVal = $options['valuePath'];
396: 
397:             if (!($options['groupPath'])) {
398:                 $mapReduce->emit($rowVal($value, $key), $rowKey($value, $key));
399: 
400:                 return null;
401:             }
402: 
403:             $key = $options['groupPath']($value, $key);
404:             $mapReduce->emitIntermediate(
405:                 [$rowKey($value, $key) => $rowVal($value, $key)],
406:                 $key
407:             );
408:         };
409: 
410:         $reducer = function ($values, $key, $mapReduce) {
411:             $result = [];
412:             foreach ($values as $value) {
413:                 $result += $value;
414:             }
415:             $mapReduce->emit($result, $key);
416:         };
417: 
418:         return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer));
419:     }
420: 
421:     /**
422:      * {@inheritDoc}
423:      *
424:      */
425:     public function nest($idPath, $parentPath, $nestingKey = 'children')
426:     {
427:         $parents = [];
428:         $idPath = $this->_propertyExtractor($idPath);
429:         $parentPath = $this->_propertyExtractor($parentPath);
430:         $isObject = true;
431: 
432:         $mapper = function ($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath, $nestingKey) {
433:             $row[$nestingKey] = [];
434:             $id = $idPath($row, $key);
435:             $parentId = $parentPath($row, $key);
436:             $parents[$id] =& $row;
437:             $mapReduce->emitIntermediate($id, $parentId);
438:         };
439: 
440:         $reducer = function ($values, $key, $mapReduce) use (&$parents, &$isObject, $nestingKey) {
441:             static $foundOutType = false;
442:             if (!$foundOutType) {
443:                 $isObject = is_object(current($parents));
444:                 $foundOutType = true;
445:             }
446:             if (empty($key) || !isset($parents[$key])) {
447:                 foreach ($values as $id) {
448:                     $parents[$id] = $isObject ? $parents[$id] : new ArrayIterator($parents[$id], 1);
449:                     $mapReduce->emit($parents[$id]);
450:                 }
451: 
452:                 return null;
453:             }
454: 
455:             $children = [];
456:             foreach ($values as $id) {
457:                 $children[] =& $parents[$id];
458:             }
459:             $parents[$key][$nestingKey] = $children;
460:         };
461: 
462:         return (new Collection(new MapReduce($this->unwrap(), $mapper, $reducer)))
463:             ->map(function ($value) use (&$isObject) {
464:                 return $isObject ? $value : $value->getArrayCopy();
465:             });
466:     }
467: 
468:     /**
469:      * {@inheritDoc}
470:      *
471:      * @return \Cake\Collection\Iterator\InsertIterator
472:      */
473:     public function insert($path, $values)
474:     {
475:         return new InsertIterator($this->unwrap(), $path, $values);
476:     }
477: 
478:     /**
479:      * {@inheritDoc}
480:      *
481:      */
482:     public function toArray($preserveKeys = true)
483:     {
484:         $iterator = $this->unwrap();
485:         if ($iterator instanceof ArrayIterator) {
486:             $items = $iterator->getArrayCopy();
487: 
488:             return $preserveKeys ? $items : array_values($items);
489:         }
490:         // RecursiveIteratorIterator can return duplicate key values causing
491:         // data loss when converted into an array
492:         if ($preserveKeys && get_class($iterator) === 'RecursiveIteratorIterator') {
493:             $preserveKeys = false;
494:         }
495: 
496:         return iterator_to_array($this, $preserveKeys);
497:     }
498: 
499:     /**
500:      * {@inheritDoc}
501:      *
502:      */
503:     public function toList()
504:     {
505:         return $this->toArray(false);
506:     }
507: 
508:     /**
509:      * {@inheritDoc}
510:      *
511:      */
512:     public function jsonSerialize()
513:     {
514:         return $this->toArray();
515:     }
516: 
517:     /**
518:      * {@inheritDoc}
519:      *
520:      */
521:     public function compile($preserveKeys = true)
522:     {
523:         return new Collection($this->toArray($preserveKeys));
524:     }
525: 
526:     /**
527:      * {@inheritDoc}
528:      *
529:      * @return \Cake\Collection\Iterator\BufferedIterator
530:      */
531:     public function buffered()
532:     {
533:         return new BufferedIterator($this);
534:     }
535: 
536:     /**
537:      * {@inheritDoc}
538:      *
539:      * @return \Cake\Collection\Iterator\TreeIterator
540:      */
541:     public function listNested($dir = 'desc', $nestingKey = 'children')
542:     {
543:         $dir = strtolower($dir);
544:         $modes = [
545:             'desc' => TreeIterator::SELF_FIRST,
546:             'asc' => TreeIterator::CHILD_FIRST,
547:             'leaves' => TreeIterator::LEAVES_ONLY
548:         ];
549: 
550:         return new TreeIterator(
551:             new NestIterator($this, $nestingKey),
552:             isset($modes[$dir]) ? $modes[$dir] : $dir
553:         );
554:     }
555: 
556:     /**
557:      * {@inheritDoc}
558:      *
559:      * @return \Cake\Collection\Iterator\StoppableIterator
560:      */
561:     public function stopWhen($condition)
562:     {
563:         if (!is_callable($condition)) {
564:             $condition = $this->_createMatcherFilter($condition);
565:         }
566: 
567:         return new StoppableIterator($this, $condition);
568:     }
569: 
570:     /**
571:      * {@inheritDoc}
572:      *
573:      */
574:     public function unfold(callable $transformer = null)
575:     {
576:         if ($transformer === null) {
577:             $transformer = function ($item) {
578:                 return $item;
579:             };
580:         }
581: 
582:         return new Collection(
583:             new RecursiveIteratorIterator(
584:                 new UnfoldIterator($this, $transformer),
585:                 RecursiveIteratorIterator::LEAVES_ONLY
586:             )
587:         );
588:     }
589: 
590:     /**
591:      * {@inheritDoc}
592:      *
593:      */
594:     public function through(callable $handler)
595:     {
596:         $result = $handler($this);
597: 
598:         return $result instanceof CollectionInterface ? $result : new Collection($result);
599:     }
600: 
601:     /**
602:      * {@inheritDoc}
603:      *
604:      */
605:     public function zip($items)
606:     {
607:         return new ZipIterator(array_merge([$this], func_get_args()));
608:     }
609: 
610:     /**
611:      * {@inheritDoc}
612:      *
613:      */
614:     public function zipWith($items, $callable)
615:     {
616:         if (func_num_args() > 2) {
617:             $items = func_get_args();
618:             $callable = array_pop($items);
619:         } else {
620:             $items = [$items];
621:         }
622: 
623:         return new ZipIterator(array_merge([$this], $items), $callable);
624:     }
625: 
626:     /**
627:      * {@inheritDoc}
628:      *
629:      */
630:     public function chunk($chunkSize)
631:     {
632:         return $this->map(function ($v, $k, $iterator) use ($chunkSize) {
633:             $values = [$v];
634:             for ($i = 1; $i < $chunkSize; $i++) {
635:                 $iterator->next();
636:                 if (!$iterator->valid()) {
637:                     break;
638:                 }
639:                 $values[] = $iterator->current();
640:             }
641: 
642:             return $values;
643:         });
644:     }
645: 
646:     /**
647:      * {@inheritDoc}
648:      *
649:      */
650:     public function isEmpty()
651:     {
652:         foreach ($this->unwrap() as $el) {
653:             return false;
654:         }
655: 
656:         return true;
657:     }
658: 
659:     /**
660:      * {@inheritDoc}
661:      *
662:      */
663:     public function unwrap()
664:     {
665:         $iterator = $this;
666:         while (get_class($iterator) === 'Cake\Collection\Collection') {
667:             $iterator = $iterator->getInnerIterator();
668:         }
669: 
670:         return $iterator;
671:     }
672: 
673:     /**
674:      * Backwards compatible wrapper for unwrap()
675:      *
676:      * @return \Iterator
677:      * @deprecated
678:      */
679:     public function _unwrap()
680:     {
681:         return $this->unwrap();
682:     }
683: 
684:     /**
685:      * {@inheritDoc}
686:      *
687:      * @return \Cake\Collection\CollectionInterface
688:      */
689:     public function cartesianProduct(callable $operation = null, callable $filter = null)
690:     {
691:         if ($this->isEmpty()) {
692:             return new Collection([]);
693:         }
694: 
695:         $collectionArrays = [];
696:         $collectionArraysKeys = [];
697:         $collectionArraysCounts = [];
698: 
699:         foreach ($this->toList() as $value) {
700:             $valueCount = count($value);
701:             if ($valueCount !== count($value, COUNT_RECURSIVE)) {
702:                 throw new LogicException('Cannot find the cartesian product of a multidimensional array');
703:             }
704: 
705:             $collectionArraysKeys[] = array_keys($value);
706:             $collectionArraysCounts[] = $valueCount;
707:             $collectionArrays[] = $value;
708:         }
709: 
710:         $result = [];
711:         $lastIndex = count($collectionArrays) - 1;
712:         // holds the indexes of the arrays that generate the current combination
713:         $currentIndexes = array_fill(0, $lastIndex + 1, 0);
714: 
715:         $changeIndex = $lastIndex;
716: 
717:         while (!($changeIndex === 0 && $currentIndexes[0] === $collectionArraysCounts[0])) {
718:             $currentCombination = array_map(function ($value, $keys, $index) {
719:                 return $value[$keys[$index]];
720:             }, $collectionArrays, $collectionArraysKeys, $currentIndexes);
721: 
722:             if ($filter === null || $filter($currentCombination)) {
723:                 $result[] = ($operation === null) ? $currentCombination : $operation($currentCombination);
724:             }
725: 
726:             $currentIndexes[$lastIndex]++;
727: 
728:             for ($changeIndex = $lastIndex; $currentIndexes[$changeIndex] === $collectionArraysCounts[$changeIndex] && $changeIndex > 0; $changeIndex--) {
729:                 $currentIndexes[$changeIndex] = 0;
730:                 $currentIndexes[$changeIndex - 1]++;
731:             }
732:         }
733: 
734:         return new Collection($result);
735:     }
736: 
737:     /**
738:      * {@inheritDoc}
739:      *
740:      * @return \Cake\Collection\CollectionInterface
741:      */
742:     public function transpose()
743:     {
744:         $arrayValue = $this->toList();
745:         $length = count(current($arrayValue));
746:         $result = [];
747:         foreach ($arrayValue as $column => $row) {
748:             if (count($row) != $length) {
749:                 throw new LogicException('Child arrays do not have even length');
750:             }
751:         }
752: 
753:         for ($column = 0; $column < $length; $column++) {
754:             $result[] = array_column($arrayValue, $column);
755:         }
756: 
757:         return new Collection($result);
758:     }
759: }
760: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs