Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Move nest() over

Make get() able to take arrays for $path.
  • Loading branch information...
commit 239f52c48ca124ee51820a04619778a5f176c0ea 1 parent afa0329
Mark Story markstory authored
Showing with 492 additions and 1 deletion.
  1. +419 −0 lib/Cake/Test/Case/Utility/Set2Test.php
  2. +73 −1 lib/Cake/Utility/Set2.php
419 lib/Cake/Test/Case/Utility/Set2Test.php
View
@@ -179,6 +179,9 @@ public function testGet() {
$result = Set2::get($data, '1.Article');
$this->assertEquals($data[1]['Article'], $result);
+
+ $result = Set2::get($data, array('1', 'Article'));
+ $this->assertEquals($data[1]['Article'], $result);
}
/**
@@ -1419,4 +1422,420 @@ public function _mapCallback($value) {
public function _reduceCallback($one, $two) {
return $one + $two;
}
+
+/**
+ * test Set nest with a normal model result set. For kicks rely on Set nest detecting the key names
+ * automatically
+ *
+ * @return void
+ */
+ public function testNestModel() {
+ $input = array(
+ array(
+ 'ModelName' => array(
+ 'id' => 1,
+ 'parent_id' => null
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 2,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 3,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 4,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 5,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 6,
+ 'parent_id' => null
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 7,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 8,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 9,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 10,
+ 'parent_id' => 6
+ )
+ )
+ );
+ $expected = array(
+ array(
+ 'ModelName' => array(
+ 'id' => 1,
+ 'parent_id' => null
+ ),
+ 'children' => array(
+ array(
+ 'ModelName' => array(
+ 'id' => 2,
+ 'parent_id' => 1
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 3,
+ 'parent_id' => 1
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 4,
+ 'parent_id' => 1
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 5,
+ 'parent_id' => 1
+ ),
+ 'children' => array()
+ ),
+
+ )
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 6,
+ 'parent_id' => null
+ ),
+ 'children' => array(
+ array(
+ 'ModelName' => array(
+ 'id' => 7,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 8,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 9,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 10,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ )
+ )
+ )
+ );
+ $result = Set2::nest($input);
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * test Set nest with a normal model result set, and a nominated root id
+ *
+ * @return void
+ */
+ public function testNestModelExplicitRoot() {
+ $input = array(
+ array(
+ 'ModelName' => array(
+ 'id' => 1,
+ 'parent_id' => null
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 2,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 3,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 4,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 5,
+ 'parent_id' => 1
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 6,
+ 'parent_id' => null
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 7,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 8,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 9,
+ 'parent_id' => 6
+ ),
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 10,
+ 'parent_id' => 6
+ )
+ )
+ );
+ $expected = array(
+ array(
+ 'ModelName' => array(
+ 'id' => 6,
+ 'parent_id' => null
+ ),
+ 'children' => array(
+ array(
+ 'ModelName' => array(
+ 'id' => 7,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 8,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 9,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ ),
+ array(
+ 'ModelName' => array(
+ 'id' => 10,
+ 'parent_id' => 6
+ ),
+ 'children' => array()
+ )
+ )
+ )
+ );
+ $result = Set2::nest($input, array('root' => 6));
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * test Set nest with a 1d array - this method should be able to handle any type of array input
+ *
+ * @return void
+ */
+ public function testNest1Dimensional() {
+ $input = array(
+ array(
+ 'id' => 1,
+ 'parent_id' => null
+ ),
+ array(
+ 'id' => 2,
+ 'parent_id' => 1
+ ),
+ array(
+ 'id' => 3,
+ 'parent_id' => 1
+ ),
+ array(
+ 'id' => 4,
+ 'parent_id' => 1
+ ),
+ array(
+ 'id' => 5,
+ 'parent_id' => 1
+ ),
+ array(
+ 'id' => 6,
+ 'parent_id' => null
+ ),
+ array(
+ 'id' => 7,
+ 'parent_id' => 6
+ ),
+ array(
+ 'id' => 8,
+ 'parent_id' => 6
+ ),
+ array(
+ 'id' => 9,
+ 'parent_id' => 6
+ ),
+ array(
+ 'id' => 10,
+ 'parent_id' => 6
+ )
+ );
+ $expected = array(
+ array(
+ 'id' => 1,
+ 'parent_id' => null,
+ 'children' => array(
+ array(
+ 'id' => 2,
+ 'parent_id' => 1,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 3,
+ 'parent_id' => 1,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 4,
+ 'parent_id' => 1,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 5,
+ 'parent_id' => 1,
+ 'children' => array()
+ ),
+
+ )
+ ),
+ array(
+ 'id' => 6,
+ 'parent_id' => null,
+ 'children' => array(
+ array(
+ 'id' => 7,
+ 'parent_id' => 6,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 8,
+ 'parent_id' => 6,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 9,
+ 'parent_id' => 6,
+ 'children' => array()
+ ),
+ array(
+ 'id' => 10,
+ 'parent_id' => 6,
+ 'children' => array()
+ )
+ )
+ )
+ );
+ $result = Set2::nest($input, array('idPath' => '{n}.id', 'parentPath' => '{n}.parent_id'));
+ $this->assertEquals($expected, $result);
+ }
+
+/**
+ * test Set nest with no specified parent data.
+ *
+ * The result should be the same as the input.
+ * For an easier comparison, unset all the empty children arrays from the result
+ *
+ * @return void
+ */
+ public function testMissingParent() {
+ $input = array(
+ array(
+ 'id' => 1,
+ ),
+ array(
+ 'id' => 2,
+ ),
+ array(
+ 'id' => 3,
+ ),
+ array(
+ 'id' => 4,
+ ),
+ array(
+ 'id' => 5,
+ ),
+ array(
+ 'id' => 6,
+ ),
+ array(
+ 'id' => 7,
+ ),
+ array(
+ 'id' => 8,
+ ),
+ array(
+ 'id' => 9,
+ ),
+ array(
+ 'id' => 10,
+ )
+ );
+
+ $result = Set2::nest($input, array('idPath' => '{n}.id', 'parentPath' => '{n}.parent_id'));
+ foreach($result as &$row) {
+ if (empty($row['children'])) {
+ unset($row['children']);
+ }
+ }
+ $this->assertEquals($input, $result);
+ }
}
74 lib/Cake/Utility/Set2.php
View
@@ -43,7 +43,11 @@ public static function get(array $data, $path) {
if (empty($data) || empty($path)) {
return null;
}
- $parts = explode('.', $path);
+ if (is_string($path)) {
+ $parts = explode('.', $path);
+ } else {
+ $parts = $path;
+ }
while (($key = array_shift($parts)) !== null) {
if (is_array($data) && isset($data[$key])) {
$data =& $data[$key];
@@ -810,4 +814,72 @@ public static function normalize(array $data, $assoc = true) {
return $data;
}
+/**
+ * Takes in a flat array and returns a nested array
+ *
+ * @param mixed $data
+ * @param array $options Options are:
+ * children - the key name to use in the resultset for children
+ * idPath - the path to a key that identifies each entry
+ * parentPath - the path to a key that identifies the parent of each entry
+ * root - the id of the desired top-most result
+ * @return array of results, nested
+ * @link
+ */
+ public static function nest($data, $options = array()) {
+ if (!$data) {
+ return $data;
+ }
+
+ $alias = key(current($data));
+ $options += array(
+ 'idPath' => "{n}.$alias.id",
+ 'parentPath' => "{n}.$alias.parent_id",
+ 'children' => 'children',
+ 'root' => null
+ );
+
+ $return = $idMap = array();
+ $ids = self::extract($data, $options['idPath']);
+
+ $idKeys = explode('.', $options['idPath']);
+ array_shift($idKeys);
+
+ $parentKeys = explode('.', $options['parentPath']);
+ array_shift($parentKeys);
+
+ foreach ($data as $result) {
+ $result[$options['children']] = array();
+
+ $id = self::get($result, $idKeys);
+ $parentId = self::get($result, $parentKeys);
+
+ if (isset($idMap[$id][$options['children']])) {
+ $idMap[$id] = array_merge($result, (array)$idMap[$id]);
+ } else {
+ $idMap[$id] = array_merge($result, array($options['children'] => array()));
+ }
+ if (!$parentId || !in_array($parentId, $ids)) {
+ $return[] =& $idMap[$id];
+ } else {
+ $idMap[$parentId][$options['children']][] =& $idMap[$id];
+ }
+ }
+
+ if ($options['root']) {
+ $root = $options['root'];
+ } else {
+ $root = self::get($return[0], $parentKeys);
+ }
+
+ foreach ($return as $i => $result) {
+ $id = self::get($result, $idKeys);
+ $parentId = self::get($result, $parentKeys);
+ if ($id !== $root && $parentId != $root) {
+ unset($return[$i]);
+ }
+ }
+ return array_values($return);
+ }
+
}
Please sign in to comment.
Something went wrong with that request. Please try again.