21 Jul

Building a tree of links with arrays

My CMS‘s admin area has a lot of customisable features, so the navigation needs to be flexible.

Recently, the number of features grew to the point that I needed to implement a hierarchical menu structure, instead of the simple linear one that I had been using.

The script that I use (magicmenu – visible in action here) is an unobtrusive JavaScript which takes an list-tree of links, and converts it dynamically into drop-down menus.

The difficulty is in defining the tree, yet letting your validation scripts do this flexibly. Up until this point, I had drawn out the links immediately upon validating that the user had access to the link, but this is very difficult to do in a hierarchical manner

For example, consider this simple list:

Let’s say you have a user who does not have rights to edit user accounts in any way. In that case, a linear method would draw the Users tree root, but it would not have any leaf nodes:

Ideally, the Users root should not appear. By building the tree in an array beforehand, we can avoid that. Here is a bit of code that builds the above in an array format:

function addMenuItem(&$arr,$file,$nav){
  if(ereg('>',$nav)){
    $bits=explode(' > ',$nav);
    if(!isset($arr[$bits[0]]))$arr[$bits[0]]=array();
    addMenuItem($arr[$bits[0]],$file,str_replace($bits[0].' > ','',$nav));
  }else{
    $arr[$nav]=$file;
  }
}
$arrayMenu=array();
addMenuItem($menuArray,'pages.php','Pages');
addMenuItem($menuArray,'user-accounts.php','Users > user accounts');
addMenuItem($menuArray,'user-groups.php','Users > user groups');
addMenuItem($menuArray,'user-admins.php','Users > admins');
addMenuItem($menuArray,'help.php','Help');

With the above, you do not need to pre-create the Users root – just tell the function you want to create the sub-node, and it will do it for you.

To draw that out, you need one more function, and a line of code:

function drawMenu($menuArray){
  $c='';
  foreach($menuArray as $name=>$item){
    $c.='<li>';
    if(is_array($item)){
      $c.='<a href="#">'.htmlspecialchars($name).'</a>';
      $c.='<ul>'.drawMenu($item).'</ul>';
    }else{
      $c.='<a href="'.$item.'">'.htmlspecialchars($name).'</a>';
    }
    $c.='</li>';
  }
  return $c;
}
echo '<ul class="magicmenutop">'.drawMenu($menuArray).'</ul>';

This code will build a compact tree-list with just the bits that are required.