Tips and trics for Zend_Navigation

The navigation component of the Zend Framework was introduced at version 1.8. From my perspective, the navigation is besides the MVC stack one of the most useful components of the system. All menu bars, breadcrumbs, meta links, sitemaps and all their variations can be created with this powerful and flexible component.

Nevertheless, some people find it difficult to use Zend_Navigation because of the complex hierarchy of objects. In this blog article I explain a few tips and tricks which I use in my projects. It eases the creation of more complex navigation structures and hope others find it useful too.

This article covers the following topics:

NB. When more snippets cross my mind, I will update the page with more items.

Dynamically inject subpages

This needs first a short introduction. At Soflomo we have a CMS system where "top-level" pages are injected into the navigation. For example, when you have a blog module in your website, only the root page of this blog is added by the cms (automatically) to the navigation tree. When you view an article, the blog module adds the page of the article to the blog root page.

This method is useful because you do not need to inejct all blog categories and articles at bootstrap time, but inject them only when necessary. In order to achieve this, I use a default navigation configuration during bootstrap and an action helper.

The default configuration can be triggered with this single line:

resources.navigation =

Furthermore you need to add pages to your Zend_Navigation container and mark one page as active. This will not be covered in this article, because I assume you already know how to do this. For example, I use a frontController plugin which fetches all pages from the database, builds the Zend_Navigation tree and activates the correct page based on the Request object. You can also use a XML file or other resources. For more information I point you towards the Zend Framework manual or a video of Jon Lebensold.

The action controller looks as follows:

<?php
class Soflomo_Controller_Action_Helper_Navigation extends Zend_Controller_Action_Helper_Abstract
{
    protected $_navigation;
    protected $_helper;

    public function __construct()
    {
        $bootstrap = $this->getFrontController()->getParam('bootstrap');
        $this->_navigation = $bootstrap->getResource('navigation');
        $this->_helper     = $bootstrap->getResource('view')
                                       ->getHelper('navigation');
    }

    public function findActive()
    {
        $active = $this->_helper->findActive($this->_navigation);
        if (!isset($active['page'])){
            return false;
        }

        return $active['page'];
    }

    public function add(array $options)
    {
        if (false !== ($page = $this->findActive()) ) {
            $page->addPage(Zend_Navigation_Page_Uri::factory($options)->setActive(true));
        }
    }
}

When you use the action helper for the first time, the action helper broker automatically instantiates one object. The construction fetches the container which contains all pages and grabs the view helper from Zend_View.

Next, you're able to do two things:

  1. Find an active page (for example, to mark it inactive or grab information of the active page)
  2. Add a new page as child of the current active page

Especially the second method is useful. For example, I do the following in my blog for an article:

<?php
public function articleAction()
{
    $article = new Blog_Model_Article;
    // fetch the article here based on an id or something

    $this->navigation->add(array(
        'label'    => $article->title,
        'uri'      => $this->url() // create the url
    ));
}

With this simple statement you inject your Zend_Navigation_Page_Uri to the current activated page (if it's ok, this will be the blog root page). So on the fly you can add pages to your navigation and you don't need to add them on bootstrap (which saves a lot of resources).

Create submenus with subpages only

Sometimes you want to divide your menu into two parts. A main menu with all the top-level entries. When one of these top-level entries is active, or a child of the top-level entry, a submenu of this entry must be shown. The "problem" is the submenu must be shown when the top-level is active too, another problem is the submenu must not contain the top-level entry.

These requirements can be translated into the following set of options for Zend_Navigation:

array(
  'minDepth'         => 1,
  'onlyActiveBranch' => true,
  'renderParents'    => false
)

So you can render the menu as follows:

<?php echo $this->navigation()->menu->renderMenu(null, array(
    'minDepth'         => 1,
    'onlyActiveBranch' => true,
    'renderParents'    => false
)); ?>

This will create the menu that you wanted!