Zend Framework View Helpers
I can’t seem to find an article here that consolidates my thoughts on Zend Framework’s view helper system, so I thought I’d better correct that. Zend Framework’s Zend_View component supports helper methods known as view helpers. They are used like this in a view script:
<?php echo $this->myHelper('myParam1'); ?>
Behind the scenes, this is implemented as a method within a class something like this:
<?php
class Zend_View_Helper_MyHelper extends Zend_View_Helper_Abstract
{
public function myHelper($myParam1)
{
$html = '';
// some logic that fills in $html.
return $html;
}
}
Note that by convention, view helpers return the string and then the view script echos it and the Zend_View_Helper_ section of the class name is known as the prefix.
A typical Zend Framework project using Zend_Application, such as that generated using the zf command line tool, will have a folder called helpers within the views folder for each module. There will also be a helpers folder within the layouts folder too. If you place your view helper in one of these helpers folders, then the prefix is Zend_View_Helper_.
We can also tell the view object about our own view helper folder. Typically this will live in the library/App/View/Helper/ folder and so, you probably want a prefix of App_View_Helper_. To do this, we simply add this line to application.ini:
resources.view.helperPath.App_View_Helper_ = "App/View/Helper/"
The view helper now lives in App/View/Helper/MyHelper.php and looks like this:
<?php
class App_View_Helper_MyHelper extends Zend_View_Helper_Abstract
{
public function myHelper($myParam1)
{
$html = '';
// some logic that fills in $html.
return $html;
}
}
Due to the way Zend Framework’s plugin loader works, our view script doesn’t change at all as the view object’s plugin loader will look across all registered paths to find the view helper specified in the view script.
I tend to prefer using the App folder over the helpers folder within layouts as I like to have one place to go to for my “site-wide” classes and so my App folder contains front controller plugins, validators, filters, form elements as well.
As you have extended Zend_View_Helper_Abstract for creating your own helper, you have access to the view object itself too. This is helpful for accessing other view helpers, such as escape. My personal preference is for my view helpers to manage their own escaping as requires as that way I can include html tags within my helper.
For example, suppose we want to create a table from an array that looks like this:
$data = array();
$data[] = array('Name', 'Email');
$data[] = array('Alison', 'alison@example.com');
$data[] = array('Bert', 'bert@example.com');
$data[] = array('Charlie', 'charlie@example.com');
Our view script may look like this:
<?php echo $this->tabulate($this->data, array('class'=>'tabulated')); ?>
(assuming our designer wants a class name attached to the table for styling.)
The view helper itself would then look something like:
<?php
class App_View_Helper_Tabulate extends Zend_View_Helper_Abstract
{
public function tabulate ($data, $attribs = array())
{
$attribString = '';
foreach ($attribs as $key => $value) {
$attribString .= ' ' . $key .'="' . $value . '"';
}
$header = array_shift($data);
$html = "<table $attribString>\n<tr>\n";
foreach ($header as $cell) {
$escapedCell = $this->view->escape($cell);
$html .= "<th>$escapedCell</th>\n";
}
$html .= "</tr>\n";
foreach ($data as $row) {
$html .= "<tr>\n";
foreach ($row as $cell) {
$escapedCell = $this->view->escape($cell);
$html .= "<td>$escapedCell</td>\n";
}
$html .= "</tr>\n";
}
$html .= '</table>';
return $html;
}
}
As you can see, we cannot escape the output of the view helper, so the view helper has to do its own escaping via the view property.
If you end up using the same set of view helpers on each project you do, then consider putting then into a vendor library. This is the same concept as using the App folder, but you use a different name (I use Akrabat) and store them in a separate vcs repository. This allows you easy reuse. You add a new line to application.ini to pick them up:
resources.view.helperPath.Akrabat_View_Helper_ = "Akrabat/View/Helper/"
and you’re done. Note that the order of the helperPath in application.ini is important. You want the vendor path before the App path so that you can override your vendor ones specifically within a project if you need to.
resources.view.helperPath.Akrabat_View_Helper_ = "Akrabat/View/Helper/"
resources.view.helperPath.App_View_Helper_ = "App/View/Helper/"
Plugin helper path order is one of the more frustrating things about the plugin loaders used within Zend Framework and affects form elements, validators, filters and view helpers to name but a few. So if you discover that a view helper isn’t being called when you think it ought to be, then check that it’s not being overridden further down the chain. You can of course use this to your advantage and override a default Zend Framework view helper by simply creating an App version with the same name.
Is it just me or does the App_View_Helper_Tabulate class seem to have to many table element tags? :)
It's you I think…
I think XeroXer's referring to a couple of typos.
There appears to be a spurious html table tag immediately after the closing bracket of the App_View_Helper_Tabulate definition.
Also the last $html concatenation opens a table tag, when I guess it should just be closing it.
Doesn't detract from the point of a useful article though.
@Rob
This
$html .= '';
should be:
$html .= '';
Regards
So the ending line with:
}
is as it should be?
An ending table element tag inside php code seems fishy to me…
And when you end the table with:
$html .= ";
should the table element really be started again?
Hmm,
No HTML allowed.
In your last $html concatenation you have two table tags.
Regards
Or I could just share: http://pastebin.com/diff.php?i=ycYfVx7g
Sorry XeroXer. Fixed.
How about "widgets", the elements that are reusable across the view? For example "top contributers list" which will be shown in multiple pages…
We can use either helpers or partial views for that… The problem is, when you need to access the "model" it's hard to seperate the bussiness logic from the presentation logic…
If we use helpers, how do we access the model? And I'd expect a seperate phtml file for the presentation part instead of a class file…
If we use partials, we have to pull the data in the controller, assign it to it's corresponding view and then to the partial view which is definitely not a good practice I believe… You have to do that in every parent controller that uses the widget…
So what I did was to write a "loadWidget" helper class which runs a php file. It pulls the data from the model and assigns it to a view file and renders it…
None of the solutions above fully satisfied me so I wanted to hear your opinion about that…
The Zend_View_Helper example works when I place the helper class in application/views/helpers folder, however the App_View_Helper example does not work leaving me with a blank screen.
My helper class: Foobar.php
<?php
//class Zend_View_Helper_FooBar extends Zend_View_Helper_Abstract {
class App_View_Helper_FooBar extends Zend_View_Helper_Abstract {
public function fooBar() {
return "This is the fooBar() function.";
}
}
1. I changed the helper class name to: "App_View_Helper_fooBar"
2. The class extends: "Zend_View_Helper_Abstract"
3. I placed the line: resources.view.helperPath.App_View_Helper_ = "App/View/Helper/" into my application.ini file (as the last line)
4. I created folders: /library/App/View/Helper/ and placed my helper class file into it.
5. In "appliaction/views/scripts/index.phtml I added the line:
I'm sure I followed your instructions correctly, How do I get it to work?
John