Login with OpenID

Extending Navigation View Helpers in Zend Framework

Written by Hector Virgen
Published on May 10, 2011
Last updated on May 10, 2011

A question that comes up from time to time in the Zend Framework mailing list is how to extend Zend Framework's Navigation view helpers. By following these simple steps, you'll be able provide your own Navigation view helpers while still taking advantage of what Zend Framework has provided.

How Most Helpers are Extended

Most of Zend Framework's view helpers can easily be extended by providing a helper with the same name within your application. Since Zend_View uses a plugin loader to locate view helpers, you can use the same helper name while avoid conflicts. Here's a basic example:

Let's say you want to extend the Json view helper so that it always pretty prints the JSON. You could simply create your own "Json" view helper, extend Zend's Json view helper, and save it at application/views/helpers/Json.php:

<?php

class Default_View_Helper_Json extends Zend_View_Helper_Json
{
    public function json($data, $keepLayouts = false)
    {
        $json = parent::json($data, $kepLayouts);
        return Zend_Json::prettyPrint($json);
    }
}

Additionally, you'll want to be sure that your view can find your new view helper, so you'll need to add this line to your application/configs/application.ini under the production section:

resources.view.helperPath.Default_View_Helper = APPLICATION_PATH "/views/helpers"

Now, whenever your view scripts call $this->json(), your improved view helper will be used. Seems easy enough!

What About Them Navigation Helpers?

So now let's say that you want to improve ZF's "menu" view helper. For example, you want to output an HTML comment whenever we call the menu helper's renderMenu() method. Why would you want to do this? I have no idea, but for the purposes of this tutorial let's say that's what we want to do.

So let's follow the same steps as we did with our custom Json view helper and see what happens. So first we define our helper and save it at application/views/helpers/Menu.php:

<?php

class Default_View_Helper_Menu extends Zend_View_Helper_Navigation_Menu
{
    public function renderMenu(Zend_Navigation_Container $container, array $options = array())
    {
        return '' . parent::renderMenu($container, $options);
    }
}

Again, we make sure our application's configuration includes the path to this helper:

resources.view.helperPath.Default_View_Helper = APPLICATION_PATH "/views/helpers"

And then we update our layout to call the view helper:

<?php echo $this->navigation()->menu()->renderMenu(); ?>

We then visit our page in a browser and look for the comment -- but it's not there! Why did it not pick up our custom Menu helper, while it picked up our custom Json helper with no problem?

What's Going On? I'm scared!

There's no need to be afraid! Let's just take a look under the hood to see what's happening.

First of all, you might have noticed that Zend's "menu" view helper is not located in the same place as all the other Zend view helpers. Most of them (including the Json view helper) are located here:

Zend/View/Helper

But the Menu view helper is located here:

Zend/View/Helper/Navigation

In order for Zend_View to load the navigation helpers, a new prefix path must be added to your view instance. The parent helper "Navigation" is smart enough to add this prefix path for us, but often times this happens very late in the request's life time (after your view has been bootstrapped). This results in the prefix paths being set up thusly:

  • Zend/View/Helper
  • application/views/helpers
  • Zend/View/Helper/Navigation

Since Zend_View uses a Last In, First Out (LIFO) stack to find view helpers, this means the path Zend/View/Helper/Navigation is the first place it looks for the Menu helper. Since ZF's helper exists there, it ends up being used and your custom Menu helper never gets called.

The solution is to take back control of the view's helper paths by manually adding Zend/View/Helper/Navigation to the stack before your custom helper path. You can do this by modifying your application.ini like this:

resources.view.helperPath.Zend_View_Helper_Navigation = "Zend/View/Helper/Navigation"
resources.view.helperPath.Default_View_Helper = APPLICATION_PATH "/views/helpers"

This results in a change in the LIFO stack, which now looks like this:

  • Zend/View/Helper
  • Zend/View/Helper/Navigation
  • application/views/helpers

Now your view will look in your custom helpers before checking the Zend directories for that Menu helper.

Conclusion

Zend's Navigation view helpers can be a bit tricky to extend at first glance, but once you see how the order of the plugin paths matter it starts to make a lot more sense.

One of the great things about Zend Framework is its extensibility, but sometimes it can be frustrating when things don't work as you'd expect. Sometimes all you need to do is just peek through the source to find your answers.

Comments

blog comments powered by Disqus