Iskren
Iskren

Reputation: 137

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

I've been stuck in this since yesterday any help will be much appreciated. The situation is this: I have a table for navigation links with the following structure, id | parent_id | text

In my scenario the parent_id may be null(no sub-menu) or any id pointing back to the table which will make the record a sub-menu of the parent. I've tried many different ways to execute this but with no luck, the closest scenario I came up with is the following,

// All navigation link results are stored in $data
public static function transform(array &$data) {
    $result = [];
    $search = function(&$array = [], $parent = null) use (&$search, &$result){
        foreach($array as $key => $value) {
            /** If the element has an ancestor */
            if($parent && $parent->id == $value->parent_id) {
                $next = $parent->children[] = $value;
                $search($array, $next);
                unset($array[$key]);
            }
        }

        return $parent;
    };

    foreach($data as $val) {
        $result[] = $search($data, $val);
    }
    return $result;
}

The function works fine and tracks all the ancestors of a certain link but in the next iteration is tracking all the ancestors of the next element(even if it was a child of the previous one) so it comes back with a lot of duplicated data. I came up with the solution to unset the iterated element from the array but for some reason, the element is still being used in the next iteration. Here is my current data

Here is my current result

Upvotes: 3

Views: 2240

Answers (1)

Dark Knight
Dark Knight

Reputation: 6531

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

Upvotes: 3

Related Questions