Browse Source

Merge pull request #12541 from cakephp/collection-lazy

Added a collection lazy() method
Mark Story 7 years ago
parent
commit
fb90516537

+ 10 - 0
src/Collection/CollectionInterface.php

@@ -803,6 +803,16 @@ interface CollectionInterface extends Iterator, JsonSerializable
     public function compile($preserveKeys = true);
 
     /**
+     * Returns a new collection where any operations chained after it are guaranteed
+     * to be run lazily. That is, elements will be yieleded one at a time.
+     *
+     * A lazy collection can only be iterated once. A second attempt results in an error.
+     *
+     * @return \Cake\Collection\CollectionInterface
+     */
+    public function lazy();
+
+    /**
      * Returns a new collection where the operations performed by this collection.
      * No matter how many times the new collection is iterated, those operations will
      * only be performed once.

+ 14 - 0
src/Collection/CollectionTrait.php

@@ -687,6 +687,20 @@ trait CollectionTrait
 
     /**
      * {@inheritDoc}
+     */
+    public function lazy()
+    {
+        $generator = function () {
+            foreach ($this->unwrap() as $k => $v) {
+                yield $k => $v;
+            }
+        };
+
+        return new Collection($generator());
+    }
+
+    /**
+     * {@inheritDoc}
      *
      * @return \Cake\Collection\Iterator\BufferedIterator
      */

+ 17 - 0
tests/TestCase/Collection/CollectionTest.php

@@ -2735,4 +2735,21 @@ class CollectionTest extends TestCase
         $this->assertTrue($newIterator->checkValues());
         $this->assertCount(3, $newIterator->toArray());
     }
+
+    /**
+     * Tests that elements in a lazy collection are not fetched immediately.
+     *
+     * @return void
+     */
+    public function testLazy()
+    {
+        $items = ['a' => 1, 'b' => 2, 'c' => 3];
+        $collection = (new Collection($items))->lazy();
+        $callable = $this->getMockBuilder(\StdClass::class)
+            ->setMethods(['__invoke'])
+            ->getMock();
+
+        $callable->expects($this->never())->method('__invoke');
+        $collection->filter($callable)->filter($callable);
+    }
 }