Part two of my ongoing look at the View layer of the Zend Framework turns its attention to the topic of View Helpers. The Zend Framework manual provides a fairly narrow definition of its helpers which indicates they enable complex tasks, like generating form elements, to be extracted out of views into dedicated helpers. Here I’ll try to explain in greater depth the View Helper pattern which is another of those patterns in the J2EE catalog, and which adds to the range of tasks View Helpers are capable of performing.

The simplest explanation is in the separation of layers enforced by the Model-View-Controller pattern. In a typical web application using the Zend Framework, a URI is used by the framework’s Controller architecture to select an action on a Zend_Controller_Action class to execute. This action method will then normally access the application’s Model to fetch any data it requires, operate on that data, and pass a subset of it to the View (for use in rendering templates). It’s clear to see so far, that the View by default relies on the Controller to pass it the data model it needs when rendering templates.

As in my previous post however, the View may also include any number of elements (header, footer, widgets, etc.) common to ALL pages. Many of these may require data of their own.

So what is the problem? Anytime a partial View needs extra data (the View’s Model) it needs to push calls to extra Controllers (following the current practice for the framework) in order to get that Model. This involves yet another complete dispatch cycle, with any number of classes, plugins, and operations involved. Yet in most cases this is completely unnecessary – why not just let the View request data from the Model directly? ;)

In simple terms, the View Helper acts as a middle-man sitting between the View and the Model. It’s job in our scenario is to replace the need to nest Controllers, and give that nesting/layout control back to where it belongs – the View layer. When a View (or partial View) requires Model data not supplied by the current controller, it calls upon a View Helper to go fetch that data independently of any Controller.

Let’s take a simple example. While building a blog, we’ve decided to add the last 5 entry titles from Planet PHP to the bottom of the page – every page in fact. In keeping with promoting reusability we create a generic Zps_View_Helper_Rss class which can consume RSS, and to which we can add extra methods in the future should they be needed for other Views. This is slightly different to the current view helper description in the framework manual – here we can offer a full selection of public methods forming a fuller (read-only) interface to the underlying Model – the RSS XML.

[geshi lang=php]require_once ‘Zend/Uri.php’;

require_once ‘Zend/Feed/Rss.php’;

class Zps_View_Helper_Rss
{

protected $_channel = null;

public function __construct($url)
{
if (!Zend_Uri::check($url)) {
throw new Exception(‘RSS URL is invalid!’);
}
$this->_channel = new Zend_Feed_Rss($url);
}

public function getTitles($number)
{
$titles = array();
$count = 0;
$number = intval($number);
// Zend_Feed_Abstract implements Iterator!
while ($count <= $number && $this->_channel->valid()) {
$titles[] = $this->_channel->current()->title();
$count++;
$this->_channel->next();
}

return $titles;
}

}[/geshi]
Now comes our sample template – one of those recurring Views:

[geshi lang=php]

    getTitles(5) as $title): ?>

[/geshi]
And not a controller in sight…;)

Of course the Model can equally be running from any datasource including the database, so there’s a lot of flexibility for these View Helper classes. I haven’t done so here, but you can probably still fit them into the current view helper convention with the single public method (in our case rss() just returning instances of the object.

Of course, there really are times when the recommended Controller _forward() (or a viable alternative) can still be useful. Reliance on plugins was suggested on the mailing list, for ACL perhaps. But the point here is that controller nesting is not required except for those exceptional cases where a View Helper just won’t cut it.

Edit: I figured it be save confusion by noting this helper could equally run from a cache of the selected RSS source which is updated separately at regular intervals (hail to cron!). Save the server making the trip on every request :) .

Related posts:

  1. Complex Web Pages with the Zend Framework?
  2. OpenID library for the Zend Framework?
  3. YAML for the Zend Framework – well, maybe…
  4. Zend_Yaml Proposal added
  5. Doing the Poka-Yoke