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.7 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.7
      • 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
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • 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 (https://cakephp.org)
   4:  * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11:  * @link          https://cakephp.org CakePHP(tm) Project
  12:  * @since         3.0.0
  13:  * @license       https://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:     public function each(callable $c)
  49:     {
  50:         foreach ($this->optimizeUnwrap() as $k => $v) {
  51:             $c($v, $k);
  52:         }
  53: 
  54:         return $this;
  55:     }
  56: 
  57:     /**
  58:      * {@inheritDoc}
  59:      *
  60:      * @return \Cake\Collection\Iterator\FilterIterator
  61:      */
  62:     public function filter(callable $c = null)
  63:     {
  64:         if ($c === null) {
  65:             $c = function ($v) {
  66:                 return (bool)$v;
  67:             };
  68:         }
  69: 
  70:         return new FilterIterator($this->unwrap(), $c);
  71:     }
  72: 
  73:     /**
  74:      * {@inheritDoc}
  75:      *
  76:      * @return \Cake\Collection\Iterator\FilterIterator
  77:      */
  78:     public function reject(callable $c)
  79:     {
  80:         return new FilterIterator($this->unwrap(), function ($key, $value, $items) use ($c) {
  81:             return !$c($key, $value, $items);
  82:         });
  83:     }
  84: 
  85:     /**
  86:      * {@inheritDoc}
  87:      */
  88:     public function every(callable $c)
  89:     {
  90:         foreach ($this->optimizeUnwrap() as $key => $value) {
  91:             if (!$c($value, $key)) {
  92:                 return false;
  93:             }
  94:         }
  95: 
  96:         return true;
  97:     }
  98: 
  99:     /**
 100:      * {@inheritDoc}
 101:      */
 102:     public function some(callable $c)
 103:     {
 104:         foreach ($this->optimizeUnwrap() as $key => $value) {
 105:             if ($c($value, $key) === true) {
 106:                 return true;
 107:             }
 108:         }
 109: 
 110:         return false;
 111:     }
 112: 
 113:     /**
 114:      * {@inheritDoc}
 115:      */
 116:     public function contains($value)
 117:     {
 118:         foreach ($this->optimizeUnwrap() as $v) {
 119:             if ($value === $v) {
 120:                 return true;
 121:             }
 122:         }
 123: 
 124:         return false;
 125:     }
 126: 
 127:     /**
 128:      * {@inheritDoc}
 129:      *
 130:      * @return \Cake\Collection\Iterator\ReplaceIterator
 131:      */
 132:     public function map(callable $c)
 133:     {
 134:         return new ReplaceIterator($this->unwrap(), $c);
 135:     }
 136: 
 137:     /**
 138:      * {@inheritDoc}
 139:      */
 140:     public function reduce(callable $c, $zero = null)
 141:     {
 142:         $isFirst = false;
 143:         if (func_num_args() < 2) {
 144:             $isFirst = true;
 145:         }
 146: 
 147:         $result = $zero;
 148:         foreach ($this->optimizeUnwrap() as $k => $value) {
 149:             if ($isFirst) {
 150:                 $result = $value;
 151:                 $isFirst = false;
 152:                 continue;
 153:             }
 154:             $result = $c($result, $value, $k);
 155:         }
 156: 
 157:         return $result;
 158:     }
 159: 
 160:     /**
 161:      * {@inheritDoc}
 162:      */
 163:     public function extract($matcher)
 164:     {
 165:         $extractor = new ExtractIterator($this->unwrap(), $matcher);
 166:         if (is_string($matcher) && strpos($matcher, '{*}') !== false) {
 167:             $extractor = $extractor
 168:                 ->filter(function ($data) {
 169:                     return $data !== null && ($data instanceof Traversable || is_array($data));
 170:                 })
 171:                 ->unfold();
 172:         }
 173: 
 174:         return $extractor;
 175:     }
 176: 
 177:     /**
 178:      * {@inheritDoc}
 179:      */
 180:     public function max($callback, $type = \SORT_NUMERIC)
 181:     {
 182:         return (new SortIterator($this->unwrap(), $callback, \SORT_DESC, $type))->first();
 183:     }
 184: 
 185:     /**
 186:      * {@inheritDoc}
 187:      */
 188:     public function min($callback, $type = \SORT_NUMERIC)
 189:     {
 190:         return (new SortIterator($this->unwrap(), $callback, \SORT_ASC, $type))->first();
 191:     }
 192: 
 193:     /**
 194:      * {@inheritDoc}
 195:      */
 196:     public function avg($matcher = null)
 197:     {
 198:         $result = $this;
 199:         if ($matcher != null) {
 200:             $result = $result->extract($matcher);
 201:         }
 202:         $result = $result
 203:             ->reduce(function ($acc, $current) {
 204:                 list($count, $sum) = $acc;
 205: 
 206:                 return [$count + 1, $sum + $current];
 207:             }, [0, 0]);
 208: 
 209:         if ($result[0] === 0) {
 210:             return null;
 211:         }
 212: 
 213:         return $result[1] / $result[0];
 214:     }
 215: 
 216:     /**
 217:      * {@inheritDoc}
 218:      */
 219:     public function median($matcher = null)
 220:     {
 221:         $elements = $this;
 222:         if ($matcher != null) {
 223:             $elements = $elements->extract($matcher);
 224:         }
 225:         $values = $elements->toList();
 226:         sort($values);
 227:         $count = count($values);
 228: 
 229:         if ($count === 0) {
 230:             return null;
 231:         }
 232: 
 233:         $middle = (int)($count / 2);
 234: 
 235:         if ($count % 2) {
 236:             return $values[$middle];
 237:         }
 238: 
 239:         return ($values[$middle - 1] + $values[$middle]) / 2;
 240:     }
 241: 
 242:     /**
 243:      * {@inheritDoc}
 244:      */
 245:     public function sortBy($callback, $dir = \SORT_DESC, $type = \SORT_NUMERIC)
 246:     {
 247:         return new SortIterator($this->unwrap(), $callback, $dir, $type);
 248:     }
 249: 
 250:     /**
 251:      * {@inheritDoc}
 252:      */
 253:     public function groupBy($callback)
 254:     {
 255:         $callback = $this->_propertyExtractor($callback);
 256:         $group = [];
 257:         foreach ($this->optimizeUnwrap() as $value) {
 258:             $group[$callback($value)][] = $value;
 259:         }
 260: 
 261:         return new Collection($group);
 262:     }
 263: 
 264:     /**
 265:      * {@inheritDoc}
 266:      */
 267:     public function indexBy($callback)
 268:     {
 269:         $callback = $this->_propertyExtractor($callback);
 270:         $group = [];
 271:         foreach ($this->optimizeUnwrap() as $value) {
 272:             $group[$callback($value)] = $value;
 273:         }
 274: 
 275:         return new Collection($group);
 276:     }
 277: 
 278:     /**
 279:      * {@inheritDoc}
 280:      */
 281:     public function countBy($callback)
 282:     {
 283:         $callback = $this->_propertyExtractor($callback);
 284: 
 285:         $mapper = function ($value, $key, $mr) use ($callback) {
 286:             /** @var \Cake\Collection\Iterator\MapReduce $mr */
 287:             $mr->emitIntermediate($value, $callback($value));
 288:         };
 289: 
 290:         $reducer = function ($values, $key, $mr) {
 291:             /** @var \Cake\Collection\Iterator\MapReduce $mr */
 292:             $mr->emit(count($values), $key);
 293:         };
 294: 
 295:         return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer));
 296:     }
 297: 
 298:     /**
 299:      * {@inheritDoc}
 300:      */
 301:     public function sumOf($matcher = null)
 302:     {
 303:         if ($matcher === null) {
 304:             return array_sum($this->toList());
 305:         }
 306: 
 307:         $callback = $this->_propertyExtractor($matcher);
 308:         $sum = 0;
 309:         foreach ($this->optimizeUnwrap() as $k => $v) {
 310:             $sum += $callback($v, $k);
 311:         }
 312: 
 313:         return $sum;
 314:     }
 315: 
 316:     /**
 317:      * {@inheritDoc}
 318:      */
 319:     public function shuffle()
 320:     {
 321:         $elements = $this->toArray();
 322:         shuffle($elements);
 323: 
 324:         return new Collection($elements);
 325:     }
 326: 
 327:     /**
 328:      * {@inheritDoc}
 329:      */
 330:     public function sample($size = 10)
 331:     {
 332:         return new Collection(new LimitIterator($this->shuffle(), 0, $size));
 333:     }
 334: 
 335:     /**
 336:      * {@inheritDoc}
 337:      */
 338:     public function take($size = 1, $from = 0)
 339:     {
 340:         return new Collection(new LimitIterator($this, $from, $size));
 341:     }
 342: 
 343:     /**
 344:      * {@inheritDoc}
 345:      */
 346:     public function skip($howMany)
 347:     {
 348:         return new Collection(new LimitIterator($this, $howMany));
 349:     }
 350: 
 351:     /**
 352:      * {@inheritDoc}
 353:      */
 354:     public function match(array $conditions)
 355:     {
 356:         return $this->filter($this->_createMatcherFilter($conditions));
 357:     }
 358: 
 359:     /**
 360:      * {@inheritDoc}
 361:      */
 362:     public function firstMatch(array $conditions)
 363:     {
 364:         return $this->match($conditions)->first();
 365:     }
 366: 
 367:     /**
 368:      * {@inheritDoc}
 369:      */
 370:     public function first()
 371:     {
 372:         $iterator = new LimitIterator($this, 0, 1);
 373:         foreach ($iterator as $result) {
 374:             return $result;
 375:         }
 376:     }
 377: 
 378:     /**
 379:      * {@inheritDoc}
 380:      */
 381:     public function last()
 382:     {
 383:         $iterator = $this->optimizeUnwrap();
 384:         if (is_array($iterator)) {
 385:             return array_pop($iterator);
 386:         }
 387: 
 388:         if ($iterator instanceof Countable) {
 389:             $count = count($iterator);
 390:             if ($count === 0) {
 391:                 return null;
 392:             }
 393:             $iterator = new LimitIterator($iterator, $count - 1, 1);
 394:         }
 395: 
 396:         $result = null;
 397:         foreach ($iterator as $result) {
 398:             // No-op
 399:         }
 400: 
 401:         return $result;
 402:     }
 403: 
 404:     /**
 405:      * {@inheritDoc}
 406:      */
 407:     public function takeLast($howMany)
 408:     {
 409:         if ($howMany < 1) {
 410:             throw new \InvalidArgumentException("The takeLast method requires a number greater than 0.");
 411:         }
 412: 
 413:         $iterator = $this->optimizeUnwrap();
 414:         if (is_array($iterator)) {
 415:             return new Collection(array_slice($iterator, $howMany * -1));
 416:         }
 417: 
 418:         if ($iterator instanceof Countable) {
 419:             $count = count($iterator);
 420: 
 421:             if ($count === 0) {
 422:                 return new Collection([]);
 423:             }
 424: 
 425:             $iterator = new LimitIterator($iterator, max(0, $count - $howMany), $howMany);
 426: 
 427:             return new Collection($iterator);
 428:         }
 429: 
 430:         $generator = function ($iterator, $howMany) {
 431:             $result = [];
 432:             $bucket = 0;
 433:             $offset = 0;
 434: 
 435:             /**
 436:              * Consider the collection of elements [1, 2, 3, 4, 5, 6, 7, 8, 9], in order
 437:              * to get the last 4 elements, we can keep a buffer of 4 elements and
 438:              * fill it circularly using modulo logic, we use the $bucket variable
 439:              * to track the position to fill next in the buffer. This how the buffer
 440:              * looks like after 4 iterations:
 441:              *
 442:              * 0) 1 2 3 4 -- $bucket now goes back to 0, we have filled 4 elementes
 443:              * 1) 5 2 3 4 -- 5th iteration
 444:              * 2) 5 6 3 4 -- 6th iteration
 445:              * 3) 5 6 7 4 -- 7th iteration
 446:              * 4) 5 6 7 8 -- 8th iteration
 447:              * 5) 9 6 7 8
 448:              *
 449:              *  We can see that at the end of the iterations, the buffer contains all
 450:              *  the last four elements, just in the wrong order. How do we keep the
 451:              *  original order? Well, it turns out that the number of iteration also
 452:              *  give us a clue on what's going on, Let's add a marker for it now:
 453:              *
 454:              * 0) 1 2 3 4
 455:              *    ^ -- The 0) above now becomes the $offset variable
 456:              * 1) 5 2 3 4
 457:              *      ^ -- $offset = 1
 458:              * 2) 5 6 3 4
 459:              *        ^ -- $offset = 2
 460:              * 3) 5 6 7 4
 461:              *          ^ -- $offset = 3
 462:              * 4) 5 6 7 8
 463:              *    ^  -- We use module logic for $offset too
 464:              *          and as you can see each time $offset is 0, then the buffer
 465:              *          is sorted exactly as we need.
 466:              * 5) 9 6 7 8
 467:              *      ^ -- $offset = 1
 468:              *
 469:              * The $offset variable is a marker for splitting the buffer in two,
 470:              * elements to the right for the marker are the head of the final result,
 471:              * whereas the elements at the left are the tail. For example consider step 5)
 472:              * which has an offset of 1:
 473:              *
 474:              * - $head = elements to the right = [6, 7, 8]
 475:              * - $tail = elements to the left =  [9]
 476:              * - $result = $head + $tail = [6, 7, 8, 9]
 477:              *
 478:              * The logic above applies to collections of any size.
 479:              */
 480: 
 481:             foreach ($iterator as $k => $item) {
 482:                 $result[$bucket] = [$k, $item];
 483:                 $bucket = (++$bucket) % $howMany;
 484:                 $offset++;
 485:             }
 486: 
 487:             $offset = $offset % $howMany;
 488:             $head = array_slice($result, $offset);
 489:             $tail = array_slice($result, 0, $offset);
 490: 
 491:             foreach ($head as $v) {
 492:                 yield $v[0] => $v[1];
 493:             }
 494: 
 495:             foreach ($tail as $v) {
 496:                 yield $v[0] => $v[1];
 497:             }
 498:         };
 499: 
 500:         return new Collection($generator($iterator, $howMany));
 501:     }
 502: 
 503:     /**
 504:      * {@inheritDoc}
 505:      */
 506:     public function append($items)
 507:     {
 508:         $list = new AppendIterator();
 509:         $list->append($this->unwrap());
 510:         $list->append((new Collection($items))->unwrap());
 511: 
 512:         return new Collection($list);
 513:     }
 514: 
 515:     /**
 516:      * {@inheritDoc}
 517:      */
 518:     public function appendItem($item, $key = null)
 519:     {
 520:         if ($key !== null) {
 521:             $data = [$key => $item];
 522:         } else {
 523:             $data = [$item];
 524:         }
 525: 
 526:         return $this->append($data);
 527:     }
 528: 
 529:     /**
 530:      * {@inheritDoc}
 531:      */
 532:     public function prepend($items)
 533:     {
 534:         return (new Collection($items))->append($this);
 535:     }
 536: 
 537:     /**
 538:      * {@inheritDoc}
 539:      */
 540:     public function prependItem($item, $key = null)
 541:     {
 542:         if ($key !== null) {
 543:             $data = [$key => $item];
 544:         } else {
 545:             $data = [$item];
 546:         }
 547: 
 548:         return $this->prepend($data);
 549:     }
 550: 
 551:     /**
 552:      * {@inheritDoc}
 553:      */
 554:     public function combine($keyPath, $valuePath, $groupPath = null)
 555:     {
 556:         $options = [
 557:             'keyPath' => $this->_propertyExtractor($keyPath),
 558:             'valuePath' => $this->_propertyExtractor($valuePath),
 559:             'groupPath' => $groupPath ? $this->_propertyExtractor($groupPath) : null
 560:         ];
 561: 
 562:         $mapper = function ($value, $key, $mapReduce) use ($options) {
 563:             /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */
 564:             $rowKey = $options['keyPath'];
 565:             $rowVal = $options['valuePath'];
 566: 
 567:             if (!$options['groupPath']) {
 568:                 $mapReduce->emit($rowVal($value, $key), $rowKey($value, $key));
 569: 
 570:                 return null;
 571:             }
 572: 
 573:             $key = $options['groupPath']($value, $key);
 574:             $mapReduce->emitIntermediate(
 575:                 [$rowKey($value, $key) => $rowVal($value, $key)],
 576:                 $key
 577:             );
 578:         };
 579: 
 580:         $reducer = function ($values, $key, $mapReduce) {
 581:             $result = [];
 582:             foreach ($values as $value) {
 583:                 $result += $value;
 584:             }
 585:             /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */
 586:             $mapReduce->emit($result, $key);
 587:         };
 588: 
 589:         return new Collection(new MapReduce($this->unwrap(), $mapper, $reducer));
 590:     }
 591: 
 592:     /**
 593:      * {@inheritDoc}
 594:      */
 595:     public function nest($idPath, $parentPath, $nestingKey = 'children')
 596:     {
 597:         $parents = [];
 598:         $idPath = $this->_propertyExtractor($idPath);
 599:         $parentPath = $this->_propertyExtractor($parentPath);
 600:         $isObject = true;
 601: 
 602:         $mapper = function ($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath, $nestingKey) {
 603:             $row[$nestingKey] = [];
 604:             $id = $idPath($row, $key);
 605:             $parentId = $parentPath($row, $key);
 606:             $parents[$id] =& $row;
 607:             /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */
 608:             $mapReduce->emitIntermediate($id, $parentId);
 609:         };
 610: 
 611:         $reducer = function ($values, $key, $mapReduce) use (&$parents, &$isObject, $nestingKey) {
 612:             static $foundOutType = false;
 613:             if (!$foundOutType) {
 614:                 $isObject = is_object(current($parents));
 615:                 $foundOutType = true;
 616:             }
 617:             if (empty($key) || !isset($parents[$key])) {
 618:                 foreach ($values as $id) {
 619:                     $parents[$id] = $isObject ? $parents[$id] : new ArrayIterator($parents[$id], 1);
 620:                     /** @var \Cake\Collection\Iterator\MapReduce $mapReduce */
 621:                     $mapReduce->emit($parents[$id]);
 622:                 }
 623: 
 624:                 return null;
 625:             }
 626: 
 627:             $children = [];
 628:             foreach ($values as $id) {
 629:                 $children[] =& $parents[$id];
 630:             }
 631:             $parents[$key][$nestingKey] = $children;
 632:         };
 633: 
 634:         return (new Collection(new MapReduce($this->unwrap(), $mapper, $reducer)))
 635:             ->map(function ($value) use (&$isObject) {
 636:                 /** @var \ArrayIterator $value */
 637:                 return $isObject ? $value : $value->getArrayCopy();
 638:             });
 639:     }
 640: 
 641:     /**
 642:      * {@inheritDoc}
 643:      *
 644:      * @return \Cake\Collection\Iterator\InsertIterator
 645:      */
 646:     public function insert($path, $values)
 647:     {
 648:         return new InsertIterator($this->unwrap(), $path, $values);
 649:     }
 650: 
 651:     /**
 652:      * {@inheritDoc}
 653:      */
 654:     public function toArray($preserveKeys = true)
 655:     {
 656:         $iterator = $this->unwrap();
 657:         if ($iterator instanceof ArrayIterator) {
 658:             $items = $iterator->getArrayCopy();
 659: 
 660:             return $preserveKeys ? $items : array_values($items);
 661:         }
 662:         // RecursiveIteratorIterator can return duplicate key values causing
 663:         // data loss when converted into an array
 664:         if ($preserveKeys && get_class($iterator) === 'RecursiveIteratorIterator') {
 665:             $preserveKeys = false;
 666:         }
 667: 
 668:         return iterator_to_array($this, $preserveKeys);
 669:     }
 670: 
 671:     /**
 672:      * {@inheritDoc}
 673:      */
 674:     public function toList()
 675:     {
 676:         return $this->toArray(false);
 677:     }
 678: 
 679:     /**
 680:      * {@inheritDoc}
 681:      */
 682:     public function jsonSerialize()
 683:     {
 684:         return $this->toArray();
 685:     }
 686: 
 687:     /**
 688:      * {@inheritDoc}
 689:      */
 690:     public function compile($preserveKeys = true)
 691:     {
 692:         return new Collection($this->toArray($preserveKeys));
 693:     }
 694: 
 695:     /**
 696:      * {@inheritDoc}
 697:      */
 698:     public function lazy()
 699:     {
 700:         $generator = function () {
 701:             foreach ($this->unwrap() as $k => $v) {
 702:                 yield $k => $v;
 703:             }
 704:         };
 705: 
 706:         return new Collection($generator());
 707:     }
 708: 
 709:     /**
 710:      * {@inheritDoc}
 711:      *
 712:      * @return \Cake\Collection\Iterator\BufferedIterator
 713:      */
 714:     public function buffered()
 715:     {
 716:         return new BufferedIterator($this->unwrap());
 717:     }
 718: 
 719:     /**
 720:      * {@inheritDoc}
 721:      *
 722:      * @return \Cake\Collection\Iterator\TreeIterator
 723:      */
 724:     public function listNested($dir = 'desc', $nestingKey = 'children')
 725:     {
 726:         $dir = strtolower($dir);
 727:         $modes = [
 728:             'desc' => TreeIterator::SELF_FIRST,
 729:             'asc' => TreeIterator::CHILD_FIRST,
 730:             'leaves' => TreeIterator::LEAVES_ONLY
 731:         ];
 732: 
 733:         return new TreeIterator(
 734:             new NestIterator($this, $nestingKey),
 735:             isset($modes[$dir]) ? $modes[$dir] : $dir
 736:         );
 737:     }
 738: 
 739:     /**
 740:      * {@inheritDoc}
 741:      *
 742:      * @return \Cake\Collection\Iterator\StoppableIterator
 743:      */
 744:     public function stopWhen($condition)
 745:     {
 746:         if (!is_callable($condition)) {
 747:             $condition = $this->_createMatcherFilter($condition);
 748:         }
 749: 
 750:         return new StoppableIterator($this->unwrap(), $condition);
 751:     }
 752: 
 753:     /**
 754:      * {@inheritDoc}
 755:      */
 756:     public function unfold(callable $transformer = null)
 757:     {
 758:         if ($transformer === null) {
 759:             $transformer = function ($item) {
 760:                 return $item;
 761:             };
 762:         }
 763: 
 764:         return new Collection(
 765:             new RecursiveIteratorIterator(
 766:                 new UnfoldIterator($this->unwrap(), $transformer),
 767:                 RecursiveIteratorIterator::LEAVES_ONLY
 768:             )
 769:         );
 770:     }
 771: 
 772:     /**
 773:      * {@inheritDoc}
 774:      */
 775:     public function through(callable $handler)
 776:     {
 777:         $result = $handler($this);
 778: 
 779:         return $result instanceof CollectionInterface ? $result : new Collection($result);
 780:     }
 781: 
 782:     /**
 783:      * {@inheritDoc}
 784:      */
 785:     public function zip($items)
 786:     {
 787:         return new ZipIterator(array_merge([$this->unwrap()], func_get_args()));
 788:     }
 789: 
 790:     /**
 791:      * {@inheritDoc}
 792:      */
 793:     public function zipWith($items, $callable)
 794:     {
 795:         if (func_num_args() > 2) {
 796:             $items = func_get_args();
 797:             $callable = array_pop($items);
 798:         } else {
 799:             $items = [$items];
 800:         }
 801: 
 802:         return new ZipIterator(array_merge([$this->unwrap()], $items), $callable);
 803:     }
 804: 
 805:     /**
 806:      * {@inheritDoc}
 807:      */
 808:     public function chunk($chunkSize)
 809:     {
 810:         return $this->map(function ($v, $k, $iterator) use ($chunkSize) {
 811:             $values = [$v];
 812:             for ($i = 1; $i < $chunkSize; $i++) {
 813:                 $iterator->next();
 814:                 if (!$iterator->valid()) {
 815:                     break;
 816:                 }
 817:                 $values[] = $iterator->current();
 818:             }
 819: 
 820:             return $values;
 821:         });
 822:     }
 823: 
 824:     /**
 825:      * {@inheritDoc}
 826:      */
 827:     public function chunkWithKeys($chunkSize, $preserveKeys = true)
 828:     {
 829:         return $this->map(function ($v, $k, $iterator) use ($chunkSize, $preserveKeys) {
 830:             $key = 0;
 831:             if ($preserveKeys) {
 832:                 $key = $k;
 833:             }
 834:             $values = [$key => $v];
 835:             for ($i = 1; $i < $chunkSize; $i++) {
 836:                 $iterator->next();
 837:                 if (!$iterator->valid()) {
 838:                     break;
 839:                 }
 840:                 if ($preserveKeys) {
 841:                     $values[$iterator->key()] = $iterator->current();
 842:                 } else {
 843:                     $values[] = $iterator->current();
 844:                 }
 845:             }
 846: 
 847:             return $values;
 848:         });
 849:     }
 850: 
 851:     /**
 852:      * {@inheritDoc}
 853:      */
 854:     public function isEmpty()
 855:     {
 856:         foreach ($this as $el) {
 857:             return false;
 858:         }
 859: 
 860:         return true;
 861:     }
 862: 
 863:     /**
 864:      * {@inheritDoc}
 865:      */
 866:     public function unwrap()
 867:     {
 868:         $iterator = $this;
 869:         while (get_class($iterator) === 'Cake\Collection\Collection') {
 870:             $iterator = $iterator->getInnerIterator();
 871:         }
 872: 
 873:         if ($iterator !== $this && $iterator instanceof CollectionInterface) {
 874:             $iterator = $iterator->unwrap();
 875:         }
 876: 
 877:         return $iterator;
 878:     }
 879: 
 880:     /**
 881:      * Backwards compatible wrapper for unwrap()
 882:      *
 883:      * @return \Traversable
 884:      * @deprecated 3.0.10 Will be removed in 4.0.0
 885:      */
 886:     // @codingStandardsIgnoreLine
 887:     public function _unwrap()
 888:     {
 889:         deprecationWarning('CollectionTrait::_unwrap() is deprecated. Use CollectionTrait::unwrap() instead.');
 890: 
 891:         return $this->unwrap();
 892:     }
 893: 
 894:     /**
 895:      * @param callable|null $operation Operation
 896:      * @param callable|null $filter Filter
 897:      * @return \Cake\Collection\CollectionInterface
 898:      * @throws \LogicException
 899:      */
 900:     public function cartesianProduct(callable $operation = null, callable $filter = null)
 901:     {
 902:         if ($this->isEmpty()) {
 903:             return new Collection([]);
 904:         }
 905: 
 906:         $collectionArrays = [];
 907:         $collectionArraysKeys = [];
 908:         $collectionArraysCounts = [];
 909: 
 910:         foreach ($this->toList() as $value) {
 911:             $valueCount = count($value);
 912:             if ($valueCount !== count($value, COUNT_RECURSIVE)) {
 913:                 throw new LogicException('Cannot find the cartesian product of a multidimensional array');
 914:             }
 915: 
 916:             $collectionArraysKeys[] = array_keys($value);
 917:             $collectionArraysCounts[] = $valueCount;
 918:             $collectionArrays[] = $value;
 919:         }
 920: 
 921:         $result = [];
 922:         $lastIndex = count($collectionArrays) - 1;
 923:         // holds the indexes of the arrays that generate the current combination
 924:         $currentIndexes = array_fill(0, $lastIndex + 1, 0);
 925: 
 926:         $changeIndex = $lastIndex;
 927: 
 928:         while (!($changeIndex === 0 && $currentIndexes[0] === $collectionArraysCounts[0])) {
 929:             $currentCombination = array_map(function ($value, $keys, $index) {
 930:                 return $value[$keys[$index]];
 931:             }, $collectionArrays, $collectionArraysKeys, $currentIndexes);
 932: 
 933:             if ($filter === null || $filter($currentCombination)) {
 934:                 $result[] = ($operation === null) ? $currentCombination : $operation($currentCombination);
 935:             }
 936: 
 937:             $currentIndexes[$lastIndex]++;
 938: 
 939:             for ($changeIndex = $lastIndex; $currentIndexes[$changeIndex] === $collectionArraysCounts[$changeIndex] && $changeIndex > 0; $changeIndex--) {
 940:                 $currentIndexes[$changeIndex] = 0;
 941:                 $currentIndexes[$changeIndex - 1]++;
 942:             }
 943:         }
 944: 
 945:         return new Collection($result);
 946:     }
 947: 
 948:     /**
 949:      * {@inheritDoc}
 950:      *
 951:      * @return \Cake\Collection\CollectionInterface
 952:      * @throws \LogicException
 953:      */
 954:     public function transpose()
 955:     {
 956:         $arrayValue = $this->toList();
 957:         $length = count(current($arrayValue));
 958:         $result = [];
 959:         foreach ($arrayValue as $column => $row) {
 960:             if (count($row) != $length) {
 961:                 throw new LogicException('Child arrays do not have even length');
 962:             }
 963:         }
 964: 
 965:         for ($column = 0; $column < $length; $column++) {
 966:             $result[] = array_column($arrayValue, $column);
 967:         }
 968: 
 969:         return new Collection($result);
 970:     }
 971: 
 972:     /**
 973:      * {@inheritDoc}
 974:      *
 975:      * @return int
 976:      */
 977:     public function count()
 978:     {
 979:         $traversable = $this->optimizeUnwrap();
 980: 
 981:         if (is_array($traversable)) {
 982:             return count($traversable);
 983:         }
 984: 
 985:         return iterator_count($traversable);
 986:     }
 987: 
 988:     /**
 989:      * {@inheritDoc}
 990:      *
 991:      * @return int
 992:      */
 993:     public function countKeys()
 994:     {
 995:         return count($this->toArray());
 996:     }
 997: 
 998:     /**
 999:      * Unwraps this iterator and returns the simplest
1000:      * traversable that can be used for getting the data out
1001:      *
1002:      * @return \Traversable|array
1003:      */
1004:     protected function optimizeUnwrap()
1005:     {
1006:         $iterator = $this->unwrap();
1007: 
1008:         if (get_class($iterator) === ArrayIterator::class) {
1009:             $iterator = $iterator->getArrayCopy();
1010:         }
1011: 
1012:         return $iterator;
1013:     }
1014: }
1015: 
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