php: Menu Classes

January 12, 2009 in PHP

Recently I’ve been working on my own framework (we’ve all done it i’m sure) and part of this was a menu handling system.

The basis really is simple, we have a menu item which really is just link and text. That menu item could have children, could be active and/or be selected.

In this case Selected is the currently menu item, which is also Active, and all it’s parents in the chain upwards are also active.

We also have a menu controller which makes it easier to add to the menus.

class menuHandler {
    public $menus=array();
    public function NewMenu() {
        $k = time();
        $this->menus[$k] = new menu_class();
        return $k;
    }
    public function AddMenu($key,menu_class $menu) {
        if (!isset($this->menus[$key])) {
            $this->menus[$key] = $menu;
        }
        return $this;
    }
    public function ItemCount($menuKey) {
        $menu = $this->menus[$menuKey];
        if (!($menu instanceof menu_class)) {
            return -1;
        }
        else {
            return count($menu->children);
        }
    }
    public function AddItem($menuKey,$itemKey=null,menu_class $item) {
        if (isset($this->menus[$menuKey])) {
            $menu = $this->menus[$menuKey];
            if (!isset($itemKey) or (is_numeric($itemKey) &amp;amp;amp;&amp;amp;amp; $itemKey <0)) {
                $itemKey = count($menu->children);
            }
            $menu->AddSubItem($item,$itemKey,$menuKey);
        }
        return $this;
    }
    public function SetActive($id,$from=null) {
        $menu = new menu_class();
        if ($from == null) {
            $from = $this->menus;
        }
        elseif($from instanceof menu_class) {
            $from = $from->children;
        }
        foreach ($from as $key=>&amp;amp;amp;$menu) {
            if ($key==$id) {
                $menu->active=true;
                return true;
            }
            elseif(count($menu->children) !=0) {
                $r = $this->SetActive($id,$menu->children);
                if ($r) {
                    $menu->expanded = true;
                    return $r;
                }
            }
        }
    }
}
class menu_class {
    public $children = array();
    public $myParent=null;
    public $active = false;
    public $expanded = false;
    public $link ='';
    public $text = '';
    public $additional = array();

    public function AddSubItem($item,$ID=null,$parentID=null) {
        if ($item instanceof menu_class) {
        }
        elseif(is_array($item)) {
            $item = new menu_class();
            $item->text = $details[0];
            $item->link = $details[1];
        }
        if (isset($ID)) {
            if (isset($this->children[$ID])) {
                if (is_numeric($ID)) {
                    $this->children = array_insert($this->children,$item,$ID);
                }
            }
            else {
                $this->children[$ID] = $item;
            }
        }
        else {
            $this->children[] = $item;
        }
        if (!isset($parentID)) {
            $parentID = "main";
        }
        $this->myParent = $parentID;
        ksort($this->children);
    }

    public function __construct($text=null,$link=null) {
        if(isset($text) &amp;amp;amp;&amp;amp;amp; !is_null($text)) {
            $this->text = $text;
        }
        if(isset($link) &amp;amp;amp;&amp;amp;amp; !is_null($link)) {
            $this->link = $link;
        }
    } 

    public function Format($text) {
        if (!empty($this->link)) {
            $text= str_replace("%href%",$this->link);
        }
        if (!empty($this->text)) {
            $text = str_replace("%text%",$this->text);
        }
        return $text;
    }
}

You will notice that i’ve used the array_insert function from my previous posting. This was why it was created, so that i could insert without worry that i would overwrite the code

here’s an example of using the code

$menus = new menuHandler();
$menus->AddMenu("main",new menu_class());
 $menus->AddItem("main",-1,new menu_class("TEXT","link.php"));
 $menus->AddItem("main",-1,new menu_class("TEXT2","link2.php"));
//we want this item to be 1st!
 $menus->AddItem("main",0,new menu_class("1st Link","homelink.php"));

So far I pass the $menus out to smarty in the usual assignment method and process like as follows

 {foreach from=$menus.main->children item="mItem"}
  <div class="mainbutton"><a href="{$mItem->link}">{$mItem->text}</a></div>
 {/foreach}

For a cascaded menu i’ve used the following

{foreach from=$menus.cats->children item='cat'}
  <div class="item{if $cat->active || $cat->expanded}_select{/if}">
    <div class="arrow"></div>
    {if !$cat->active}<a href="{$cat->link}">{/if}{$cat->text}{if !$cat->active}</a>
    {/if}
  </div>
  {if $cat->children|@count ne 0 || $cat->expanded}
    {foreach from=$cat->children item='subCat'}
      <div class="subitem{if $subCat->active}_select{/if}">
        <div class="arrow"></div>
      {if !$subCat->active}<a href="{$subCat->link}">{/if}{$subCat->text}{if !$subCat->active}</a>{/if}
      </div>
    {/foreach}
  {/if}
  {/foreach}

As usual any comments gratefully recieved