Zend Framework’s MVC Introduces the ViewRenderer

May 25, 2007

News, Zend Framework

One of my roles at Zend is as a Zend Framework developer, and as such, I’m
the lead developer on the MVC components. Yesterday, I committed a change to
core in preparation for the 1.0.0RC1 release that, while breaking backwards
compatability slightly, will also greatly simplify the integration between
the controller and view components.

First, some history.

In the 0.9.x series of ZF, we introduced some rudimentary View
integration into Zend_Controller_Action with the methods initView(),
getViewScript(), and render(). With these methods, you could quickly and
easily instantiate view objects as well as render view scripts to the
response object.

In 0.9.3, we introduced Action Helpers. Similar to view helpers, these
can be loaded on-demand by action controllers to provide extra
functionality. They also have pre- and postDispatch() hooks such that
globally registered helpers can hook into dispatched action controllers.

One developer, Ralph Schindler, felt it didn’t go far enough, and posted
an issue in the
framework issue tracker
detailing an idea he had
for adding more integrated View dependency injection to
Zend_Controller_Action. After some thought and discussion with him, I
countered with a new action helper, the ViewRenderer.

The ViewRenderer’s goals are:

  • Eliminate the need to instantiate view objects within controllers.
  • Have a view object globally available to all controllers.
  • Automatically determine view script, helper, and filter paths based
    on the current module’s filesystem location.
  • Automatically associate the current module name as a class prefix
    for view helper and filter classes.
  • Allow the developer to set default view rendering options for all
    controllers or a single action.
  • Add the ability for developers to create their own path
    specifications for determining the view base path and individual
    view script paths.
  • Add the ability to autorender view scripts based on the current
    action.

What does this mean to you Zend Framework users? Basically, it means less
coding for you, the developer, in your controller classes.

It means that instead of this:

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        $this->initView();        // initialize view
        $this->view->baz = 'bat'; // set view variable
        $this->render();          // render view
    }
}

You would then do this:

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        $this->view->baz = 'bat'; // set view variable
    }
}

In other words, you’re just setting view variables; you don’t need to
worry about initializing the view object, or rendering it. The
ViewRenderer automatically determines the view script path (by assuming
a conventional modular layout), and renders ‘foo/bar.phtml’ (the default
view script path is :controller/:action.phtml’).

So, how will this affect your existing controllers?

If you call $this->render(), $this->_forward(), or
$this->_redirect() (or use the Redirector helper) in your action methods,
no changes are required.

If you have action methods that call none of the above — i.e., you’re
not redirecting, not forwarding to a new method, and/or not rendering
via the built-in render() method — you will need to make changes. You have
a few strategies, based on how your applications are structured.

First off, as an interim solution, you can globally disable the ViewRenderer
in your site bootstrap:

// Assuming $front is your front controller instance
$front->setParam('noViewRenderer', true);

This one line will keep your application working just as it always had.
However, long term, it means that you’ll be using more code.

When you’re ready to start using the ViewRenderer functionality, there are
several things to look for in your controller code. First, look at your
action methods (the methods ending in ‘Action’), and determine what each is
doing. If none of the following is happening, you’ll need to make changes:

  • Calls to $this->render()
  • Calls to $this->_forward()
  • Calls to $this->_redirect()
  • Calls to the Redirector action helper

The easiest change is to disable auto rendering for that method:

$this->_helper->viewRenderer->setNoRender()

If you find that none of your action methods are rendering,
forwarding, or redirecting, you will likely want to put the above line in
your preDispatch() or init() methods:

public function preDispatch()
{
    // disable view script autorendering
    $this->_helper->viewRenderer->setNoRender()
    // .. do other things...
}

If you are calling render(), and you’re using href="http://framework.zend.com/manual/en/zend.controller.modular.html">the
Conventional Modular directory structure, you’ll want to change your
code to make use of autorendering:

  • If you’re rendering multiple view scripts in a single action, you don’t
    need to change a thing.
  • If you’re simply calling render() with no arguments, you can remove such
    lines.
  • If you’re calling render() with arguments, and not doing any processing
    afterwards or rendering multiple view scripts, you can change these calls to
    read $this->_helper->viewRenderer();

If you’re not using the conventional modular directory structure, there are
a variety of methods for setting the view base path and script path
specifications so that you can make use of the ViewRenderer. Please
read the ViewRenderer documentation for information on these methods
(documentation is available via subversion at the time of this writing, but
will eventually be on the Zend Framework site).

If you’re using a view object from the registry, or customizing your
view object, or using a different view implementation, you’ll want to
inject the ViewRenderer with this object. This can be done easily at any
time.

  • Prior to first instantiating a front controller instance:
    // Assuming $view has already been defined
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
        

  • After retrieving a front controller instance:
    $viewRenderer = Zend_Controller_Action_HelperBroker::getExistingHelper('viewRenderer');
    $viewRenderer->setView($view);
        

There are many ways to modify the ViewRenderer, including setting a
different view script to render, specifying replacements for all replaceable
elements of a view script path (including the suffix), choosing a response
named segment to utilize, and more. If you aren’t using the conventional
modular directory structure, you can even associate different path
specifications with the ViewRenderer.

Documentation is now in the
repository
if you want to get a jump on this new feature. I strongly
encourage you to start modifying your controllers to make use of this
helper; I did so in a site of mine while developing this, and reduced my
controller code by > 20%, and eliminated several subclasses and plugins
I’d created.

– Matthew Weier O’Phinney
PHP Developer />Zend Technologies

About Matthew Weier O'Phinney

Matthew is an open source software architect, specializing in PHP. He is currently project lead for Zend Framework, a project with which he has been involved since before the first public preview release. He is a Zend Certified Engineer, and a member of the Zend Education Advisory Board, the group responsible for authoring the Zend Certification Exam. He contributes to a number of open source projects, blogs on PHP-related topics, and presents talks and tutorials related to PHP development and the projects to which he contributes. You can read more of his thoughts on his blog, weierophinney.net/matthew/.

View all posts by Matthew Weier O'Phinney

4 Responses to “Zend Framework’s MVC Introduces the ViewRenderer”

  1. describer Says:

    Just a quick reminder that the preDispatch function is missing the semicolon after the setNoRender() call. Maybe you can correct that and delete this comment.

  2. _____anonymous_____ Says:

    Hi ,

    Good Article .

    Can you plot out which is best to Use SMARTY or ZF’s ViewRenderer.

    Thanks ,
    Trinath Somanchi.

  3. _____anonymous_____ Says:

    Hi ,

    Good Article .

    Can you plot out which is best to Use SMARTY or ZF’s ViewRenderer.

    Thanks ,
    Trinath Somanchi.

  4. _____anonymous_____ Says:

    what does this error mean?
    PHP Strict Standards: Creating default object from empty value in /path/to/app/default/controllers/IndexController.php on line 6

    line 6 is simply
    $this->view->testvar = ‘something’;
    which appears inside the indexAction() method
    eg.
    class IndexController extends Zend_Controller_Action {
    public function indexAction {
    $this->view->testvar = ‘something’;
    }
    }

    bootstrap looks like
    Zend_Loader::loadClass(‘Zend_Controller_Front’);

    try {
    $controller = Zend_Controller_Front::getInstance();
    $controller->setControllerDirectory(/path/to/controllers/);
    $controller->dispatch();
    }
    catch (Exception $e) {
    }

    i just created a new project following the directory structure suggested, except that i put views in the default module, as i don’t want to separate into modules

    i have no helpers, or view renderer code
    this is a just a simple test, but doesn’t seem to work

    dave