PHP Hierarchical Array - Parents and Childs

PHP hierarchical array - Parents and childs

The suggestion by @deceze worked. However the input array needs to change a litte, like this...

$rows = array(
array(
'id' => 33,
'parent_id' => 0,
),
array(
'id' => 34,
'parent_id' => 0,
),
array(
'id' => 27,
'parent_id' => 33,
),
array(
'id' => 17,
'parent_id' => 27,
),
);

From https://stackoverflow.com/a/8587437/476:

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);

print_r( $tree );

Create unlimited nested hierarchical array of child parent company Laravel

As @rypskar suggested, you should use a recursion.

I suggest you to use anonymous function that will use itself inside of it (that is called closure) and do something like this:

 $userId = Auth::user()->id;

$getChildOf = function ($parent) use ($userId, &$getChildOf) {

$company = \App\CompanyBasic::whereRaw('user_id = ' . $userId . " AND parent_id = " . $parent)->get();
$companies = false;

if ($company->isNotEmpty()) {
$companies = array();

foreach ($company as $single_company) {
$companies[] = array(
'id' => $single_company->id,
'comp_name' => $single_company->comp_name,
'child' => $getChildOf($single_company->id),
);
echo "company id: " . $single_company->id . "<br>";
}
}

return $companies;
};

$companies = $getChildOf(0);

Here the anonymous function passed to variable $getChildOf which is used for itself.

I've removed your $count variable to simplify the code.

By the way, you should understand that this code will preform a database query on each iteration, so it will substantially increase server load

Also, injecting variables to SQL queries with concatenation is insecure and considered to be a bad practice. You should consider to use prepared statements

PHP hierarchical array - To check if child value is there or not, then assign parent properties value assign to its child array

You can use the below code:

func_x($arr);

function func_x($arr, $level = 0, $parent = '')
{
foreach($arr as $x)
{
if( empty ( $x['access_type'] ) && $parent == 'full-access' ) {
$x['access_type'] = $parent;
}
echo str_repeat("---", $level) . " [" . $x['id'] . "] -> " . $x['access_type'] . "<br>";

if(!empty($x['child']))
{
func_x($x['child'], $level+1, $x['access_type']);
}
}
}

Basically you need to pass the parent access_type to your recursive function as third parameter.

Online Demo here

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
}
]
}
]

How can I convert a hierarchical tree to parent-child relationships?

For those who are interested in my final code. It works for storing the data in the database and build it again into a hierarchical tree + a printable tree for HTML.

JavaScript code for the nestable plugin:

var nestable_update = function(e){
$(".dd-item").each(function(index){
$(this).data("id", index+1);
});

var list = e.length ? e : $(e.target),
output = list.data("output");

if (window.JSON) {
output.val(window.JSON.stringify(list.nestable("serialize")));
} else {
output.val("JSON browser support required for this demo.");
}
};

$(".dd").nestable({
maxDepth:5
}).on("change", nestable_update);

nestable_update($(".dd").data("output", $("#nestable_output")));

Database structure:

menuitem_id
menu_id
menuitem_order
menuitem_name
menuitem_page_id
parent_menuitem_id

PHP functions for building trees (format storing data in database + format getting data from database):

function create_flatten_hierarchical_tree($tree, $parent_id=0) {
$items = array();
foreach ($tree as $item) {
$items[] = array("id" => $item["id"], "parent_id" => $parent_id);
if (isset($item["children"])) $items = array_merge($items, create_flatten_hierarchical_tree($item["children"], $item["id"]));
}
return $items;
}

function create_hierarchical_tree($tree, $root=0) {
$return = array();
foreach($tree as $child => $parent) {
if($parent["parent_menuitem_id"] == $root) {
if(isset($tree[$child]["menuitem_id"]) === true){
$parent['children'] = create_hierarchical_tree($tree, $tree[$child]["menuitem_id"]);
}

unset($tree[$child]);
$return[] = $parent;
}
}
return empty($return) ? null : $return;
}

function print_hierarchical_tree($tree, $rows_pages) {
if(!is_null($tree) && count($tree) > 0) {
$return .= "<ol class='dd-list'>";
foreach($tree as $item){
$options = "";
foreach($rows_pages as $row_pages){
$selected = "";

if($row_pages["page_id"] == $item["menuitem_page_id"]){
$selected = "selected";
}

$options .= "<option value='".$row_pages["page_id"]."' $selected>".$row_pages["friendly_url"]."</option>";
}

$return .= "<li class='dd-item' data-id='".$item["menuitem_id"]."'><div class='dd-handle'>drag</div><div class='item_wrapper'><div class='item'><div class='item_title'>".$item["menuitem_name"]."</div></div><div class='item_sub'><div class='label_input'><label for='menuitem_name".$item["menuitem_id"]."'>Menuitem name</label><input type='text' id='menuitem_name".$item["menuitem_id"]."' name='menuitem_name[]' value='".$item["menuitem_name"]."' /></div><div class='label_input'><label for='page_link".$item["menuitem_id"]."'>Page link</label><label class='select'><select id='page_link".$item["menuitem_id"]."' name='menuitem_page_id[]'>".$options."</select></label></div> <a onClick='delete_menuitem(".$item["menuitem_id"].");' class='button red_bg delete'>Delete</a></div></div>";
$return .= print_hierarchical_tree($item["children"], $rows_pages);
$return .= "</li>";
}
$return .= "</ol>";
}

return empty($return) ? null : $return;
}

Core code of menu_edit.php page:

<?php
$stmt_menuitems = $dbh->prepare("SELECT * FROM inno_mw_thurs_menuitems mi WHERE mi.menu_id=:menu_id");
$stmt_menuitems->bindParam(":menu_id", $_GET["menu_id"]);
$stmt_menuitems->execute();

if (!empty($stmt_menuitems->rowCount())) {
?>
<div class="dd">
<?php
$result = $stmt_menuitems->fetchAll();
$tree = create_hierarchical_tree($result);

$stmt_pages = $dbh->prepare("SELECT * FROM inno_mw_thurs_pages");
$stmt_pages->execute();
$rows_pages = $stmt_pages->fetchAll();

$tree = print_hierarchical_tree($tree, $rows_pages);

echo $tree;
?>
</div>
<?php
}

Core code of menu_edit_process.php page:

if(isset($_POST["menu_id"])){
$menu_id = $_POST["menu_id"];
$nestable_output = json_decode($_POST["nestable_output"], true);

$parent_menuitem_ids_arr = create_flatten_hierarchical_tree($nestable_output);

$stmt = $dbh->prepare("TRUNCATE TABLE inno_mw_thurs_menuitems");
$stmt->execute();
$stmt = $dbh->prepare("INSERT INTO inno_mw_thurs_menuitems (menu_id, menuitem_order, menuitem_name, menuitem_page_id, parent_menuitem_id) VALUES (:menu_id, :menuitem_order, :menuitem_name, :menuitem_page_id, :parent_menuitem_id)");

$menuitem_order_arr = array();
foreach($_POST["menuitem_name"] as $f => $name){
$menuitem_name = $_POST["menuitem_name"][$f];
$menuitem_page_id = $_POST["menuitem_page_id"][$f];
$parent_menuitem_id = $parent_menuitem_ids_arr[$f]["parent_id"];
if(array_key_exists($parent_menuitem_id, $menuitem_order_arr) && $parent_menuitem_id != 0){
$menuitem_order_arr[$parent_menuitem_id] += 1;
}
else{
$menuitem_order_arr[$parent_menuitem_id] = 0;
}

$stmt->bindParam(":menu_id", $menu_id);
$stmt->bindParam(":menuitem_order", $menuitem_order_arr[$parent_menuitem_id]);
$stmt->bindParam(":menuitem_name", $menuitem_name);
$stmt->bindParam(":menuitem_page_id", $menuitem_page_id);
$stmt->bindParam(":parent_menuitem_id", $parent_menuitem_id);
$stmt->execute();
}

header("location: menus_list.php");
}

Please, feel free to improve this code.

Convert a series of parent-child relationships into a hierarchical tree?

This requires a very basic recursive function to parse the child/parent pairs to a tree structure and another recursive function to print it out. Only one function would suffice but here's two for clarity (a combined function can be found at the end of this answer).

First initialize the array of child/parent pairs:

$tree = array(
'H' => 'G',
'F' => 'G',
'G' => 'D',
'E' => 'D',
'A' => 'E',
'B' => 'C',
'C' => 'E',
'D' => null
);

Then the function that parses that array into a hierarchical tree structure:

function parseTree($tree, $root = null) {
$return = array();
# Traverse the tree and search for direct children of the root
foreach($tree as $child => $parent) {
# A direct child is found
if($parent == $root) {
# Remove item from tree (we don't need to traverse this again)
unset($tree[$child]);
# Append the child into result array and parse its children
$return[] = array(
'name' => $child,
'children' => parseTree($tree, $child)
);
}
}
return empty($return) ? null : $return;
}

And a function that traverses that tree to print out an unordered list:

function printTree($tree) {
if(!is_null($tree) && count($tree) > 0) {
echo '<ul>';
foreach($tree as $node) {
echo '<li>'.$node['name'];
printTree($node['children']);
echo '</li>';
}
echo '</ul>';
}
}

And the actual usage:

$result = parseTree($tree);
printTree($result);

Here's the contents of $result:

Array(
[0] => Array(
[name] => D
[children] => Array(
[0] => Array(
[name] => G
[children] => Array(
[0] => Array(
[name] => H
[children] => NULL
)
[1] => Array(
[name] => F
[children] => NULL
)
)
)
[1] => Array(
[name] => E
[children] => Array(
[0] => Array(
[name] => A
[children] => NULL
)
[1] => Array(
[name] => C
[children] => Array(
[0] => Array(
[name] => B
[children] => NULL
)
)
)
)
)
)
)
)

If you want a bit more efficiency, you can combine those functions into one and reduce the number of iterations made:

function parseAndPrintTree($root, $tree) {
$return = array();
if(!is_null($tree) && count($tree) > 0) {
echo '<ul>';
foreach($tree as $child => $parent) {
if($parent == $root) {
unset($tree[$child]);
echo '<li>'.$child;
parseAndPrintTree($child, $tree);
echo '</li>';
}
}
echo '</ul>';
}
}

You'll only save 8 iterations on a dataset as small as this but on bigger sets it could make a difference.

Display hierarchical Multidimensional Array with parent child numbering

You could use simple recursive function:

function makeOneLevelArray($in, &$out, $prefixLevel = '') {
$level = 1;
foreach ($in as $v) {
$out[] = $prefixLevel . $level . ' ' . $v['name'];
if (!empty($v['children'])) {
makeOneLevelArray($v['children'], $out, $prefixLevel . $level . '.');
}
$level++;
}
}

$outputArray = array();
makeOneLevelArray($inputArray, $outputArray);
print_r($outputArray);

demo



Related Topics



Leave a reply



Submit