Hierarchical Menu

I've been coding on experts exchange, testing myself. Here's some relevant code for one of the answers.

They're classes that have been used to generate hierarchical menus.


<?php
/**
 * @package menu
 * @author John Kawakami 
 * @link http://www.slaptech.net Developed by The Slaptech Collective
 * @copyright 2006 Public Domain
 * @version $Id: Menu.class.php 97 2006-07-27 10:43:40Z johnk $
 *
 * Library to help create hierarchical menus.
 * Use MenuRenderer to display the menus.
 *
 * 
 * $menuBar = new Menu('MenuBar');
 * $fileMenu = $menuBar->addMenu( 'File' );
 * $fileMenu->addUrl( 'Open', '/admin/foo.php' );
 * $fileMenu->addUrl( 'Save', '/admin/bar.php' );
 * $fileMenu->addDivider();
 * $fileMenu->addUrl( 'Close', '/admin/baz.php' );
 * 
 */

include_once('ACL.class.php');
 
class Menu
{
	var $acl;
	var $menu;
	var $menuHash; //fixme - combine these two structures into one
	var $name;
	var $description;
	var $icon;
	var $url;
	var $highlight;

	function Menu( &$acl, $name, $description='', $icon='', $url='' )
	{
		$this->acl			=& $acl;
		$this->menu 		= array();
		$this->name 		=& $name;
		$this->description 	=& $description;
		$this->icon 		=& $icon;
		$this->url 			=& $url;
		$this->highlight	= false;
	}
	function addObject( &$obj )
	{
		array_push( $this->menu, &$obj );
	}
	function addNamedObject( $name, &$obj )
	{
		$this->menuHash[$name] =& $obj;
		$this->addObject( &$obj );
	}
	/**
	 * Utility function.
	 */
	function &addMenu( &$acl, $name, $description='', $icon='', $url='' )
	{
		$new = new Menu( &$acl, $name, $description, $icon, $url );
		$this->addObject( $new );
		return $new;
	}
	function &getNamedObject( $name )
	{
		return $this->menuHash[$name];
	}
	function &addUrl( &$acl, $name, $url, $description='', $popup='' )
	{
		$new = new UrlMenuItem( &$acl, $name, $url, $description, $popup );
		$this->addObject( $new );
		return $new;
	}
	function &addDivider()
	{
		$new = new DividerMenuItem();
		$this->addObject( $new );
		return $new;
	}
	function select( $name )
	{
		for( $i=0; $i < count($this->menu); $i++ )
		{
			if ($this->menu[$i]->name==$name)
			{
				return $this->menu[$i];
			}
		}
	}
	function rewind()
	{
		reset($this->menu);
	}
	function &next()
	{
		// check the acl to see if this user can see the next item - fixme
		list( $index, $obj ) = each( $this->menu );
		return $obj;
	}
	function disable()
	{
		$this->enabled = false;
	}
	function enable()
	{
		$this->enabled = true;
	}
	function highlight()
	{
		$this->highlight = true;
	}
}

class UrlMenuItem extends Menu
{
	var $name;
	var $url;
	var $description;
	var $popup;

	function &UrlMenuItem( &$acl, $name, $url, $description='', $popup='' )
	{
		$this->acl			=& $acl;
		$this->name 		=& $name;
		$this->url 			=& $url;
		$this->description 	=& $description;
		$this->popup 		=& $popup;
		return $this;
	}

}
class DividerMenuItem extends Menu
{
	function DividerMenuItem()
	{
		$this->name = 'divider';
	}
}


?>


The menus are rendered via another class:


<?php
/**
 * @package menu
 * @author John Kawakami 
 * @link http://www.slaptech.net Developed by The Slaptech Collective
 * @copyright 2006 Public Domain
 * @version $Id: MenuRenderer.class.php 92 2006-07-27 09:13:10Z johnk $
 *
 * Rendering classes for menus.  These are kind of like "templates"
 * for menus, except they can render hierarchies, portions of menus,
 * etc.  There's HTML code in here.  Yuk.
 */

/**
 * Rendering interface.  Each renderer must implement the
 * url, divider, and menu methods.  The menu() method is the heart
 * of the renderer.
 */
class MenuRenderer
{
	function MenuRenderer() {}
	function divider() {}
	function url() {}
	function menu() {}
	function item( $obj ) 
	{
		return $obj->name;
	}
}

/**
 * Renders a menu as an html unordered list (UL).
 */
class HtmlMenuRenderer extends MenuRenderer
{
	function HtmlMenuRenderer() {}

	function menu( &$menu, $indent=0 )
	{
		$t = str_repeat( ' ', $indent );
		$m =& $menu;
		$m->rewind();
		if ($m->name != '@')
			$output .= "$t
  • "; if ($m->name != '@') { if ($m->url) $output .= "url'>"; if ($m->icon) { $output .= "icon' border='0' />"; $output .= "
    "; } $output .= $m->name."\n"; if ($m->url) $output .= '
    '; } $output .= "$t
      \n"; while( $obj =& $m->next() ) { $class = strtolower(get_class($obj)); // echo "$class
      "; switch( $class ) { case 'apmenu': case 'menu': $output .= $this->menu( $obj, $indent+4 ); break; case 'urlmenuitem': $output .= $this->url( $obj, $indent+4 ); break; case 'dividermenuitem': $output .= $this->divider( $obj, $indent+4 ); break; default: $output .= $this->item( $obj, $indent+4 ); break; } } $output .= "$t
    \n"; if ($m->name != '@') $output .= "$t
  • \n"; return $output; } function divider( &$obj, $indent=0 ) { $t = str_repeat( ' ', $indent ); return "$t
  • -----------
  • \n"; } function url( &$obj, $indent=0 ) { $t = str_repeat( ' ', $indent ); return "$t
  • url'>$obj->name
  • \n"; } }

    Adjacency Table to Hierarchy

    Here's a way to turn an adjacency table into a hierarchy in html.


    <?php
    $menu = array();
    $menu[] = array( 'id' => 1, 'parent_id' => 0, 'name' => 'a' );
    $menu[] = array( 'id' => 2, 'parent_id' => 1, 'name' => 'a.1' );
    $menu[] = array( 'id' => 3, 'parent_id' => 0, 'name' => 'b' );
    $menu[] = array( 'id' => 4, 'parent_id' => 3, 'name' => 'b.1' );
    $menu[] = array( 'id' => 5, 'parent_id' => 3, 'name' => 'b.2' );
    $menu[] = array( 'id' => 6, 'parent_id' => 5, 'name' => 'b.2.1' );

    // acts like a SELECT statement
    function selectWhereParentIdIs( $id )
    {
    global $menu;
    $out = array();

    for( $i=0; $i 0 )
    {
    $out .= '

      ';
      reset( $ar );
      foreach( $ar as $value )
      {
      $out .= '
    1. '.$value['name'];
      $out .= menuToHtml( $value['id'] );
      $out .= '
    2. ';
      }
      $out .= '

    ';
    return $out;
    }
    else
    {
    return '';
    }

    }

    echo menuToHtml( 0 );

    ?>

    And here's a select version


    <?php
    $menu = array();
    $menu[] = array( 'id' => 1, 'parent_id' => 0, 'name' => 'a' );
    $menu[] = array( 'id' => 2, 'parent_id' => 1, 'name' => 'a.1' );
    $menu[] = array( 'id' => 3, 'parent_id' => 0, 'name' => 'b' );
    $menu[] = array( 'id' => 4, 'parent_id' => 3, 'name' => 'b.1' );
    $menu[] = array( 'id' => 5, 'parent_id' => 3, 'name' => 'b.2' );
    $menu[] = array( 'id' => 6, 'parent_id' => 5, 'name' => 'b.2.1' );

    // acts like a SELECT statement
    function selectWhereParentIdIs( $id )
    {
    global $menu;
    $out = array();

    for( $i=0; $i 0 )
    {
    reset( $ar );
    foreach( $ar as $value )
    {
    $out .= '';
    if ($depth>0) $out .= '-';
    $out .= str_repeat( '-', $depth ) . $value['name'];
    $out .= '';
    $out .= menuToSelect( $value['id'], $depth+1 );
    }
    return $out;
    }
    else
    {
    return '';
    }

    }

    echo '';
    echo menuToSelect( 0, 0 );
    echo '';

    ?>