Multi-level menu in PHP – how to do it without killing yourself or the database in the process

Why don’t you take a look at this newer tutorial about creating a multi-level menu in PHP. It also has a Bootstrap Framework implementation 🙂

Well. It’s been a long time since I’ve written a post. I was busy learning Laravel… Currently, I am trying the framework by working on a CMS, and, again, I have arrived at the phase of menus. And by “menus”, I am refering to multi-level menus. In the past I have found ways to bypass this dilemma, but now my time allowed me to dive into it.

You will find a lot of answers about how to do it, best of which can be found on stackoverflow (http://stackoverflow.com/questions/2871861/how-to-build-unlimited-level-of-menu-through-php-and-mysql), but they seemed way too complicated (at least, for me), or they were killing the database by querying the table for each item (http://www.100scripts.com/article/35/PHP-Multi-Level-Menu.html).

So, how do you retrieve a multi-level menu from the database using PHP? I will not teach you how to connect to a database nor how to select from it, this not being the subject of this post.

I will assume that you will have a table that has at least the following columns: id, name, url, parent_id.

When you retrieve the menu items from the database, let’s presume the array will have the following structure:

$menu_items = array(
array(‘id’=>…, ‘parent_id’=>…, ‘name’=>…, ‘url’=>…),
array(‘id’=>…, ‘parent_id’=>…, ‘name’=>…, ‘url’=>…),
………..
);

Hope you get the idea…

In order to work with the array we will use a recursive function (a function that calls on itself) on the array and return the result.

Now, how do we build the multilevel menu?

I have it in two flavours:

1. A multi-dimensional array (if you’re working with a framework it would be nice to send the menu to the view as an array…)

function ordered_menu($array,$parent_id = 0)
{
  $temp_array = array();
  foreach($array as $element)
  {
    if($element['parent_id']==$parent_id)
    {
      $element['subs'] = ordered_menu($array,$element['id']);
      $temp_array[] = $element;
    }
  }
  return $temp_array;
}

2. An <ul> list with sub-lists

function html_ordered_menu($array,$parent_id = 0)
{
  $menu_html = '<ul>';
  foreach($array as $element)
  {
    if($element['parent_id']==$parent_id)
    {
      $menu_html .= '<li><a href="'.$element['url'].'">'.$element['name'].'</a>';
      $menu_html .= ordered_menu($array,$element['id']);
      $menu_html .= '</li>';
    }
  }
  $menu_html .= '</ul>';
  return $menu_html;
}

As you can see, the function will receive an array and a parent id. Assuming you’ve set value 0 to the menu items that are on the first level, you can call the function with:

$array_menu_items = ordered_menu($menu_items,0); // for retrieving an array
$html_menu_items = html_ordered_menu($menu_items,0); // for retrieving a multi-list

From here on, the function should do its job, and return a nice ordered menu. Hope you liked it.

13 comments

    1. First of all… If you put it inside MY_Model, you need to pass the get_menu() function properties and not variables. Also, why do you have NULL as value for root elements? Why not 0? More on this, you can find in my comments on your gist.

    1. Well… you can either use the CSS pseudo-element :active (…a:active {color: red;}…) or you can look inside the url of the link to see if the current page is the same as the link.

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.