简体   繁体   中英

Recursively creating a multi-dimensional array in PHP

I am trying to figure out the best way to write a PHP function that will recursively build a multi-dimensional array with an unknown number of sublevels from a mysql table. Its purpose is to create a data structure which can be looped through to create a navigation menu on a website, with each menu item possibly having a submenu with child menu items.

The fields of note in the table are:
int ItemID
int ParentID
varchar ItemText
text ItemLink
tinyint HasChildren

So an example of a returned array from the function would be:

$menuItems = 
    array(
        itemID# => 
            array(
                'ItemText' => 'Home',
                'ItemLink' => 'index.php',
                'Children' => array(
                        itemID# => array (
                            'ItemText' => 'Home Sub 1',
                            'ItemLink' => 'somepage.php',
                            'Children' => 0
                        ),
                        itemID# => array (
                            'ItemText' => 'Home Sub 2',
                            'ItemLink' => 'somepage2.php',
                            'Children' => 0
                        ),
                    )
            ),
        itemID# => 
            array(
                'ItemText' => 'Contact',
                'ItemLink' => 'contact.php',
                'Children' => 0
            )
        )
    );

Would greatly appreciate if someone could point me in the right direction to accomplish this. Thanks!

Not too hard. What you do is store the menu items in an array where you can look them up by ID. Then you iterate over the menu items and if they have a non-null ParentID you add them to their parent's list of children. Then you remove all the children from the master list so you have only top-level items left.

Code:

<?php
$menuItems = array
(
    1 => array
    (
        'ItemText' => 'Home',
        'ItemLink' => 'index.php',
        'ParentID' => null,
    ),

    2 => array
    (
        'ItemText' => 'Home Sub 1',
        'ItemLink' => 'somepage.php',
        'ParentID' => 1,
    ),

    3 => array
    (
        'ItemText' => 'Home Sub 2',
        'ItemLink' => 'somepage2.php',
        'ParentID' => 1,
    ),

    4 => array
    (
        'ItemText' => 'Contact',
        'ItemLink' => 'contact.php',
        'ParentID' => null,
    ),
);

// Each node starts with 0 children
foreach ($menuItems as &$menuItem)
    $menuItem['Children'] = array();

// If menu item has ParentID, add it to parent's Children array    
foreach ($menuItems as $ID => &$menuItem)
{
    if ($menuItem['ParentID'] != null)
        $menuItems[$menuItem['ParentID']]['Children'][$ID] = &$menuItem;
}

// Remove children from $menuItems so only top level items remain
foreach (array_keys($menuItems) as $ID)
{
    if ($menuItems[$ID]['ParentID'] != null)
        unset($menuItems[$ID]);
}

print_r($menuItems);
?>

Output:

Array
(
    [1] => Array
        (
            [ItemText] => Home
            [ItemLink] => index.php
            [ParentID] => 
            [Children] => Array
                (
                    [2] => Array
                        (
                            [ItemText] => Home Sub 1
                            [ItemLink] => somepage.php
                            [ParentID] => 1
                            [Children] => Array
                                (
                                )

                        )

                    [3] => Array
                        (
                            [ItemText] => Home Sub 2
                            [ItemLink] => somepage2.php
                            [ParentID] => 1
                            [Children] => Array
                                (
                                )

                        )

                )

        )

    [4] => Array
        (
            [ItemText] => Contact
            [ItemLink] => contact.php
            [ParentID] => 
            [Children] => Array
                (
                )

        )

)

Have a function that calls itself every time it gets an array element. As in:

Your function is called to display a node. Then it checks if the node its calling from has a sub menu, and if does, it calls itself again. And the process repeats until it dies out, and all the previous function calls return.

void printData($mysql_table_node){
    if($mysql_table_node.has_node()){
        for($i = 0; $i < $mysqql_table_node.num_nodes()){
            printData($mysql_table_node->own_node);
        }
    }
        return;
}

Multi-dimensional tree and an unordered HTML list generator

  1. Recursively build a tree from a multi-dimensional array.
  2. Generate a multi-dimensional HTML list code, from the tree (1).

You can't never know how many dimensions is in the given list of items. Each element can have a son->Grandson->Great grandson an so on.

So, you must use a recursive function to generate a multi-dimensional list.

Here is the code:

<?php

$categories = array(
    '1'=>   array('name'=>'one','parent'=>null),
    '2'=>   array('name'=>'two','parent'=>null),
    '20'=>  array('name'=>'twenty','parent'=>'2'),
    '21'=>  array('name'=>'twenty one','parent'=>'2'),
    '210'=> array('name'=>'two hundred and ten',    'parent'=>'21'),
    '211'=> array('name'=>'two hundred and eleven', 'parent'=>'21'),
    '212'=> array('name'=>'two hundred and twelve', 'parent'=>'21')
);

$tree=Menu::CreateTree($categories);
print_r($tree);
Menu::GenerateMenuHtmlCode($tree);

class Menu
{   
    public static function GenerateMenuHtmlCode($tree)
    {
        echo '<ul>';
        foreach ($tree as $key=>$value)
        {
             echo "<li>".$value['name'];
             if(!empty($value['sons'])) 
                 self::GenerateMenuHtmlCode($value['sons']);
             echo "</li>";
        }
        echo '</ul>';
    }

    public static function CreateTree($categories)
    {
        $tree=array();
        self::AddElement(&$categories,&$tree,null);
        return $tree;
    }

    private function AddElement($categories,&$tree,$parent)
    {
        foreach ($categories as $key=>$value)
        {
            if($value['parent']==$parent)
            {
                $tree[$key]=$categories[$key];
                $tree[$key]['sons']=array();
                self::AddElement($categories,&$tree[$key]['sons'],$key);
            }
            if(empty($tree['sons'])) unset ($tree['sons']);
        }
        unset($categories[$parent]);
        return ;
    }
}
?>

The result:

Array
(
    [1] => Array
        (
            [name] => one
            [parent] => 
            [sons] => Array()
        )

    [2] => Array
        (
            [name] => two
            [parent] => 
            [sons] => Array
                (
                    [20] => Array
                        (
                            [name] => twenty
                            [parent] => 2
                            [sons] => Array()
                        )

                    [21] => Array
                        (
                            [name] => twenty one
                            [parent] => 2
                            [sons] => Array
                                (
                                    [210] => Array
                                        (
                                            [name] => two hundred and ten
                                            [parent] => 21
                                            [sons] => Array()
                                        )

                                    [211] => Array
                                        (
                                            [name] => two hundred and eleven
                                            [parent] => 21
                                            [sons] => Array()
                                        )

                                    [212] => Array
                                        (
                                            [name] => two hundred and twelve
                                            [parent] => 21
                                            [sons] => Array()
                                        )
                                )
                        )
                )
        )
)

and:

<ul>
    <li>one</li>
    <li>two
        <ul>
            <li>twenty</li>
            <li>twenty one
                <ul>
                    <li>two hundred and ten</li>
                    <li>two hundred and eleven</li>
                    <li>two hundred and twelve</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

I know this is a late reply but I found this script above and it was fantastic. I ran into an issue though with it unsetting my children because of the way the ItemID was working, when I ran it with a similarly designed table to the OP. To get around this, and given the amount of RAM in most web servers should be able to handle this, I've taken John Kugelman's great example and modified it slightly. Instead of having to apply children to all of the items and then unsetting them after, I create a new array and build it all in one

Code:

$new_array = array();
foreach ($menuItems as $key => &$menuItem) {
    if (($menuItem['ParentID'] != NULL) && ($menuItem['ParentID'] != '')) {
        $new_array[$menuItem['ParentID']]['Children'][$menuItem['ItemID']] = &$menuItem;
    } else {
        $new_array[$menuItem['ItemID']] = &$menuItem;
    }
}

print_r($new_array);

Hope this helps someone else because the above certainly helped me

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM