Automatically prepare your pages with Zend Framework 1

When you make a new website or work on a new project, you want to keep it as easy as possible to add new pages. Zend Framework 1 offers a few ways to do this, for example if all the actions in your controller share some common code, you would put that in the controller’s init() function. But what if your whole site shares some common code?

In my case, such common code was to add CSS and JS files automatically using the headMeta and headScript view helpers. In my projects I usually include default CSS and JS files which are applied site-wide, and then controller-level CSS and JS files, which apply to all the actions within the same controller, and then the action-level CSS and JS files, which apply only to the given action. I would for example have these JS files:

public/js/
├── default.js
├── index
│   └── index.js
└── index.js

When I started with Zend Framework, I was overwhelmed with all the possibilities it had to offer. Plugins, action helpers, view helpers, pre-dispatch, post-dispatch, there were so many things I had to consider. I used an action helper, called Initializer, to execute common code in my controller classes, by calling it in the init() function:

class IndexController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->Initializer();
    }
}

This worked well, but I found it cumbersome to have to call a separate helper to do this. I thought this could be simpler and done more elegantly. And yes, there is a way to do it more elegantly, if you know OOP.

I made a new class, which extends Zend_Controller_Action, and let my controller classes extend from this new class. In this example, the class is put in the models directory, but it is much better to put it in a separate library, which I am planning to make – a Zend Framework Extension library.

class ZF1_Model_Controller_Base extends Zend_Controller_Action
{
    /**
     * Constructor
     *
     * After calling the parent constructor, this constructor
     * will take care of the default resource files (CSS and
     * JavaScript) to be included, using the default view
     * helpers.
     *
     * @param Zend_Controller_Request_Abstract $request
     * @param Zend_Controller_Response_Abstract $response
     * @param array $invokeArgs
     * @return void
     */
    public function __construct(
        Zend_Controller_Request_Abstract $request,
        Zend_Controller_Response_Abstract $response,
        array $invokeArgs = array()
    )
    {
        parent::__construct($request, $response, $invokeArgs);
 
        // Initialize view
        if (!isset($this->view)) {
            $this->initView();
        }
        // Get controller and action names
        $controller = $this->getRequest()->getControllerName();
        $action = $this->getRequest()->getActionName();
 
        // Automatically add CSS and JS
        $headLink = $this->view->headLink();
        $headLink->appendStylesheet('/css/default.css');
        $headLink->appendStylesheet('/css/' . $controller . '.css');
        $headLink->appendStylesheet('/css/' . $controller . '/' . $action . '.css');
 
        $headScript = $this->view->headScript();
        $headScript->appendFile('/js/default.js');
        $headScript->appendFile('/js/' . $controller . '.js');
        $headScript->appendFile('/js/' . $controller . '/' . $action . '.js');
    }
 
    /**
     * Pre-dispatch routine
     *
     * This will replace the default pre-dispatch routine and call
     * the action-level pre-dispatch function, if it exists. This
     * gives us more flexibility in controlling the process flow.
     *
     * @return void
     */
    public function preDispatch()
    {
        $action = $this->getRequest()->getActionName();
 
        $func = 'pre' . ucfirst(strtolower($action)) . 'Action';
        if (method_exists($this, $func)) $this->$func();
    }
 
    /**
     * Post-dispatch routine
     *
     * This will replace the default post-dispatch routine and call
     * the action-level post-dispatch function, if it exists. This
     * gives us more flexibility in controlling the process flow.
     *
     * @return void
     */
    public function postDispatch()
    {
        $action = $this->getRequest()->getActionName();
 
        $func = 'post' . ucfirst(strtolower($action)) . 'Action';
        if (method_exists($this, $func)) $this->$func();
    }
}

As you can see, the CSS and JS files are added in the constructor of this class, using the headMeta and headScript view helpers. Additionally, this class also enables the main controller classes to define action-level pre- and post-dispatch functions, so you will be able to control the flow on a higher level. If you use this class as a base class for your IndexController, opening the index action on your project, will first process the init() function of your controller class, then the preIndexAction() function, if it exists, then the main indexAction() function, and then the postIndexAction() function if it exists. This is rather useful if you need the controller to respond to or hint at the action helpers’ pre- and post-dispatch functions.

To use this class as the base class for your controllers, just extend your controllers from this class. For example, your IndexController would be as follows:

class IndexController extends ZF1_Model_Controller_Base
{
}

This is much more elegant, don’t you think? If you have any ideas or suggestions for implementation or improvement, please do share!

What are your thoughts?