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:
- Take the array of all elements and the id of the current parent
(initially 0/nothing/null/whatever). - Loop through all elements.
- 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). - 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. - 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
andvalues 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
Manipulate a Url String by Adding Get Parameters
PHP - Import CSV File to MySQL Database Using Load Data Infile
How to Make Strings "Xml Safe"
Debug a Domdocument Object in PHP
Convert a String into an Array of Characters
$_Files Field 'Tmp_Name' Has No Value on .Jpg File Extension
What Is a Good Parser Generator for PHP
Parse JSON String Contents into PHP Array
Symfony 2 Entitymanager Injection in Service
How to Set Time Zone in Codeigniter
How to Create Installer for Website. PHP MySQL
How to Remove the Url Protocol and Slash from User Input in PHP
Allowed Memory Size of 262144 Bytes Exhausted (Tried to Allocate 24576 Bytes)
Find Number Which Is Greater Than or Equal to N in an Array