Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flat PHP Array to Hierarchy Tree

I have an array with the following keys

id   
parent_id
name

A sample array:

array(7) {
  [0]=>
  array(3) {
    ["id"]=>
    string(1) "4"
    ["parent_id"]=>
    string(1) "0"
    ["name"]=>
    string(16) "Top Level Page 4"
  }
  [1]=>
  array(3) {
    ["id"]=>
    string(1) "5"
    ["parent_id"]=>
    string(1) "1"
    ["name"]=>
    string(19) "Second Level Page 1"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(1) "6"
    ["parent_id"]=>
    string(1) "2"
    ["name"]=>
    string(19) "Second Level Page 2"
  }
  [3]=>
  array(3) {
    ["id"]=>
    string(1) "7"
    ["parent_id"]=>
    string(1) "5"
    ["name"]=>
    string(18) "Third Level Page 1"
  }
  [4]=>
  array(3) {
    ["id"]=>
    string(1) "3"
    ["parent_id"]=>
    string(1) "0"
    ["name"]=>
    string(16) "Top Level Page 3"
  }
  [5]=>
  array(3) {
    ["id"]=>
    string(1) "2"
    ["parent_id"]=>
    string(1) "0"
    ["name"]=>
    string(16) "Top Level Page 2"
  }
  [6]=>
  array(3) {
    ["id"]=>
    string(1) "1"
    ["parent_id"]=>
    string(1) "0"
    ["name"]=>
    string(16) "Top Level Page 1"
  }
}

What I would like to do is display a hierarchy tree using this array, the code I have at the moment is producing:

Top Level Page 4
--Second Level Page 1
---Second Level Page 2
----Third Level Page 1
Top Level Page 3
Top Level Page 2
Top Level Page 1

Ideally I need to produce the below result but with unlimited levels:

Top Level Page 4
-Second Level Page 1
-Second Level Page 2
--Third Level Page 1
Top Level Page 3
Top Level Page 2
Top Level Page 1

The code I have so far is:

$level = 1;
        foreach ($data as $row) {
            if ($row['parent_id'] == 0) {
                echo $row['name'] . '<br/>';
            } else {
                $level++;
                foreach ($data as $m) {
                    if ($m['parent_id'] === $row['parent_id']) {
                        $c = 0;
                        $append = '';
                        while ($c < $level) {
                            $append.="-";
                            $c++;
                        }
                        echo $append . $row['name'] . '<br/>';
                    }
                }
            }
        }
    }

If anyone could give me some pointers on how to achieve this it would be much appreciated.

I found a solution here: Create nested list from PHP array for dropdown select field

like image 791
Tommy Arnold Avatar asked Feb 06 '13 22:02

Tommy Arnold


2 Answers

You should use recursion.

Here an exemple of code:

$datas = array(
    array('id' => 1, 'parent' => 0, 'name' => 'Page 1'),
    array('id' => 2, 'parent' => 1, 'name' => 'Page 1.1'),
    array('id' => 3, 'parent' => 2, 'name' => 'Page 1.1.1'),
    array('id' => 4, 'parent' => 3, 'name' => 'Page 1.1.1.1'),
    array('id' => 5, 'parent' => 3, 'name' => 'Page 1.1.1.2'),
    array('id' => 6, 'parent' => 1, 'name' => 'Page 1.2'),
    array('id' => 7, 'parent' => 6, 'name' => 'Page 1.2.1'),
    array('id' => 8, 'parent' => 0, 'name' => 'Page 2'),
    array('id' => 9, 'parent' => 0, 'name' => 'Page 3'),
    array('id' => 10, 'parent' => 9, 'name' => 'Page 3.1'),
    array('id' => 11, 'parent' => 9, 'name' => 'Page 3.2'),
    array('id' => 12, 'parent' => 11, 'name' => 'Page 3.2.1'),
    );

function generatePageTree($datas, $parent = 0, $depth=0){
    $ni=count($datas);
    if($ni === 0 || $depth > 1000) return ''; // Make sure not to have an endless recursion
    $tree = '<ul>';
    for($i=0; $i < $ni; $i++){
        if($datas[$i]['parent'] == $parent){
            $tree .= '<li>';
            $tree .= $datas[$i]['name'];
            $tree .= generatePageTree($datas, $datas[$i]['id'], $depth+1);
            $tree .= '</li>';
        }
    }
    $tree .= '</ul>';
    return $tree;
}

echo(generatePageTree($datas));

You can test it at: http://phpfiddle.org/main/code/1qy-5fj

Or if you want the exact format:

function generatePageTree($datas, $parent = 0, $depth = 0){
    $ni=count($datas);
    if($ni === 0 || $depth > 1000) return ''; // Make sure not to have an endless recursion
    $tree = '';
    for($i=0; $i < $ni; $i++){
        if($datas[$i]['parent'] == $parent){
            $tree .= str_repeat('-', $depth);
            $tree .= $datas[$i]['name'] . '<br/>';
            $tree .= generatePageTree($datas, $datas[$i]['id'], $depth+1);
        }
    }
    return $tree;
}

The test: http://phpfiddle.org/main/code/jw3-s1j

like image 120
Code-Source Avatar answered Sep 18 '22 11:09

Code-Source


You can use FlatToTreeConverter class from Gears helper library:

<?php
namespace Cosmologist\Gears\Collection;
/**
 * Class able to convert a flat array with parent ID's to a nested tree
 */
class FlatToTreeConverter
{
    /**
     * Convert a flat array with parent ID's to a nested tree
     *
     * @link http://blog.tekerson.com/2009/03/03/converting-a-flat-array-with-parent-ids-to-a-nested-tree/
     *
     * @param array  $array           Flat array
     * @param string $idKeyName       Key name for the element containing the item ID
     * @param string $parentIdKey     Key name for the element containing the parent item ID
     * @param string $childNodesField Key name for the element for placement children
     *
     * @return array
     */
    public static function convert(array $array, $idKeyName = 'id', $parentIdKey = 'parentId', $childNodesField = 'children')
    {
        $indexed = array();
        // first pass - get the array indexed by the primary id
        foreach ($array as $row) {
            $indexed[$row[$idKeyName]]                   = $row;
            $indexed[$row[$idKeyName]][$childNodesField] = array();
        }
        // second pass
        $root = array();
        foreach ($indexed as $id => $row) {
            $indexed[$row[$parentIdKey]][$childNodesField][$id] = &$indexed[$id];
            if (!$row[$parentIdKey]) {
                $root[$id] = &$indexed[$id];
            }
        }
        return $root;
    }
}
like image 32
Cosmologist Avatar answered Sep 18 '22 11:09

Cosmologist