1

In working on a dynamic menu w/ CRUD I have been having some trouble with the finishing touches. It works, but there are some extra tags getting inserted where there shouldn't be and can't figure out how to clean it up before I share it with the world. I used this as a starting point, then changed it to work with an accordion menu (http://www.phpro.org/tutorials/Simple-Mysql-PHP-Menu.html).

Below is the data from the table (I changed the names on the first 2 fields to get it to fit in the SO format from menu_item_id to id, and from menu_parent_id to pid).

id  pid    menu_item_name         menu_url  sortorder status 
1      0    Settings                            0   ACTIVE
2      5    Grid Demo            grid.php           ACTIVE
3      5    setGridOptions       gridoptions.php    ACTIVE
4      1    Menu Items           adminmenu.php  1   ACTIVE
5      0    Grid Settings                       100 ACTIVE
6      1    General Settings     settings.php   100 ACTIVE

Here is the PHP that connects to the mysql database and creates the hierarchical tree array to make it work:

include 'db.php';
$sql = "SELECT * FROM menu_items WHERE status = \"ACTIVE\" ORDER BY sortorder, menu_item_name";
$query = $db->query($sql);


while($data = $query->fetch(PDO::FETCH_ASSOC))
   // loop over the results
    {
        // Assign by reference
        $thisref = &$refs[ $data['menu_item_id'] ];

        // add the the menu parent
        $thisref['menu_item_id'] = $data['menu_item_id'];
        $thisref['menu_parent_id'] = $data['menu_parent_id'];
        $thisref['menu_item_name'] = $data['menu_item_name'];
        $thisref['menu_url'] = $data['menu_url'];

        // if there is no parent id
        if ($data['menu_parent_id'] == 0)
        {
            $list[ $data['menu_item_id'] ] = &$thisref;
        }
        else
        {
            $refs[ $data['menu_parent_id'] ]['children'][ $data['menu_item_id'] ] = &$thisref;
        }
    }

    function create_list( $arr )
    {
        $html = "";
        foreach ($arr as $key=>$v) 
        {
            if ($v['menu_parent_id'] == '0')
            {
             $html .= '<a class="menuitem submenuheader" href="'. $v['menu_url'] .'">'.$v['menu_item_name']."</a>\n";
             $html .= "<div class=\"submenu\">\n<ul>\n";
             $html .= "<li>" . create_list($v['children']) . "</li>";
             $html .= "</ul>\n";
            }
            else{
             $html .= '<li><a id="' . $v['menu_item_id'] . '">'.$v['menu_item_name']."</a></li>\n";
                }
            }
        $html .= "</div>\n";
        return $html;
    }
echo "<div class=\"glossymenu\">";
    echo create_list( $list );
echo "</div>";

When I run it, it outputs the following:

<div class="glossymenu"><a class="menuitem submenuheader">Settings</a>
<div class="submenu">
<ul>
<li><li><a id="4">Menu Items</a></li>
<li><a id="6">General Settings</a></li>
</div>
</li></ul>
<a class="menuitem submenuheader" href="">Grid Settings</a>
<div class="submenu">
<ul>
<li><li><a id="2">Grid Demo</a></li>
<li><a id="3">setGridOptions</a></li>
</div>
</li></ul>
</div>
</div>  

As you can see there are extra <li> tags, the </ul> is in the wrong spot (should be after the </div>) Other than that, it is working great.

The other thing I can't figure out is if I have a root menu item with no children, I would love it to have a different output like

<a id="8">No Children Menu Item</a>
Instead of:
<a class="menuitem submenuheader">No Children Menu Item</a>

The second example would create the make it show up the little (+/-) for expanding and contracting and wouldn't allow me to click on it. For clarification on the <a> tags, I am using javascript to do a .get() based off the id which is why there is no href or url shown.

UPDATE

It is working correctly and I posted it on Github for anyone that wants it. https://github.com/ajhalls/php-accordian-menu

3
  • When you start new projects in the future, strongly consider using MVC. If you choose a good MVC framework to separate your display code from your logic, you will find that troubleshooting such problems becomes markedly easier. Commented May 1, 2014 at 21:22
  • You use create_list() recursively. If you call create_list() with some value and you end up at line --- $html .= "<li>" . create_list($v['children']) . "</li>"; --- but $v['children'] is empty it will insert </div> resulting in: ... <li></div>. Is the above output the actual output or the output interpreted by your browser? Commented May 1, 2014 at 21:23
  • @m02ph3u5 thanks, what I posted was from firefox view source. Commented May 1, 2014 at 21:36

1 Answer 1

1

Try this :

<?php
include 'db.php';

$sql = "SELECT * FROM menu_items WHERE status = 'ACTIVE' ORDER BY pid ASC, sortorder ASC, menu_item_name ASC";
$query = $db->query($sql);

$menu_items = array();

while($data = $query->fetch(PDO::FETCH_ASSOC)) {
    if($data['pid'] == 0) {
        $menu_items[$data['id']] = array();
        $menu_items[$data['id']]['id'] = $data['id'];
        $menu_items[$data['id']]['name'] = $data['menu_item_name'];
        $menu_items[$data['id']]['url'] = $data['menu_url'];
        $menu_items[$data['id']]['children'] = array();
    } else if($data['pid'] != 0) {
        $tmp = array();
        $tmp['id'] = $data['id'];
        $tmp['name'] = $data['menu_item_name'];
        $tmp['url'] = $data['menu_url'];

        array_push($menu_items[$data['pid']]['children'],$tmp);
        unset($tmp);
    }
}

function create_list($arr)
{
    $html = "";
    foreach($arr as $key => $value) {
        if(count($value['children']) > 0) {
            $html .= '  <a class="menuitem submenuheader" href="'. $value['url'] .'">'.$value['name'].'</a>
                        <div class="submenu">
                        <ul>';

            foreach($value['children'] AS $child) {
                $html .= '  <li>
                                <a id="'.$child['id'].'">'.$child['name'].'</a>
                            </li>';
            }

            $html .= '  </ul>
                        </div>';
        } else{
            $html .= '  <a id="'.$value['id'].'">'.$value['name'].'</a>';
        }
    }

    return $html;
}

echo "<div class=\"glossymenu\">";
    echo create_list($menu_items);
echo "</div>";
?>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.