Permalink
Browse files

Implementend collection nest

  • Loading branch information...
1 parent 4de65e7 commit 41ddf1a8d53758fd890734031fab25808bc7c3ad @lorenzo lorenzo committed Jan 18, 2014
Showing with 152 additions and 0 deletions.
  1. +49 −0 src/Collection/CollectionTrait.php
  2. +103 −0 tests/TestCase/Collection/CollectionTest.php
@@ -15,6 +15,7 @@
namespace Cake\Collection;
use AppendIterator;
+use ArrayObject;
use Cake\Collection\Collection;
use Cake\Collection\Iterator\ExtractIterator;
use Cake\Collection\Iterator\FilterIterator;
@@ -702,6 +703,54 @@ public function combine($keyPath, $valuePath, $groupPath = null) {
}
/**
+ * Returns a new collection where the values are nested in a tree-like structure
+ * based on an id property path and a parent id property path.
+ *
+ * @param callable|string $idPath the column name path to use for determining
+ * whther an element is parent of another
+ * @param callable|string $parentPath the column name path to use for determining
+ * whther an element is child of another
+ * @return \Cake\Collection\Collection
+ */
+ public function nest($idPath, $parentPath) {
+ $parents = [];
+ $idPath = $this->_propertyExtractor($idPath);
+ $parentPath = $this->_propertyExtractor($parentPath);
+ $isObject = !is_array($this->first());
+
+ $mapper = function($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath) {
+ $row['children'] = [];
+ $id = $idPath($row, $key);
+ $parentId = $parentPath($row, $key);
+ $parents[$id] =& $row;
+ $mapReduce->emitIntermediate($id, $parentId);
+ };
+
+ $reducer = function($values, $key, $mapReduce) use (&$parents, $isObject) {
+ if (empty($key) || !isset($parents[$key])) {
+ foreach ($values as $id) {
+ $parents[$id] = $isObject ? $parents[$id] : new ArrayObject($parents[$id]);
+ $mapReduce->emit($parents[$id]);
+ }
+ return;
+ }
+
+ foreach ($values as $id) {
+ $parents[$key]['children'][] =& $parents[$id];
+ }
+ };
+
+ $collection = new MapReduce($this, $mapper, $reducer);
+ if (!$isObject) {
+ $collection = (new Collection($collection))->map(function($value) {
+ return (array)$value;
+ });
+ }
+
+ return new Collection($collection);
+ }
+
+/**
* Returns an array representation of the results
*
* @param boolean $preserveKeys whether to use the keys returned by this
@@ -676,4 +676,107 @@ function($value, $key) {
$this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
}
+/**
+ * Tests the nest method with only one level
+ *
+ * @return void
+ */
+ public function testNest() {
+ $items = [
+ ['id' => 1, 'parent_id' => null],
+ ['id' => 2, 'parent_id' => 1],
+ ['id' => 3, 'parent_id' => 1],
+ ['id' => 4, 'parent_id' => 1],
+ ['id' => 5, 'parent_id' => 6],
+ ['id' => 6, 'parent_id' => null],
+ ['id' => 7, 'parent_id' => 1],
+ ['id' => 8, 'parent_id' => 6],
+ ['id' => 9, 'parent_id' => 6],
+ ['id' => 10, 'parent_id' => 6]
+ ];
+ $collection = (new Collection($items))->nest('id', 'parent_id');
+ $expected = [
+ [
+ 'id' => 1,
+ 'parent_id' => null,
+ 'children' => [
+ ['id' => 2, 'parent_id' => 1, 'children' => []],
+ ['id' => 3, 'parent_id' => 1, 'children' => []],
+ ['id' => 4, 'parent_id' => 1, 'children' => []],
+ ['id' => 7, 'parent_id' => 1, 'children' => []]
+ ]
+ ],
+ [
+ 'id' => 6,
+ 'parent_id' => null,
+ 'children' => [
+ ['id' => 5, 'parent_id' => 6, 'children' => []],
+ ['id' => 8, 'parent_id' => 6, 'children' => []],
+ ['id' => 9, 'parent_id' => 6, 'children' => []],
+ ['id' => 10, 'parent_id' => 6, 'children' => []]
+ ]
+ ]
+ ];
+ $this->assertEquals($expected, $collection->toArray());
+ }
+
+/**
+ * Tests the nest method with more than one level
+ *
+ * @return void
+ */
+ public function testNestMultiLevel() {
+ $items = [
+ ['id' => 1, 'parent_id' => null],
+ ['id' => 2, 'parent_id' => 1],
+ ['id' => 3, 'parent_id' => 2],
+ ['id' => 4, 'parent_id' => 2],
+ ['id' => 5, 'parent_id' => 3],
+ ['id' => 6, 'parent_id' => null],
+ ['id' => 7, 'parent_id' => 3],
+ ['id' => 8, 'parent_id' => 4],
+ ['id' => 9, 'parent_id' => 6],
+ ['id' => 10, 'parent_id' => 6]
+ ];
+ $collection = (new Collection($items))->nest('id', 'parent_id');
+ $expected = [
+ [
+ 'id' => 1,
+ 'parent_id' => null,
+ 'children' => [
+ [
+ 'id' => 2,
+ 'parent_id' => 1,
+ 'children' => [
+ [
+ 'id' => 3,
+ 'parent_id' => 2,
+ 'children' => [
+ ['id' => 5, 'parent_id' => 3, 'children' => []],
+ ['id' => 7, 'parent_id' => 3, 'children' => []]
+ ]
+ ],
+ [
+ 'id' => 4,
+ 'parent_id' => 2,
+ 'children' => [
+ ['id' => 8, 'parent_id' => 4, 'children' => []]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ 'id' => 6,
+ 'parent_id' => null,
+ 'children' => [
+ ['id' => 9, 'parent_id' => 6, 'children' => []],
+ ['id' => 10, 'parent_id' => 6, 'children' => []]
+ ]
+ ]
+ ];
+ $this->assertEquals($expected, $collection->toArray());
+ }
+
}

0 comments on commit 41ddf1a

Please sign in to comment.