Get your FREE 30 page Developing SOLID Applications guide!

An Intro To Zend_Navigation

Out Of Date Warning

Languages change. Perspectives are different. Ideas move on. This article was published on March 29, 2010 which is more than two years ago. It may be out of date. You should verify that technical information in this article is still current before relying upon it for your own purposes.

This past week I had the need to integrate Zend_Acl with a navigation and menu system. Unfortunately, the documentation took a couple reads for me to understand how Zend_Navigation worked; given this, I wanted to write a primer that others could use to get started with Zend_Navigation. This is a short guide to how Zend_Navigation works, and how you can use it to improve your application.

Some Critical Vocabulary
There is some critical vocabulary that needs to be understood about Zend_Navigation and its use. For starters, Zend_Navigation makes a distinction between pages and containers. A page is a page in your application, either internal (Mvc) or external (Uri). A container is anything that stores pages. The confusing part of this is that pages are containers in themselves. This allows you to build trees of pages.

If that was a bit confusing, I understand. For the purposes of this introduction, I’ll be calling them parents and children. When I use the word container I’ll be referring only to Zend_Navigation objects (concrete implementations of Zend_Navigation). When I use the word pages I’ll be referring to concrete implementations of Zend_Navigation_Page_Mvc or Zend_Navigation_Page_Uri.

Zend_Navigation also makes an important distinction between MVC pages and URI pages. There is an important difference between the two. MVC pages are usually meant to refer to the Zend Framework MVC structure; URI pages are designed to refer to complete URLs, internally or externally. Besides their intended uses, the API is fairly similar between the two objects.

Creating Pages
Creating pages is easy enough to do. The following source code technically gives you a complete page:


$page = new Zend_Navigation_Page_Mvc();

Obviously this page would be pretty useless, so let’s discuss some of the things you can give a page.


$page = new Zend_Navigation_Page_Mvc(array('controller' => 'home', label = 'Home'));

Now that’s more like it! We’ve given the page two of several arguments that it can take (you can read all about them in the manual). In this case, we’ve told Zend_Navigation that we have a page whose controller is “home” and that should be labeled “Home”. If we were to turn this into a URL (or run $page->getHref()) we’d get “http://localhost/home” as a URL.

This might seem overly simple, but that’s all there is to creating pages. One of the beautiful things about pages is that they take all of their arguments as an array, which means they’re easy to serialize and store for later retrieval (something that’s highly recommended). Additionally, you can use a Zend_Config object to create pages, or even to create a whole tree of pages (more on that, next).

Creating Trees
Single pages aren’t very useful to us. Typically navigation is arranged in a hierarchy of some kind, with some pages being parents of other pages. Zend Framework makes it easy for us to create trees that have parents with children.

I typically start with just a generic container class of Zend_Navigation:


$container = new Zend_Navigation();

Two things are of critical importance to note at this point: when I create my pages, pages themselves can be parents or children, or both. All the pages will go inside the $container object; child pages can go inside parent pages, and so on. So assume for the moment this is our hierarchy:

- Home
- About
|
- Careers
- Mission
- Tools
|
- Free Tools
- New Licenses
- Products

Creating these pages is easy. First, we need an array of the pages (which we can store in a separate file or you can even create XML for it and use Zend_Config to grab the XML). Here is my array:


$navArray = array(
array(
'controller' => 'index',
'label' => 'Home',
),
array(
'controller' => 'about',
'label' => 'About',
'pages' => array(
array(
'controller' => 'about',
'action' => 'careers',
'label' => 'Careers',
),
array(
'controller' => 'about',
'action' => 'mission',
'label' => 'Mission',
),
),
),
array(
'controller' => 'tools',
'label' => 'Tools',
'pages' => array(
array(
'controller' => 'tools',
'action' => 'free',
'label' => 'Free Tools',
),
array(
'controller' => 'tools',
'action' => 'licenses',
'label' => 'New Licenses',
),
array(
'controller' => 'tools',
'action' => 'products',
'label' => 'Products',
),
),
),
);

Next, once we have that array in our source code (through whatever means) we can make use of it:


$config = new Zend_Config($navArray);
$nav = new Zend_Navigation();
$nav->addPages($config);

And that’s it! The tree will automatically build itself, and the pages will be in the right order, under the right parents, and inside the Navigation container. You can choose to do this in your Bootstrap or somewhere else (it’s up to you). It is recommended that you store this somewhere in some caching mechanism, to help ensure that you don’t do it on every pageload, but this is the general idea.

Finding Pages In Trees
Of course, now that we have all of those pages in a huge tree, how the heck do we ever find anything? The navigation object has built-in methods for finding our page objects, and can search by any number of categories.

One of the options we have for creating pages is to tag them, which allows us to find all pages by a certain tag. Obviously we didn’t do that, but never fear, we can search by label instead. Let’s say we need to get information about the Careers page:


$page = $nav->findOneBy('label', 'Careers');

And just like that, we have a page object we can use! Be aware that if Zend_Navigation doesn’t find a page, it returns null (rather than throwing an exception) so you should include some logic to ensure that an object is actually returned to you. Other search options are findBy() and findAllBy().

Conclusions
Zend_Navigation has an easy way to create trees of pages without too much hassle. What we haven’t covered is how to make these trees work for you, or how to include Zend_Acl along with them. Those topics will be covered in the next two entries on Zend_Navigation.

Write better object oriented PHP today.

Object oriented programming always leaves you with a headache. What if you could master it instead?

Get the book now! »

James S (@tkstudios) wrote at 3/29/2010 12:45 pm:

Nice straightforward explanation, looking forward to the next post. :)

Carlos Aguado wrote at 3/29/2010 3:02 pm:

Thanks Brandon! Looking forward to take a look to the next Zend_Navigation entries…

Gary (@garyj) wrote at 3/29/2010 8:17 pm:

That’s a great primer, thank you Brandon!

Shaun Farrell (@farrelley) wrote at 3/30/2010 7:23 pm:

I love Zend Navigation. It allows you to do a lot. You can generate navigation, sitemaps, and breadcrumbs.

You show a good way to do this via and array however you can also do this in your config files. This way it will make your bootstrap file a little less cluttered.

Good Post! Keep them coming!

Leonard Dronkers (@leonarddronkers) wrote at 3/31/2010 5:54 am:

Nice post, thanks will look forward to the ACL part.

@farrelley)
I have used it with the routes I have defined as a source for my navigation as part of my cms, and store it all in a database. This makes it very flexible and there is no coupleing between controllers, actions and modules. That is if you have a good naming convention for your routes. So when you create a new module for instance (with routes) they become available in my navigation cms.