Search code examples
phpdynamicmenunestedhierarchical

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


At the moment, i'm creating a dynamic menu for my own CMS (practising) in PHP, but I don't know to save the data in my database.

Database structure:

menuitem_id
menu_id
menuitem_order
menuitem_name
menuitem_page_id
parent_menuitem_id

I do get this output as a hierarchical tree, but that isn't the desired format for storing it into my database:

Array
(
    [0] => Array
        (
            [id] => 2
        )

    [1] => Array
        (
            [id] => 1
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 4
                                        )

                                    [1] => Array
                                        (
                                            [id] => 5
                                        )

                                )

                        )

                    [1] => Array
                        (
                            [id] => 6
                        )

                )

        )

)

However, I want to convert this to a parent ID array with new fresh ID's (I will truncate the table and insert new data). Something like this:

Array
(
    [0] => 0
    [1] => 0
    [2] => 2
    [3] => 3
    [4] => 3
    [5] => 2
)

How can this be done?

Note: i have read this article, but I need the opposite code of it.


Solution

  • 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.