You are on page 1of 33

Building an Extensible Menu Class

By Team Melonfire
This article copyright Melonfire 20002002. All rights reserved.

Building an Extensible Menu Class

Table of Contents
The Horns Of A Dilemma..................................................................................................................................1 Back To Class......................................................................................................................................................2 What's On The Menu?.......................................................................................................................................4 Children And Their Parents..............................................................................................................................5 I Say Method, You Say Madness.......................................................................................................................7 Playing With Nodes ...........................................................................................................................................10 Rounding Up The Family.................................................................................................................................13 Saving My Bookmarks.....................................................................................................................................17 Reaching Higher ................................................................................................................................................21 Collapsing Inwards...........................................................................................................................................27 Extending Yourself...........................................................................................................................................30

The Horns Of A Dilemma


How many times have you sat down to code a script and halfway through thought to yourself, "Didn't I do something similar just last week?" If you're anything like the average Web developer, you probably ask yourself this question at least once every few days. And more often than not, you're torn between coding the same functions again (because you're already halfway there and looking for last week's code just isn't worth the effort) and spending an hour searching for that ittybitty script on your twentyterabyte hard drive (because it's just more convenient to modify last week's code than to write it all over again.) It's to resolve precisely this sort of dilemma that a bunch of whitehaired software gurus (who, according to legend, live on a snowy mountain peak in the Himalayas and spend most of their time coding algorithms to calculate the value of pi to the nth decimal) came up with the concept of objectoriented programming. Very simply, objectoriented programming allows developers to create reusable, extensible program modules in order to speed up code development and maintenance. Now, you may not know this, but my favourite language and yours, PHP, comes with some pretty powerful OOP capabilities. And over the course of this article, I'm going to demonstrate some of them by building an object to address a very common task generating a menu tree on a Web site. That's not all, though once I've successfully created a Menu object, I'm going to torturetest it with some of the most popular menu systems available on the Web to see if it does, in fact, offer any significant advantages. If all goes well, this experiment should teach you a little about the theory and possible applications of OOP; provide you with a Menu class which is (hopefully) useful to you in your development activities; and perhaps even spark off some ideas for using PHP classes in your next project. If, on the other hand, I crash and burn, you'll have something to snicker over at the pub tonight. Sounds like fun? Keep reading.

The Horns Of A Dilemma

Back To Class
Before we begin, let's just go over the basics quickly: In PHP, a "class" is simply a set of program statements which perform a specific task. A typical class definition contains both variables and functions, and serves as the template from which to spawn specific instances of that class. Once a class has been defined, PHP allows you to spawn as many instances of the class as you like. These instances of a class are referred to as "objects". Each of these instances is a completely independent object, with its own properties and methods, and can thus be manipulated independently of other objects. This comes in handy in situations where you need to spawn more than one instance of an object for example, two simultaneous database links for two simultaneous queries, or two shopping carts. Classes also help you to separate your code into independent modules, and thereby simplify code maintenance and changes. A class definition typically looks like this:

<? class ShoppingCart { // this is where the properties are defined var $items; var $quantities; var $prices; ... // this is where the methods are defined function validate_credit_card() { // code goes here } ... } ?>

Once the class has been defined, an object can be spawned with the "new" keyword and assigned to a PHP variable,

Back To Class

Building an Extensible Menu Class

$myCart = new ShoppingCart;

?>

which can then be used to access all object methods and properties.

<? // accessing properties $myCart>items = array("eye of newt", "tail of lizard", "wings of bat"); $myCart>quantities = array(9, 4, 14); // accessing methods $myCart>validate_credit_card(); ?>

Back To Class

What's On The Menu?


So that's the theory. Let's now spend a few minutes discussing the rationale behind the Menu object I plan to build. Conceptually, a Web site can be considered as a combination of two things: menus and content. Menus are used to organize and classify the type of content, and to offer one or more navigational paths to specific content modules. Now, although a menu may be visually presented in a number of different ways, there are certain common elements present in every menu: 1. Most menus are broken into levels, with each level more focused than the last; this hierarchical structure is sometimes referred to as a "menu tree". 2. Every menu tree consists of nodes connected to each other by branches. 3. A node may have one or more children, but can have only one parent. Using these common principles, it is possible to build a Menu object which exposes certain generic methods. These methods will have nothing to do with the visual presentation of the menu tree; rather, they provide a simple API to various menu attributes and relationships, and can be used by client or serverside scripts which are more closely connected to the presentation layer.

What's On The Menu?

Children And Their Parents


Now, the Menu object that I plan to build actually consists of two components: a database, and a series of functions to interact with it. The database contains all the raw data needed to generate the menu tree, while the class contains all the functions needed to massage the data into a useful format. I plan to use a very simple mySQL table to store all my menu information, as well as the relationships between the various levels of the tree take a look:

# # Table structure for table 'menu' # DROP TABLE IF EXISTS menu; CREATE TABLE menu ( id tinyint(3) unsigned NOT NULL auto_increment, label varchar(255) NOT NULL, link varchar(255), parent tinyint(3) unsigned DEFAULT '0' NOT NULL, PRIMARY KEY (id) ); # # # # # #

id unique identifier for each node label descriptive text for each node link URL for each node parent id of this node's parent

This design makes it easy to represent a hierarchical menu tree in terms of database records. For example, I could represent the following visual tree

USA | | | | | | | |

California Los Angeles Massachusetts Boston

United Kingdom

Children And Their Parent...

Building an Extensible Menu Class | | London

as a series of records in the "menu" table.

mysql> SELECT * FROM menu; +++++ | id | link | label | parent | +++++ | 1 | | USA | 0 | | 2 | | California | 1 | | 3 | | Los Angeles | 2 | | 4 | | Massachusetts | 1 | | 5 | | Boston | 4 | | 6 | | United Kingdom | 0 | | 7 | | London | 6 | +++++ 7 rows in set (0.00 sec)

Children And Their Parent...

I Say Method, You Say Madness...


With the database design out of the way, it's now time to begin work on the methods which will interact with the records in the database. Before actually sitting down to code the class, it's a good idea to spend some time listing the important methods, together with their purpose. Here's my initial cut: query($query) execute an SQL query; get_children($node) return a collection of this node's children; get_parent($node) return the identifier of this node's parent; get_label($node) return the name of this node get_type($node) return the node type (leaf or branch) is_root_node($node) is this node at the root level or not? These are the essential methods; there may be more, which I will add as development progresses. Let's now begin by setting up the class definition:

<? // menu.class.php // methods to obtain node and tree relationships class Menu {

} ?><hr noshade size=1 color=#cccccc></pre></blockquote> Now, since this class will be talking to a database, I need to add a few variables to hold databasespecific information. <blockquote><pre><hr noshade size=1 color=#cccccc><? class Menu { // set up some variables with default values // to hold database parameters // hostname, user and password

I Say Method, You Say Mad...

Building an Extensible Menu Class var $hostname; var $user; var $pass; // database and table containing menu data var $db; var $table; } ?>

PHP makes it possible to automatically execute a specific function when a new instance of a class is spawned. This function is referred to as a "constructor" and must have the same name as the class. In this case, I plan to initialize my Menu object with certain default values for the various database parameters. I have the option of assigning these variablevalue pairs individually, or writing a method to assign them all in one fell swoop; I pick the latter.

<? class Menu { // set up some variables // snip // constructor function Menu() { $this>set_database_parameters("localhost", "me", "bs49h5634", "apps", "menu"); }

// function: set database parameters // returns: none function set_database_parameters($hostname, $user, $password, $db, $table) { $this>hostname = $hostname; $this>user = $user; $this>password = $password; $this>db = $db; $this>table = $table; } }

I Say Method, You Say Mad...

Building an Extensible Menu Class ?>

In case you're wondering, the $this prefix provides a convenient way to access variables and functions which are "local" to the class Now, all the methods listed above will be querying the database for information. Since the connectandquery code is common to all of them, I can extract it into a separate method named query().

<? class Menu { // other methods // function: execute query $query // returns: result identifier function query($query) { // connect $connection = mysql_connect($this>hostname, $this>user, $this>pass) or die ("Cannot connect to database"); // run query $ret = mysql_db_query($this>db, $query, $connection) or die ("Error in query: $query"); // return result identifier return $ret; } } ?>

All my object methods can now simply use query() to execute SQL queries on the database. Further if I ever decide to move to another database, I need only update the code in this single function to ensure that my class will continue to work with the new system.

I Say Method, You Say Mad...

Playing With Nodes


With the underlying, databasespecific layer in place, I can now begin work on the main object methods. The first of these is also one of the simplest it accepts a node id and returns the id of the node's parent.

<? class Menu { // other methods // function: get parent // returns: node id function get_parent($id) { $query = "SELECT parent FROM $this>table WHERE id = '$id'"; $result = $this>query($query); $row = mysql_fetch_row($result); return $row[0]; } } ?>

An offshoot of this is the is_root_node() method, used to test whether a particular node is at the base of the menu tree.

<? class Menu { // other methods // function: is this node at the root of the tree? // returns: boolean function is_root_node($id) { if($this>get_parent($id) == 0) { return 1; } else { return 0; } }

Playing With Nodes

10

Building an Extensible Menu Class } ?>

The get_label() and get_link() methods accept a node id and return the corresponding text and URL from the table.

<? class Menu { // other methods // function: get label for $id // returns: string function get_label($id) { $query = "SELECT label FROM $this>table WHERE id = '$id'"; $result = $this>query($query); $row = mysql_fetch_row($result); return $row[0]; } // function: get link for $id // returns: string function get_link($id) { $query = "SELECT link FROM $this>table WHERE id = '$id'"; $result = $this>query($query); $row = mysql_fetch_row($result); return $row[0]; } } ?>

Using the menu table constructed a few pages ago, the code

<? $obj = new Menu(); echo $obj>get_label(3); ?>

would return

Playing With Nodes

11

Building an Extensible Menu Class Los Angeles

Playing With Nodes

12

Rounding Up The Family


Next, one of the most useful methods in this collection the get_children() method. This method accepts a node id and returns an array containing the next level of the menu tree.

<? class Menu { // other methods // function: get next level of menu tree // returns: array function get_children($id) { $query = "SELECT id, label, link FROM $this>table WHERE parent = '$id'"; $result = $this>query($query); $count = 0; while ($row = mysql_fetch_array($result)) { $children[$count]["id"] = $row["id"]; $children[$count]["label"] = $row["label"]; $children[$count]["link"] = $row["link"]; $count++; } return $children; } } ?>

This method accepts a node id and queries the database for a list of items which reference that node in the "parent" column. These records are packaged as an array of arrays and returned to the calling function. Here's an example of how it could be used:

<? $obj = new Menu(); // get children $arr = $obj>get_children(1); echo "<ul>"; // iterate through array for ($x=0; $x<sizeof($arr); $x++) { echo "<li>" . $arr[$x]["label"];

Rounding Up The Family

13

Building an Extensible Menu Class } echo "</ul>"; ?>

And this correctly displays the children of node id 1 (USA) to be

California Massachusetts

A useful corollary of this is the get_type() method, which can be used to identify whether a particular node on the tree has children or not in other words, whether it is a leaf or a branch.

<? class Menu { // other methods // function: test whether this id is a branch or leaf // returns: boolean function get_type($id) { if($this>get_children($id) ) { return 1; } else { return 0; } } } ?>

The get_ancestors() method does the reverse of the get_children() method it returns a list of nodes between the tree root and the supplied node identifier, starting from the top of the menu tree and proceeding downwards.

Rounding Up The Family

14

Building an Extensible Menu Class <? class Menu { // other methods // function: return a list of this node's parents // by travelling upwards all the way to the root of the tree // returns: array function get_ancestors($id, $count = 0) { // get parent of this node $parent = $this>get_parent($id); // if not at the root, add to $ancestors[] array if($parent) { $this>ancestors[$count]["id"] = $parent; $this>ancestors[$count]["label"] = $this>get_label($parent); $this>ancestors[$count]["link"] = $this>get_link($parent); // recurse to get the parent of this parent $this>get_ancestors($this>ancestors[$count]["id"], $count+1); // all done? at this stage the array contains a list in bottomup order // reverse the array and return return array_reverse($this>ancestors); } } } ?>

Returning to the example above, an attempt to find out the ancestors of node id 5 (Boston)

<? $obj = new Menu(); // get children $arr = $obj>get_ancestors(5); echo "<ul>"; // iterate through array for ($x=0; $x<sizeof($arr); $x++) { echo "<li>" . $arr[$x]["label"]; } echo "</ul>"; ?>

Rounding Up The Family

15

Building an Extensible Menu Class would return

USA Massachusetts

Finally, the print_menu_tree() method comes in handy while debugging, if you need to visually see the complete menu tree with its internal dependencies.

<? class Menu { // other methods // function: display complete menu tree (useful when debugging) // returns: HTML list function print_menu_tree($id = 0) { $result = $this>get_children($id); echo "<ul>"; for ($x=0; $x<sizeof($result); $x++) { echo "<li>" . $result[$x]["label"] . "[" . $result[$x]["id"] . "]"; $this>print_menu_tree($result[$x]["id"]); } echo "</ul>"; } } ?>

Rounding Up The Family

16

Saving My Bookmarks
At this stage, I think I have enough building blocks to actually begin using this class to build menu trees. Keep in mind, though, that I've been wrong before, and so my initial feeling of satisfaction may soon disappear. The only way to find out for sure is to try building a tree to see if the methods exposed by the class are simple and generic enough to be used in a variety of situations so let's do that. I will attempt to use this Menu class to build a simple Web portal, which has links classified into hierarchical categories (a lot like the Open Directory Project at http://www.dmoz.org/) This is a good time to download the accompanying source code, which contains complete versions of the SQL records displayed below, together with a copy of the final Menu class. menu.zip Since this is a portal, my database needs to reflect the various categories and links. Here's a snippet:

mysql> SELECT id, link, label, parent FROM menu3; +++++ | id | link | label | parent | +++++ | 1 | | Server Side | 0 | | 2 | | Client Side | 0 | | 3 | | Tools | 0 | | 4 | | DevTalk | 0 | | 5 | http://www.devshed.com/ClipScripts/ | ClipScripts | 0 | | 6 | http://www.devshed.com/rdf.html | DevShed RDF | 0 | | 7 | http://www.devshed.com/Propaganda | Propaganda! | 0 | | 8 | http://www.ngenuity.com/advertise/ | About DevShed | 0 | | 9 | | Administration | 1 | +++++

My user interface should clearly reflect this menu tree, by making a distinction between "categories" and "links". A click on a category reveals the subcategories and links under it, while a click on a link directs the browser to the appropriate content module or URL. Here's the script to accomplish this:

<? // set default id to 0 if (!$id) { $id = 0; } // include class include("menu.class.php");

Saving My Bookmarks

17

Building an Extensible Menu Class // create Menu $obj = new Menu; // get next level $children = $obj>get_children($id); // check to see if items are "leaves" or "branches" on the tree for ($x=0; $x<sizeof($children); $x++) { if($obj>get_type($children[$x]["id"]) == 1) { $branches[] = $children[$x]; } else { $leaves[] = $children[$x]; } } // get lineage from tree root (used to create navigation path) $ancestors = $obj>get_ancestors($id) ?> <html> <head> <basefont face="Arial"> </head> <body bgcolor="White" link="Black" vlink="Black"> <img src="logo.gif" height=50 width=206 border=0 alt=""> <table width="100%" border="0" cellspacing="0" cellpadding="3" bgcolor="#9898D0"> <tr> <td height=20> <font color="Black"><b> <? // use $ancestors[] to set up path for ($x=0; $x<sizeof($ancestors); $x++) { $path .= "<a href=" . $PHP_SELF . "?id=" . $ancestors[$x]["id"] . ">" . $ancestors[$x]["label"] . "</a>" . " > "; } // add current level to path and print $path .= $obj>get_label($id);

Saving My Bookmarks

18

Building an Extensible Menu Class echo $path; ?> </b></font> </td> <td align=right><? if ($id > 0) { ?><font color="Black"><b><a href="<? echo $PHP_SELF; ?>">Top</a></b></font> <? } ?></td> </tr> </table> <p> <? if ($branches) { ?> <b><font color="#9898D0" size="+1">Categories</font></b> <ul> <? // print branches here for ($x=0; $x<sizeof($branches); $x++) { echo "<li><a href=" . $PHP_SELF . "?id=" . $branches[$x]["id"] . ">" . $branches[$x]["label"] . "</a><br>"; } ?> </ul> <? } ?> <p> <? if ($leaves) { ?> <b><font color="#9898D0" size="+1">Links</font></b> <ul> <? // print leaves here for ($x=0; $x<sizeof($leaves); $x++) { echo "<li><a href=" . $leaves[$x]["link"] . ">" .

Saving My Bookmarks

19

Building an Extensible Menu Class $leaves[$x]["label"] . "</a><br>"; } ?> </ul> <? } ?> </body> </html>

In this case, I'm first using the get_children() method to obtain a list of all items under the current tree branch. I'm then using the get_type() method to split the list of child nodes into two separate arrays, $branches and $nodes, and formatting and displaying each appropriately. Finally, with the help of the get_ancestors() method, I'm building a hierarchical, clickable trail leading to the current node just like any good portal would. Here's what the end result looks like:

Saving My Bookmarks

20

Reaching Higher
Now, while that's all fine and dandy, a portal is perhaps the simplest application of this class. It remains to be seen if it can be used with other, more complex menu interfaces. So let's put it to the test, by putting it in the ring with some popular JavaScriptbased menu systems. The first of these is the very popular HIERmenus script (available at http://www.webreference.com/dhtml/hiermenus/ ). This very flexible menu system is completely written in JavaScript, and relies on JavaScript arrays (packaged in a specific format) to build a hierarchical menu tree. I'm not going to get into the nittygritty of how it works there's some excellent documentation if you're interested but rather plan to focus on how this clientside code can be connected to a database of menu items via the Menu class. Let's suppose that I wanted to build a menu tree which looked like this:

mysql> SELECT id, label, parent FROM menu; ++++ | id | label | parent | ++++ | 1 | Services | 0 | | 2 | Company | 0 | | 3 | Media Center | 0 | | 4 | Your Account | 0 | | 5 | Community | 0 | | 6 | For Content Publishers | 1 | | 7 | For Small Businesses | 1 | | 8 | Background | 2 | | 9 | Clients | 2 | | 10 | Addresses | 2 | | 11 | Jobs | 2 | | 12 | News | 2 | | 13 | Press Releases | 3 | | 14 | Media Kit | 3 | | 15 | Log In | 4 | | 16 | Columns | 5 | | 17 | Colophon | 16 | | 18 | Cut | 16 | | 19 | Boombox | 16 | | 20 | The HITG Report | 16 | | 21 | Trog | 16 | ++++ 21 rows in set (0.06 sec)

If I was to build this menu using the HIERmenus system, I would need to manually code a series of JavaScript arrays, like this:

Reaching Higher

21

Building an Extensible Menu Class HM_Array1 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Services", "http://www.melonfire.com/services/", 1, 0, 1], ["Company", "http://www.melonfire.com/company/", 1, 0, 1], ["Media Center", "http://www.melonfire.com/mcenter/", 1, 0, 1], ["Your Account", "http://www.melonfire.com/account/", 1, 0, 1], ["Community", "http://www.melonfire.com/community/", 1, 0, 1] ]; HM_Array1_1 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["For Content Publishers", "http://www.melonfire.com/services/content.html", 1, 0, 0], ["For Small Businesses", "http://www.melonfire.com/services/sbs.html", 1, 0, 0] ]; HM_Array1_2 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Background", "http://www.melonfire.com/company/background.html", 1, 0, 0], ["Clients", "http://www.melonfire.com/company/clients.html", 1, 0, 0], ["Addresses", "http://www.melonfire.com/company/addresses.html", 1, 0, 0], ["Jobs", "http://www.melonfire.com/company/jobs.html", 1, 0, 0], ["News", "http://www.melonfire.com/company/news.php3", 1, 0, 0] ]; HM_Array1_3 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Press Releases", "http://www.melonfire.com/mcenter/pr.html", 1, 0, 0], ["Media Kit", "http://www.melonfire.com/mcenter/mkit.html", 1, 0, 0] ]; HM_Array1_4 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Log In", "http://www.melonfire.com/account/index.html", 1, 0, 0] ]; HM_Array1_5 = [ [180, 200, 50, "black", "white", "white", "black", "black",

Reaching Higher

22

Building an Extensible Menu Class "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Columns", "http://www.melonfire.com/community/columns/", 1, 0, 1] ]; HM_Array1_5_1 = [ [180, 200, 50, "black", "white", "white", "black", "black", "gray", 0, 0, 0, 1, 1, 1, "null", "null", ,], ["Colophon", "http://www.melonfire.com/community/columns/colophon/", 1, 0, 0], ["Cut", "http://www.melonfire.com/community/columns/cut/", 1, 0, 0], ["Boombox", "http://www.melonfire.com/community/columns/boombox/", 1, 0, 0], ["The HITG Report", "http://www.melonfire.com/community/columns/thr/", 1, 0, 0], ["Trog", "http://www.melonfire.com/community/columns/trog/", 1, 0, 0] ];

The downside here is obvious each time I want to change the menu structure, I have to go into the JavaScript source and muck about with the arrays (which aren't exactly all that userfriendly to begin with.) What I would prefer to do, however, is somehow interface my database table to the HIERmenus system, so that updating the menu becomes as simple as executing an SQL query to change the relationships in the mySQL table. With the help of some clever PHP code and the Menu object I've just built, this becomes not just possible, but a snap to accomplish. The first step is to alter the HIERmenus scripts to reference a PHP file for the arrays, rather than a JavaScript file simply alter the reference to "HM_Arrays.js" in the file "HM_Loader.js" to "HM_Arrays.js.php", as below.

if(HM_IsMenu) { document.write("<SCR" + SRC='HM_Arrays.js.php ' "IPT>"); document.write("<SCR" + SRC='HM_Script"+ HM_BrowserString +".js' "IPT>"); }

"IPT LANGUAGE='JavaScript1.2' TYPE='text/javascript'><\/SCR" + "IPT LANGUAGE='JavaScript1.2' TYPE='text/javascript'><\/SCR" +

Next, create the script "HM_Arrays.js.php", and populate it with the following PHP code:

<? // options to configure menu appearance $HM_Menu_Options = "150, 200, 50, \"black\", \"white\",

Reaching Higher

23

Building an Extensible Menu Class \"white\", \"black\", \"black\", \"gray\", 0, 0, 0, 1, 1, 1, \"null\", \"null\", ,";

// create object include("menu.class.php"); $obj = new Menu; // run recursive function buildMenu(0, "HM_Array1"); // this is a recursive function to generate // the JS arrays needed by HIERmenus // this function accepts an id (used to generate children) // and a prefix string (attached to every array name, needed by HIERmenus) // for more information on how this works and syntax, // refer to HIERmenus documentation function buildMenu($id, $prefix) { // obtain a reference to the Menu object // and also source the options global $obj; global $HM_Menu_Options; // get children of this node $children = $obj>get_children($id); // create a JS array with the correct name $array_str = $prefix . " = [ [" . $HM_Menu_Options . "]"; // iterate through child nodes and create individual array elements for ($x=0; $x<sizeof($children); $x++) { $array_elements_str = ", [\"" . $children[$x]['label'] . "\", \"" . $children[$x]['link'] . "\", 1, 0, " . $obj>get_type($children[$x]['id']) . "]"; // if a child node has further children, // recurse with appropriate modification to the prefix if($obj>get_type($children[$x]['id']) == 1) { $temp = $prefix . "_" . ($x+1);

Reaching Higher

24

Building an Extensible Menu Class buildMenu($children[$x]['id'], $temp); } // add the final list of array elements to the main array $array_str .= $array_elements_str; } // close the JS array $array_str .= " ];"; // and print it print $array_str; } ?>

The end result of all this processing: a set of JavaScript arrays containing the various menu nodes, in a format which is acceptable to HIERmenus. This is accomplished by means of a recursive function (conceptually identical to the one used in the print_menu_tree() method) which takes care of iterating through the various levels of the menu tree and printing arrays in the format required by HIERmenus. Now all we need is a HTML page which activates the HIERmenus system and displays the menu tree in all its glory.

<html> <head> <basefont face="Arial"> <script language="JavaScript" type="text/javascript"> <! if(window.event + "" == "undefined") event = null; function HM_f_PopUp(){return false}; function HM_f_PopDown(){return false}; popUp = HM_f_PopUp; popDown = HM_f_PopDown; //> </script> </head> <body> <a href="#" onMouseOver="popUp('HM_Menu1',event)" onMouseOut="popDown('HM_Menu1')">Roll your mouse over this link to see a menu</a>

Reaching Higher

25

Building an Extensible Menu Class <script language="JavaScript1.2" src="HM_Loader.js" type='text/javascript'></script> </body> </html>

And here's what it looks like:

Don't worry too much about the JavaScript code in this example it is merely standard code required for HIERmenus to work, and is clearly documented by the developers of the system. The important thing to note here is that we've taken a pure clientside application and successfully connected it to a serverside database using standard method calls within the Menu object. Of course, this solution may not be ideal in every case. Using a database and a PHP script to generate the JavaScript arrays dynamically (rather than storing and using arrays from static JavaScript files) may degrade performance; however, it does offer a benefit from the point of view of simpler maintenance of menu tree relationships. It's much easier to alter relationships in a mySQL table than it is to open up a JavaScript file and edit the information in it; a nontechnical person can easily accomplish the former, but may have difficulty with the latter. Consequently, a thorough costbenefit analysis should be performed before making a decision as to which option is best suited to a specific case.

Reaching Higher

26

Collapsing Inwards
If you go back a few pages, you'll notice that one of the design goals of this Menu object was to separate the visual presentation of a menu from the relationships between the various nodes. I've already demonstrated how the same Menu object can be used to create a directory and a hierarchical menu tree with standard methods. My third example is similar to the second, again connecting a JavaScriptbased menu system to the menu database to dynamically build a menu tree. For this, I plan to use another very popular menu constructor, FolderTree (free version available at http://www.geocities.com/marcelino_martins/foldertree.html ), which uses Windows Explorerstyle files and folders to display a hierarchy of items. First, I need an HTML page to invoke FolderTree and display the menu:

<html> <head> <basefont face="Arial"> <link rel="stylesheet" href="ftie4style.css"> <! Infrastructure code for the tree > <script src="ftiens4.js"></script> <! Execution of the code that actually builds the specific tree > <script src="menu.js.php"></script> <script> initializeDocument() </script> </head> <body bgcolor=white> </body> </html>

As you can see, this file sources "menu.js.php", which contains the actual menu data. This file is usually created manually as per the user's requirements; I plan to hook it up to my database table to generate it dynamically. Here's the code:

// menu.js.php // set up tree root

Collapsing Inwards

27

Building an Extensible Menu Class foldersTree = gFld("Melonfire", "http://www.melonfire.com"); <? // initialize object include("menu.class.php"); $obj = new Menu(); // counter used to create tree nodes $count = 0; function buildMenu($id, $node) { global $obj; global $count; // get children $children = $obj>get_children($id); for ($x=0; $x<sizeof($children); $x++) { // write the name of this node $newNode = "aux" . $count; // if folder if($obj>get_type($children[$x]["id"]) == 1) { // call appropriate FolderTree function to display folder icon $count++; $str = $newNode . " = insFld(" . $node . ", gFld(\"" . $children[$x]['label'] . "\", \"" . $children[$x]['link'] . "\"));"; print $str; // and recurse for next level buildMenu($children[$x]["id"], $newNode); } else { // if last level // call appropriate FolderTree function to display file icon $str = "insDoc(" . $node . ", gLnk(2, \"" . $children[$x]['label'] . "\", \"" . $children[$x]['link'] . "\"));"; print $str; } } } // initiate recursive function buildMenu(0, "foldersTree"); ?>

Collapsing Inwards

28

Building an Extensible Menu Class

You just gotta love those recursive functions! Here's what it all looks like:

Collapsing Inwards

29

Extending Yourself
All the examples you've seen thus far have used the same standard API defined within the Menu object. However, this assumes one important thing that a database table (in the format described) has already been created and populated with menu records. In case this is an unreasonable assumption for your specific requirements, you might consider adding a few method calls to add and delete nodes respectively.

<? class Menu { // other methods // function: add a record to the menu table function create_node($label, $link, $parent) { $this>query("INSERT INTO $this>table(label, link, parent) VALUES ('$label', '$link', '$parent')"); } // function: remove a record from the menu table function remove_node($id) { $this>query("DELETE FROM $this>table WHERE id = '$id'"); } } ?>

You might also want to consider developing a simple administration interface to these method calls, so that users can easily modify the menu tree via a GUI. And that's about all for the moment. In this article, you expanded your knowledge of PHP's OOP capabilities by actually using all that theory to build something useful a menu widget which can be used to describe the relationships within a hierarchical menu system, independent of how the menu is visually presented. If you work with menu systems, whether on a Web site, within a Web application or on an embedded system, you might find this object a handy tool in your next development effort. If you're a novice programmer struggling to understand how OOP can make your life easier, I hope this article offered some pointers, as well as some illustration of how objectoriented programming works. And if you don't fit into either of those categories well, I hope you found it interesting and informative, anyway. See you soon! Note: All examples in this article have been tested on Linux/i586 with PHP4, HIERmenus 4.0.12, and

Extending Yourself

30

Building an Extensible Menu Class FolderTree 2.0. HIERmenus and FolderTree copyright their respective authors. Examples are illustrative only, and are not meant for a production environment. YMMV!

Extending Yourself

31

You might also like