Helpers and plugins

by Naneau

For people new to the Zend Framework the way it implements the model view controller (MVC) pattern may be a bit confusing. While the basic structure couldn’t be easier, it’s the details that cause problems. I’m going to have a go at trying to explain some of them here. I hope it helps. If it doesn’t, blame SpotSec ;) !

Assuming you’re at least somewhat familiar with the concept of MVC, there is Zend_Db_Table for your models, Zend_View for your views, and Zend_Controller_Action to glue them together. Those are the 3 basic classes you’ll find yourself working with over and over again. Worthy of mention is also Zend_Controller_Front, which dispatches an action controller after it interprets a request in your bootstrap file.

If you understand the basics of how the front controller, and it’s default router, work, it’s easy to set up some action controllers, let them render a view and build basic applications. While you don’t necessarily need them, there are additions to the 3 big components of the framework’s MVC implementation that can make your life a little easier.

View Helpers

As soon as you start writing views that consist of more than just text and a little dressing HTML, you’re going to run into view helpers. The framework comes with a set of handy helpers, that you may already use. But what exactly are those helpers, and why should you write one?

Basically a view helper does what it’s name suggests. It’s something that helps you write views. For instance, in the default set of helpers there are helpers that generate form elements and urls. If you find yourself writing the same bit of HTML over and over again in your views, why not build a helper for it? They are very easy to create, consisting of nothing more than a class and a single method.

From view context you can call them using:

1
<?php echo $this->aViewHelper($someVar, $anotherVar); ?>

Where the helper is nothing more than:

1
2
3
4
5
class Naneau_View_Helper_AViewHelper {
    function aViewHelper($someVar, $anotherVar) {
         return '<strong>' . $someVar . '</strong><em>' . $anotherVar . '</em>';
    }
}

Action Helpers

What a view helper is for views, an action helper is for action controllers. They are a bit more complex, but very powerful. If you’ve been around long enough to remember the introduction of the infamous viewRenderer, you already have experience with action helpers. They can perform tasks your controller actions do, and help avoid repetition of mundane controller tasks.

They are more complex than view helpers, because they work with a concept of “hooks”. A hook is a method you can write into your action helpers that gets called at specific moment in a controller’s life cycle. As you may or may not know, an action controller gets instantiated by the front controller and the method matching the action from the request will be called. An action helper has hooks for three moments during that cycle.

There’s init(), which gets called by the helper broker before the controller is instantiated, preDispatch() and postDispatch(), which are called before and after the action method of the controller is called, respectively. From action helper context you can do everything you can do from controller context, including modifying the request and response. You can also retrieve the controller object that you are “helping”, and work with that.

In your controllers all the helpers currently registered are in the $this->_helper array. The viewRenderer action helper, for instance, can be found in $this->_helper->viewRenderer. Action helpers can be set up in your bootstrap using the action helper broker.

For instance, say that you have some variables Foo and Bar, that you want to be available in all your action controllers. For instance, a database connection or the current user would be things you would want access to at all times. You could write a simple action helper that does this:

1
2
3
4
5
6
7
8
9
10
class Naneau_Controller_Action_Helper_FooBar  extends Zend_Controller_Action_Helper_Abstract {
    public function preDispatch() {
        //get $foo and $bar
        //think Zend_Registry
        $controller = $this->getActionController();
        //the current action controller
        $controller->foo = $foo;
        $controller->bar = $bar;
    }
}

In controller context, you could then retrieve them with:

1
2
3
4
$this->foo;
//$foo
$this->bar;
//$bar

Front Controller Plugins

If you find your action helpers lack power, there’s always the option of writing a front controller plugin. They have the same preDispatch() and postDispatch() methods an action helper has, although you can’t modify the action controller directly from a plugin. In addition to those it provides you with a few other “hooks”.

routeStartup() and routeShutdown() are called before and after the front controller starts evaluating it’s request and start looking for routes that match it. dispatchLoopStartup() and dispatchLoopShutdown() give you access to the point in the lifecycle of the front controller where it starts dispatching controllers, and when it stops dispatching them. There’s a subtle difference with preDispatch() and postDispatch(), because those will be called before every single controller action is dispatched.

While plugins are the most scary looking of the three, they are the most powerful. They have the power of stopping a dispatch, so they can be used for access control. Also, if you must insert some variables into the request before anything happens, they are the way to go. Don’t be scared by it’s apparent complexity though, once you get the hang of it you’ll love the control they give you.

If you’d want to force people to log in before they can view your pages, it would make sense to write a plugin for that. Repeating the check for a valid login in every action controller would lead to a lot of repetition. A simple plugin that would check for a login could look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Naneau_Plugin_CheckLogin extends Zend_Controller_Plugin_Abstract {
    public function preDispatch() {
        $login = false;
        //you will probably want to implement this ;)
        if (!$login) {
            //no login
            $request = $this->getRequest();
            //the request
            $request->setModuleName('default');
            $request->setControllerName('login');
            $request->setActionName('index');
            //send to default/login/index
        }
    }
}