Browse Source

Naïve version of supporting {n} notation in collection methods

Jose Lorenzo Rodriguez 10 years ago
parent
commit
4761d41897

+ 11 - 2
src/Collection/CollectionTrait.php

@@ -158,11 +158,20 @@ trait CollectionTrait
     /**
      * {@inheritDoc}
      *
-     * @return \Cake\Collection\Iterator\ExtractIterator
      */
     public function extract($matcher)
     {
-        return new ExtractIterator($this->unwrap(), $matcher);
+        $extractor = new ExtractIterator($this->unwrap(), $matcher);
+
+        if (is_string($matcher) && strpos($matcher, '{n}') !== false) {
+            $extractor = $extractor
+                ->filter(function ($data) {
+                    return $data instanceof \Traversable || is_array($data);
+                })
+                ->unfold();
+        }
+
+        return $extractor;
     }
 
     /**

+ 26 - 6
src/Collection/ExtractTrait.php

@@ -32,13 +32,15 @@ trait ExtractTrait
      */
     protected function _propertyExtractor($callback)
     {
-        if (is_string($callback)) {
-            $path = explode('.', $callback);
-            $callback = function ($element) use ($path) {
-                return $this->_extract($element, $path);
-            };
+        if (!is_string($callback)) {
+            return $callback;
         }
 
+        $path = explode('.', $callback);
+        $callback = function ($element) use ($path) {
+            return $this->_extract($element, $path);
+        };
+
         return $callback;
     }
 
@@ -53,10 +55,28 @@ trait ExtractTrait
     protected function _extract($data, $path)
     {
         $value = null;
-        foreach ($path as $column) {
+        $collectionTransform = false;
+
+        foreach ($path as $i => $column) {
+            if ($column === '{n}') {
+                $collectionTransform = true;
+                continue;
+            }
+
+            if ($collectionTransform &&
+                !($data instanceof \Traversable || is_array($data))) {
+                return null;
+            }
+
+            if ($collectionTransform) {
+                $rest = implode('.', array_slice($path, $i));
+                return (new Collection($data))->extract($rest);
+            }
+
             if (!isset($data[$column])) {
                 return null;
             }
+
             $value = $data[$column];
             $data = $value;
         }

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

@@ -1381,4 +1381,51 @@ class CollectionTest extends TestCase
         $collection = new Collection(['a' => 1, 'b' => 4, 'c' => 6]);
         $this->assertEquals(11, $collection->sumOf());
     }
+
+    public function testUnfoldedExtract()
+    {
+        $items = [
+            ['comments' => [['id' => 1], ['id' => 2]]],
+            ['comments' => [['id' => 3], ['id' => 4]]],
+            ['comments' => [['id' => 7], ['nope' => 8]]],
+        ];
+
+        $extracted = (new Collection($items))->extract('comments.{n}.id');
+        $this->assertEquals([1, 2, 3, 4, 7, null], $extracted->toList());
+
+        $items = [
+            [
+                'comments' => [
+                    [
+                        'voters' => [['id' => 1], ['id' => 2]]
+                    ]
+                ]
+            ],
+            [
+                'comments' => [
+                    [
+                        'voters' => [['id' => 3], ['id' => 4]]
+                    ]
+                ]
+            ],
+            [
+                'comments' => [
+                    [
+                        'voters' => [['id' => 5], ['nope' => 'fail'], ['id' => 6]]
+                    ]
+                ]
+            ],
+            [
+                'comments' => [
+                    [
+                        'not_voters' => [['id' => 5]]
+                    ]
+                ]
+            ],
+            ['not_comments' => []]
+        ];
+        $extracted = (new Collection($items))->extract('comments.{n}.voters.{n}.id');
+        $expected = [1, 2, 3, 4, 5, null];
+        $this->assertEquals($expected, $extracted->toList());
+    }
 }