Skip to content

Commit

Permalink
Merge branch '3.next' of github.com:cakephp/cakephp into 3.next
Browse files Browse the repository at this point in the history
  • Loading branch information
markstory committed Jul 16, 2016
2 parents 918ddd2 + 8decfd9 commit 09bfdb9
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/Collection/CollectionInterface.php
Expand Up @@ -620,9 +620,10 @@ public function combine($keyPath, $valuePath, $groupPath = null);
* whether an element is parent of another
* @param callable|string $parentPath the column name path to use for determining
* whether an element is child of another
* @param string $nestingKey The key name under which children are nested
* @return \Cake\Collection\CollectionInterface
*/
public function nest($idPath, $parentPath);
public function nest($idPath, $parentPath, $nestingKey = 'children');

/**
* Returns a new collection containing each of the elements found in `$values` as
Expand Down
10 changes: 5 additions & 5 deletions src/Collection/CollectionTrait.php
Expand Up @@ -422,22 +422,22 @@ public function combine($keyPath, $valuePath, $groupPath = null)
* {@inheritDoc}
*
*/
public function nest($idPath, $parentPath)
public function nest($idPath, $parentPath, $nestingKey = 'children')
{
$parents = [];
$idPath = $this->_propertyExtractor($idPath);
$parentPath = $this->_propertyExtractor($parentPath);
$isObject = true;

$mapper = function ($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath) {
$row['children'] = [];
$mapper = function ($row, $key, $mapReduce) use (&$parents, $idPath, $parentPath, $nestingKey) {
$row[$nestingKey] = [];
$id = $idPath($row, $key);
$parentId = $parentPath($row, $key);
$parents[$id] =& $row;
$mapReduce->emitIntermediate($id, $parentId);
};

$reducer = function ($values, $key, $mapReduce) use (&$parents, &$isObject) {
$reducer = function ($values, $key, $mapReduce) use (&$parents, &$isObject, $nestingKey) {
static $foundOutType = false;
if (!$foundOutType) {
$isObject = is_object(current($parents));
Expand All @@ -456,7 +456,7 @@ public function nest($idPath, $parentPath)
foreach ($values as $id) {
$children[] =& $parents[$id];
}
$parents[$key]['children'] = $children;
$parents[$key][$nestingKey] = $children;
};

return (new Collection(new MapReduce($this->unwrap(), $mapper, $reducer)))
Expand Down
4 changes: 2 additions & 2 deletions src/ORM/Query.php
Expand Up @@ -56,8 +56,8 @@
* @method \Cake\Collection\CollectionInterface append(array|Traversable $items) Appends more rows to the result of the query.
* @method \Cake\Collection\CollectionInterface combine($k, $v, $g = null) Returns the values of the column $v index by column $k,
* and grouped by $g.
* @method \Cake\Collection\CollectionInterface nest($k, $p) Creates a tree structure by nesting the values of column $p into that
* with the same value for $k.
* @method \Cake\Collection\CollectionInterface nest($k, $p, $n = 'children') Creates a tree structure by nesting the values of column $p into that
* with the same value for $k using $n as the nesting key.
* @method array toArray() Returns a key-value array with the results of this query.
* @method array toList() Returns a numerically indexed array with the results of this query.
* @method \Cake\Collection\CollectionInterface stopWhen(callable $c) Returns each row until the callable returns true.
Expand Down
6 changes: 4 additions & 2 deletions src/ORM/Table.php
Expand Up @@ -1085,13 +1085,14 @@ public function findList(Query $query, array $options)
*
* You can customize what fields are used for nesting results, by default the
* primary key and the `parent_id` fields are used. If you wish to change
* these defaults you need to provide the keys `keyField` or `parentField` in
* these defaults you need to provide the keys `keyField`, `parentField` or `nestingKey` in
* `$options`:
*
* ```
* $table->find('threaded', [
* 'keyField' => 'id',
* 'parentField' => 'ancestor_id'
* 'nestingKey' => 'children'
* ]);
* ```
*
Expand All @@ -1104,6 +1105,7 @@ public function findThreaded(Query $query, array $options)
$options += [
'keyField' => $this->primaryKey(),
'parentField' => 'parent_id',
'nestingKey' => 'children'
];

if (isset($options['idField'])) {
Expand All @@ -1115,7 +1117,7 @@ public function findThreaded(Query $query, array $options)
$options = $this->_setFieldMatchers($options, ['keyField', 'parentField']);

return $query->formatResults(function ($results) use ($options) {
return $results->nest($options['keyField'], $options['parentField']);
return $results->nest($options['keyField'], $options['parentField'], $options['nestingKey']);
});
}

Expand Down
165 changes: 165 additions & 0 deletions tests/TestCase/Collection/CollectionTest.php
Expand Up @@ -913,12 +913,117 @@ public function testNest()
$this->assertEquals($expected, $collection->toArray());
}

/**
* Tests the nest method with alternate nesting key
*
* @return void
*/
public function testNestAlternateNestingKey()
{
$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', 'nodes');
$expected = [
[
'id' => 1,
'parent_id' => null,
'nodes' => [
['id' => 2, 'parent_id' => 1, 'nodes' => []],
['id' => 3, 'parent_id' => 1, 'nodes' => []],
['id' => 4, 'parent_id' => 1, 'nodes' => []],
['id' => 7, 'parent_id' => 1, 'nodes' => []]
]
],
[
'id' => 6,
'parent_id' => null,
'nodes' => [
['id' => 5, 'parent_id' => 6, 'nodes' => []],
['id' => 8, 'parent_id' => 6, 'nodes' => []],
['id' => 9, 'parent_id' => 6, 'nodes' => []],
['id' => 10, 'parent_id' => 6, 'nodes' => []]
]
]
];
$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', 'nodes');
$expected = [
[
'id' => 1,
'parent_id' => null,
'nodes' => [
[
'id' => 2,
'parent_id' => 1,
'nodes' => [
[
'id' => 3,
'parent_id' => 2,
'nodes' => [
['id' => 5, 'parent_id' => 3, 'nodes' => []],
['id' => 7, 'parent_id' => 3, 'nodes' => []]
]
],
[
'id' => 4,
'parent_id' => 2,
'nodes' => [
['id' => 8, 'parent_id' => 4, 'nodes' => []]
]
]
]
]
]
],
[
'id' => 6,
'parent_id' => null,
'nodes' => [
['id' => 9, 'parent_id' => 6, 'nodes' => []],
['id' => 10, 'parent_id' => 6, 'nodes' => []]
]
]
];
$this->assertEquals($expected, $collection->toArray());
}

/**
* Tests the nest method with more than one level
*
* @return void
*/
public function testNestMultiLevelAlternateNestingKey()
{
$items = [
['id' => 1, 'parent_id' => null],
Expand Down Expand Up @@ -1033,6 +1138,66 @@ public function testNestObjects()
$this->assertEquals($expected, $collection->toArray());
}

/**
* Tests the nest method with more than one level
*
* @return void
*/
public function testNestObjectsAlternateNestingKey()
{
$items = [
new ArrayObject(['id' => 1, 'parent_id' => null]),
new ArrayObject(['id' => 2, 'parent_id' => 1]),
new ArrayObject(['id' => 3, 'parent_id' => 2]),
new ArrayObject(['id' => 4, 'parent_id' => 2]),
new ArrayObject(['id' => 5, 'parent_id' => 3]),
new ArrayObject(['id' => 6, 'parent_id' => null]),
new ArrayObject(['id' => 7, 'parent_id' => 3]),
new ArrayObject(['id' => 8, 'parent_id' => 4]),
new ArrayObject(['id' => 9, 'parent_id' => 6]),
new ArrayObject(['id' => 10, 'parent_id' => 6])
];
$collection = (new Collection($items))->nest('id', 'parent_id', 'nodes');
$expected = [
new ArrayObject([
'id' => 1,
'parent_id' => null,
'nodes' => [
new ArrayObject([
'id' => 2,
'parent_id' => 1,
'nodes' => [
new ArrayObject([
'id' => 3,
'parent_id' => 2,
'nodes' => [
new ArrayObject(['id' => 5, 'parent_id' => 3, 'nodes' => []]),
new ArrayObject(['id' => 7, 'parent_id' => 3, 'nodes' => []])
]
]),
new ArrayObject([
'id' => 4,
'parent_id' => 2,
'nodes' => [
new ArrayObject(['id' => 8, 'parent_id' => 4, 'nodes' => []])
]
])
]
])
]
]),
new ArrayObject([
'id' => 6,
'parent_id' => null,
'nodes' => [
new ArrayObject(['id' => 9, 'parent_id' => 6, 'nodes' => []]),
new ArrayObject(['id' => 10, 'parent_id' => 6, 'nodes' => []])
]
])
];
$this->assertEquals($expected, $collection->toArray());
}

/**
* Tests insert
*
Expand Down

0 comments on commit 09bfdb9

Please sign in to comment.