You are on page 1of 7

Action Helpers in Zend Framework

weierophinney | 12 comments | April 7th, 2008 Action Helpers in Zend Framework are often considered a fairly arcane subject, something for experts only. However, they are meant to be an easy way to extend the capabilities of <a href="http://framework.zend.com/manual/en/zend.controller.action.html">Action Controllers, negating the need to create your own base controller with custom functionality. The aim of this tutorial is to show you how to quickly and easily create and use Action Helpers to your advantage. </a

Basics
Many tutorials on Zend Framework would have you believe you should create a base class extending Zend_Controller_Action to provide base functionality for your controllers:
/** * Your concrete controllers would now extend My_Controller_Action */ abstract class My_Controller_Action extends Zend_Controller_Action { // create your utility methods here... }

However, this is not only not necessary, but typically not a great move for extensibility. You may find later that a given controller only needs a subset of the methods in your base controller or that youre constantly adding methods that only a few of your controllers need, creating bloat. The better solution is to use action helpers. Action helpers are intended to provide run-time, use-at-will capabilities to action controllers. In other words, you can use them if you need them, but they arent loaded by default. Action helpers are handled by a broker. maintains a static registry of registered helpers, and also serves as a factory for loading helpers on demand. By default, the $_helper property of Zend_Controller_Action contains an instance of the broker.
Zend_Controller_Action_HelperBroker

When an action controller is instantiated, a new broker instance is registered with it, and the action controller is in turn registered with the broker. When you retrieve or access helpers, they then have access to the

controller allowing integration with it. So, for example, you can set public properties or call public methods on the action controller via your helper and vice versa. In general, you retrieve your helper by using the last segment of the class name. So, for example, if your helper is named Foo_Helper_Bar, youd refer to it as bar. You then have two options for retrieving it: as a property of the broker, or via the getHelper() method:
$bar = $this->_helper->bar; $bar = $this->_helper->getHelper('bar');

However, this is just the tip of the iceberg.

The direct() Method


Action helpers can use the <a href="http://en.wikipedia.org/wiki/Strategy_pattern">Strategy Pattern. If you define the method direct() in your helper, you can call your helper as if it were a method of the broker. </a An illustration is worth a thousand words. Lets look at the Url helper, which returns a URL based on the input received:
$url = $this->_helper->url('bar', 'foo'); // "/foo/bar"

Implementing the direct() method in your action helpers is an easy way to add virtual functionality to your actions.

Event Hooks
As if that wasnt enough, Action Helpers also have several event hooks to help automate functionality. The three hooks provided are:
init(): called

when the action controller is intialized (but only if an instance of the helper already exists in the broker) preDispatch(): called after plugin preDispatch() routines, but prior to the action controller preDispatch() routines but only if an instance of the helper already exists in the broker. postDispatch(): called after the action controller postDispatch() routines, but prior to plugin postDispatch() routines but only if an instance of the helper already exists in the broker.

Note the caveat on each: only if an instance of the helper exists in the broker already. Typically, youll load helpers on-demand i.e., only when you need them. However, there are some cases where you may want to add automatic functionality similar to plugins but with the ability to introspect the current controller. This is where the action helper hooks come in handy. As an example, the <a href="http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.contr oller.actionhelpers.viewrenderer">ViewRenderer, which is enabled by default in the ZF MVC, is an action helper. It uses the hooks as follows: </a
init(): Initializes the view object,

sets appropriate script, helper, and filter paths for the current controller, and registers the current view object as the controllers view member. postDispatch(): determines if it should render anything, and, if so, renders a view script based on the current (or requested) action to the appropriate response segment.

As another example, you could add a preDispatch() hook to an action helper that checks a public member of your action controller to determine which actions require authentication and redirect to a login form when a match is made. This works better than using a standard plugin, as it allows you to keep the information about authentication requirements with the controller where it belongs.

Registering Helpers with the Broker


If you want to make use of hooks, you will need to register your helpers early typically in the bootstrap or an early running plugin. To do this, you register them with the broker:
Zend_Controller_Action_HelperBroker::addHelper( new Foo_Helper_Bar() );

However, another reason to register with the broker is to ensure that your custom helpers are found. To this end, you can simply tell the helper broker the class prefix of your helpers, so it knows where to find them:
// By class prefix: Zend_Controller_Action_HelperBroker::addPrefix('Foo_Helper'); // Alternately, providing the path to classes with that prefix, if they // are not on the include_path: Zend_Controller_Action_HelperBroker::addPath($path, 'Foo_Helper');

Adding a path or prefix only tells the broker where to look for helpers it doesnt instantiate them. If you want a helper loaded before the dispatch cycle, so that event hooks can be utilized, you will still need to either add an instance of the helper to the broker, or attempt to retrieve it (which will create an instance). Which brings us to our next topic.

Retrieving Helpers from the Broker Statically


Sometimes you may find that you want to utilize an action helper outside an action controller perhaps to configure it, or because it offers functionality you need. The static method getStaticHelper() is used to perform this. As an example, I often find I need to configure the ViewRenderer for instance, to set some default view helper paths. I can do so as follows:
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); $viewRenderer->initView(); // make sure the view object is initialized $viewRenderer->view->addHelperPath($path); // set a helper path

Its a lot to type, but you typically wont need such functionality often. However, the functionality it provides is useful since only one instance of a particular helper can exist in the broker at any given time, you can be assured that youre configuring it globally.

Creating Your Own Helper


Action helpers should extend the
Zend_Controller_Action_Helper_Abstract

class. That class

contains the following utility methods:


setActionController(), for setting the current action

controller
getActionController(), for retrieving the current action

controller
getFrontController(), for retrieving the current front

controller instance getRequest(), for retrieving the current request object (uses the action controller first, then looks in the front controller) getResponse(), for retrieving the current response object (uses the action controller first, then looks in the front controller) getName(), to return the name of the helper

In addition, you can also define any of the event hook methods as listed previously. You can have your action helper do anything you want at this point. If it will have a common action, you may want to expose it via the direct() method, as detailed earlier. Otherwise, anything goes.

Example: Form Retrieval Helper


Now that weve learned about action helpers, lets create one. For our example, lets say you have a suite of controllers that each use one or more forms; furthermore, lets say that a given form may be used in multiple controllers. Were going to create a helper that allows you to fetch a form by class name. Well assume that form classes are stored in the forms subdirectory of the current module. Well also assume that they are namespaced with the current module (unless were in the default module), plus the prefix Form_; e.g., if we had a news module, forms would be prefixed with News_Form_. Finally well use the name passed to the helper to determine which form class to load, using the prefix. Well primarily use the direct() method to interact with the helper, as we only really have one thing we want the helper to do load forms.
/** * Action Helper for loading forms * * @uses Zend_Controller_Action_Helper_Abstract */ class My_Helper_FormLoader extends Zend_Controller_Action_Helper_Abstract { /** * @var Zend_Loader_PluginLoader */ public $pluginLoader; /** * Constructor: initialize plugin loader * * @return void */ public function __construct() { $this->pluginLoader = new Zend_Loader_PluginLoader(); } /** * Load a form with the provided options

* * @param string $name * @param array|Zend_Config $options * @return Zend_Form */ public function loadForm($name, $options = null) { $module = $this->getRequest()->getModuleName(); $front = $this->getFrontController(); $default = $front->getDispatcher() ->getDefaultModule(); if (empty($module)) { $module = $default; } $moduleDirectory = $front->getControllerDirectory($module); $formsDirectory = dirname($moduleDirectory) . '/forms'; $prefix = (('default' == $module) ? '' : ucfirst($module) . '_') . 'Form_'; $this->pluginLoader->addPrefixPath($prefix, $formsDirectory); $name = ucfirst((string) $name); $formClass = $this->pluginLoader->load($name); return new $formClass($options); } /** * Strategy pattern: call helper as broker method * * @param string $name * @param array|Zend_Config $options * @return Zend_Form */ public function direct($name, $options = null) { return $this->loadForm($name, $options); }

Place the above in a file named FormLoader.php in the My/Helper/ directory of your library (or any directory on your include_path). Okay, now, how would we use it? Lets assume were in the LoginController in the default module, and want to load the login form. Wed name it Form_Login, and place the class file in forms/Login.php in our application directory:
application/ controllers/ LoginController.php forms/ Login.php - Contains class 'Form_Login'

In our bootstrap file or an early-running plugin, wed make sure that we tell the broker where to find our helpers:
Zend_Controller_Action_HelperBroker::addPrefix('My_Helper');

And, finally, in our controller, we can now grab our form using the helper:
$loginForm = $this->_helper->formLoader('login');

Seem like a lot of work to simply load a form? Consider this: as long as you follow the rules outlined by our FormLoader helper, you can now use this in all your action controllers. So, you may have a UserController, and need to grab the registration form:
$regForm = $this->_helper->formLoader('registration');

Additionally, once youve registered a particular helper prefix (e.g., My_Helper), you can drop other helpers in that same location, and theyll automatically be found by the broker you wont have additional setup past that original call to add the prefix to the broker. The point here is that action helpers help you DRY up your code you push the bits and pieces you think youll use again and again in your controllers to your action helpers. After a while, youll have a library of controller-related functionality that you can draw on for other projects without needing your own, custom base class for action controllers, which ultimately leaves your library more extensible and flexible.