Login

Building an Extensible Menu Class

So you know the theory behind OOP, but don’t really understand
its applications? Well, it’s time to take objects out of the classroom and
into the real world – this article demonstrates how OOP can save you time
and effort by building a PHP-based Menu object to describe the
relationships in a hierarchical menu tree. And since the proof of the
pudding is in the eating, it then combines the newly-minted Menu object
with some of the most popular JavaScript menu systems available online to
show you how cool objects really are.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 itty-bitty script on your twenty-terabyte 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 white-haired 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
object-oriented programming. Very simply, object-oriented 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 torture-test 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.{mospagebreak title=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.

{mospagebreak title=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
server-side scripts which are more closely connected to the presentation layer.
{mospagebreak title=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:

{mospagebreak title=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.

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 variable-value
pairs individually, or writing a method to assign them all in one fell swoop; I
pick the latter.

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.{mospagebreak title=Playing With
Nodes} With the underlying, database-specific 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.

{mospagebreak title=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.

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:

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.

<?
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 bottom-up 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)

{mospagebreak title=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.

My user interface should clearly reflect this menu tree, by
making a distinction between “categories” and “links”. A click on a category
reveals the sub-categories and links under it, while a click on a link directs
the browser to the appropriate content module or URL.

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:

{mospagebreak title=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
JavaScript-based 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 nitty-gritty of how it works – there’s
some excellent documentation if you’re interested – but rather plan to focus on
how this client-side 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:

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 user-friendly 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.

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.

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 client-side application and successfully
connected it to a server-side 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 non-technical person can easily accomplish the former, but
may have difficulty with the latter. Consequently, a thorough cost-benefit
analysis should be performed before making a decision as to which option is best
suited to a specific case.{mospagebreak title=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 JavaScript-based 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 Explorer-style files and folders to display a hierarchy of
items.

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:

{mospagebreak
title=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.

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
object-oriented 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 FolderTree 2.0. HIERmenus
and FolderTree copyright their respective authors. Examples are illustrative
only, and are not meant for a production environment. YMMV!