PHP - How to Build Tree Structure List

PHP - How to build tree structure list?

You do not need to create 2 tables in the database for this you can maintain it like below from one table only

+-------+---------------+---------------------------+
| id | parent_id | title |
+-------+---------------+---------------------------+
| 1 | 0 | Parent Page |
| 2 | 1 | Sub Page |
| 3 | 2 | Sub Sub Page |
| 4 | 0 | Another Parent Page |
+-------+---------------+---------------------------+

The array generated will be like

Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
[children] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 1
[title] => Sub Sub Page
)
)
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)

You need to use the below recursive function to achieve it

function buildTree(array $elements, $parentId = 0) {
$branch = array();

foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}

return $branch;
}

$tree = buildTree($rows);

The algorithm is pretty simple:

  1. Take the array of all elements and the id of the current parent
    (initially 0/nothing/null/whatever).
  2. Loop through all elements.
  3. If the parent_id of an element matches the current parent id you got in 1., the element is a child of the parent. Put it in your list
    of current children (here: $branch).
  4. Call the function recursively with the id of the element you have just identified in 3., i.e. find all children of that element,
    and add them as children element.
  5. Return your list of found children.

Create tree structure in PHP with given depth, root and parent amount of children

EDIT: I see that the original question is looking for a solution that works with the 3 constants. I've added to the code so it's possible to define constants and loop based on them, but I am not sure I understand how one is supposed to use all three variables. In particular, MATCHES_TREE_MAX_DEPTH seems out of place given your descriptions. You indicated only two levels of children and instructed that we should skip any additional items in your list. If you want code that defines still more levels of children, you'll need to be clearer about how you want your structure to grow.

Given the very narrow limits described to this problem in the additional comments, it seems like a waste of effort to agonize over loops to handle this problem. This tree can't ever have more than five elements so an iterative solution like this should work:

// function to convert from list objects to the array we want as output
function new_obj($obj) {
$ret = array(
"userid" => $obj->user_id,
"name" => $obj->first_name
);
if (isset($obj->street)) {
$ret["street"] = $obj->street;
}
$ret["matches"] = [];
return $ret;
}

// INPUT DATA
$root = (object)["user_id" => "1", "first_name" => "Dennis"];
$children = [
(object)[
"user_id" => "2",
"first_name" => "Tom",
"street" => "Teststreet"
],
(object)[
"user_id" => "3",
"first_name" => "Mary",
"street" => "Maryland avenue"
],
(object)[
"user_id" => "4",
"first_name" => "Jeff",
"street" => "Teststreet"
],
(object)[
"user_id" => "5",
"first_name" => "Arthur",
"street" => "Teststreet"
]

];

$result1 = new_obj($root);

// an iterative solution, only works for one trivial set of values for the constants defined
// but also does provide some insight into a more general solution
if (isset($children[0])) {
$result1["matches"][0] = new_obj($children[0]);
}
if (isset($children[1])) {
$result1["matches"][1] = new_obj($children[1]);
}
if (isset($children[2])) {
$result1["matches"][0]["matches"][0] = new_obj($children[2]);
}
if (isset($children[3])) {
$result1["matches"][1]["matches"][0] = new_obj($children[3]);
}

print_r($result1);

If you want to define constants/vars to specify child limits and then use loops, try this with the same $root and $children vars defined above.

// solution must use these constants:
define("MATCHES_TREE_MAX_DEPTH", 3);
define("MATCHES_TREE_ROOT_MAX_CHILDREN_AMOUNT", 2);
define("MATCHES_TREE_PARENT_MAX_CHILDREN_AMOUNT", 1);

$result2 = new_obj($root);

$i = 0;
while ($child = array_shift($children)) {
$result2["matches"][$i] = new_obj($child);
$i++;
if ($i >= MATCHES_TREE_ROOT_MAX_CHILDREN_AMOUNT) break;
}

$i = 0;
while ($grandchild = array_shift($children)) {
$child = $result2["matches"][$i];
if (count($child["matches"]) >= MATCHES_TREE_PARENT_MAX_CHILDREN_AMOUNT) {
// if we reach a child that has its max number of grandchildren, it's time to quit
break;
}
// otherwise, assign this child as a grandchild
$result2["matches"][$i]["matches"] = new_obj($grandchild);

// increment the counter and check if we cycle back to the first child of root
$i++;
if ($i >= count($result2["matches"])) {
$i = 0;
}
}
print_r($result2);

How to build tree structure from array in PHP

As you want the year and month as the main indexes, you need to use both of these when adding the data into the $tree...

$tree = array();
foreach ($arr as $row) {
$tree[$row['year']][$row['month']] = [ 'id' => $row['id'],
'name' => $row['name']];
}

with your test data, this gives...

Array
(
[2019] => Array
(
[9] => Array
(
[id] => 100
[name] => a
)

[12] => Array
(
[id] => 101
[name] => b
)

)

[2020] => Array
(
[1] => Array
(
[id] => 102
[name] => c
)

[2] => Array
(
[id] => 103
[name] => d
)

)

)

Build directory tree from flat array of paths in PHP

You could do this in two steps:

  • Create a hierarchy of associative arrays, where the labels are the keys, and nested arrays correspond to children.
  • Transform that structure to the target structure

Code:

function buildTree($branches) {
// Create a hierchy where keys are the labels
$rootChildren = [];
foreach($branches as $branch) {
$children =& $rootChildren;
foreach($branch as $label) {
if (!isset($children[$label])) $children[$label] = [];
$children =& $children[$label];
}
}
// Create target structure from that hierarchy
function recur($children) {
$result = [];
foreach($children as $label => $grandchildren) {
$node = ["label" => $label];
if (count($grandchildren)) $node["children"] = recur($grandchildren);
$result[] = $node;
}
return $result;
}
return recur($rootChildren);
}

Call it likes this:

$tree = buildTree($branches);

The above will omit the children key when there are no children. If you need to have a children key in those cases as well, then just remove the if (count($grandchildren)) condition, and simplify to the following version:

function buildTree($branches) {
// Create a hierchy where keys are the labels
$rootChildren = [];
foreach($branches as $branch) {
$children =& $rootChildren;
foreach($branch as $label) {
if (!isset($children[$label])) $children[$label] = [];
$children =& $children[$label];
}
}
// Create target structure from that hierarchy
function recur($children) {
$result = [];
foreach($children as $label => $grandchildren) {
$result[] = ["label" => $label, "children" => recur($grandchildren)];
}
return $result;
}
return recur($rootChildren);
}

Create Tree Structure from Array in PHP

Collect all ids in the order of parent -> child in an array where parent_id is the key and it has an array of all child IDs. Second step is to just traverse the tree and assign the ranks.

<?php

function assignLevels(&$data){
$kids = [];

foreach($data as $d){
$kids[ $d['parent_id'] ] = $kids[ $d['parent_id'] ] ?? [];
$kids[ $d['parent_id'] ][] = $d['id'];
}

$data = array_column($data, null, 'id');
traverseTree($kids, 0, $data);
$data = array_values($data);
}

function traverseTree($kids, $id, &$data, $level = 0){
foreach($kids[ $id ] as $sub_id){
$data[ $sub_id ]['level'] = $level;
if(isset($kids[ $sub_id ] )) traverseTree($kids, $sub_id, $data, $level + 1);
}
}

assignLevels($data);

print_r($data);

Online Demo

How to build grouped tree array from flat array in php

function buildTree(array $flat)
{
$grouped = [];
foreach ($flat as $node) {
$grouped[$node['parent_id']][] = $node;
}

$fnBuilder = function ($siblings) use (&$fnBuilder, $grouped) {
foreach ($siblings as $k => $sibling) {
$id = $sibling['id'];
if (isset($grouped[$id])) {
$sibling['children'] = $fnBuilder($grouped[$id]);
}
$siblings[$k] = $sibling;
}
return $siblings;
};
return $fnBuilder($grouped[0]);
}

$tree = buildTree($flat);

pass tree structure array to groupedTree();

$groupd = groupedTree($tree);

echo json_encode($groupd, JSON_PRETTY_PRINT);

function groupedTree($tree)
{
$groupedByFuncTableName = array_reduce($tree, function (array $accumulator, array $element) {
if (isset($element['children'])) {
$element['children'] = groupedTree($element['children']);
}
$accumulator[$element['name']][] = $element;

return $accumulator;
}, []);
return $groupedByFuncTableName;
}

How to make a list into a tree structure in PHP

$table = array();

while($row = mysqli_fetch_array($res, MYSQLI_ASSOC))
{
if ( !isset( $table[ $row["category"] ] ) ) {

// No category exists, create now
$table[ $row["category"] ] = array();
}

// append to category
$table[ $row["category"] ][] = $row["thing"];
}

// some additional sorting here
/* todo */

print_r($table);

This solution piggybacks off of PHP inherent named keys by "strings" (which behind the scenes are hashed into "buckets". Since your data only shows a data structure that nests one level deep, it is not a recursive solution. But even still, your basic requirement is on category, so will work even if you have a nested data structure.

isset will check if the category exists. If not a new sub array is created which will hold those values.

[] syntax is PHP style to append to an array.

Generate Nested Tree Structure or Hierarchy based on parent child relationship using recursion - PHP

A simple concept for faster search and to process one item only once.

  • Create a temp array with key as id and values as array of child ids. Will use this array to process next direct childs. It would be a depth first traversal.
  • Also set id as key in input array, so we can directly access whole node when required.
function generateTree($data){
$arrChild = []; // Store parent as key and its childs as array to quickly find out.
foreach($data as $obj){
$arrChild[$obj->parent_id][] = $obj->id;
$data[$obj->id] = $obj;
}

$final = [];

$setChild = function(&$array, $parents) use (&$setChild, $data, $arrChild){
foreach($parents as $parent){
$temp = $data[$parent];
// If key is set, that means given node has direct childs, so process them too.
if(isset($arrChild[$parent])){
$temp->children = [];
$setChild($temp->children, $arrChild[$parent]);
}
$array[] = $temp;
}
};
// Empty key would represent nodes with parent as `null`
$setChild($final, $arrChild['']);
return $final;
}

$final = generateTree($arr);

echo json_encode($final, JSON_PRETTY_PRINT);

Output:

[
{
"id": 1,
"navigation_id": 4,
"parent_id": null,
"text": "link 1",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 2,
"navigation_id": 4,
"parent_id": 1,
"text": "link 2",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 3,
"navigation_id": 4,
"parent_id": 2,
"text": "link 3",
"icon": "fas fa-ad",
"url": "https:\/\/google.com",
"page_id": null
},
{
"id": 4,
"navigation_id": 4,
"parent_id": 2,
"text": "link 4",
"icon": "fab fa-google",
"url": "https:\/\/google.com",
"page_id": null
}
]
}
]
},
{
"id": 5,
"navigation_id": 4,
"parent_id": null,
"text": "link 5",
"icon": "",
"url": null,
"page_id": 5,
"children": [
{
"id": 6,
"navigation_id": 4,
"parent_id": 5,
"text": "link 6",
"icon": "",
"url": null,
"page_id": 4
},
{
"id": 7,
"navigation_id": 4,
"parent_id": 5,
"text": "link 7",
"icon": "",
"url": null,
"page_id": 4
}
]
}
]

List to tree structure

I've just wrote simpler code using references instead of recurency.

Take a look my code, it contains comments saying what's happening:

<?php

$relationships = ['contact', 'property', 'contact.type1', 'contact.type2', 'contact.type1.color1', 'contact.type1.color2'];

$tree = [];
foreach($relationships as $path) {
$parts = explode('.', $path);

//represents current position in current $path
$currentNode = &$tree;

foreach($parts as $part) {
//if this part doesn't exist yet, let's create it
if(empty($currentNode[$part])) {
$currentNode[$part] = [];
}
//move forward in out $path position
$currentNode = &$currentNode[$part];
}
//after we finish a particular path, we reset the position.
unset($currentNode);
}

Working example: https://3v4l.org/o3MGP

EDIT: Here's explanation of your mistake:

As I've analyzed your code, I can see your mistake in your function getRelationTree() here:

// Has nested relationships
if (count($sections) > 0) {
$children = [];
$children[] = getRelationTree(implode('.',$sections), $children);

$tree[$main] = $children; // <==this line exactly
}

You are overriding already existing values.

So when your tree already has parsed 'contact.type1.color1' path, and you're parsing 'contact.type1.color2', you will override $tree['contact'] which already has values from 'type1.color1' with new subtree of type1.color2. And so on.



Related Topics



Leave a reply



Submit