Zend Framework: Using View Helpers to Build Rich, Scalable, Controls
Using View Helpers to Build Rich, Scalable, Controls
by: Jon Lebensold
Whether you’re developing an ASP.NET application, working with rails or dealing with a templating engine like Smarty, the idea of partial templates is not foreign. Partial templates allow developers and designers to work with panels or sub-groupings of content that need to be dynamic.
With a proper AJAX framework in place, these controls can be written in a way that they can be updated in whichever Controller they eventually reside. This kind of flexibility can allow you the flexibility of using certain signature controls for multiple parts of an application.
For example, you could develop an AJAX comment box that would write comments for articles and blog posts but would hit different database tables based on it’s constructer. The rest of the control would remain the same, resulting in a more consistent look and feel for your users.
View Helpers are not partials in the Ruby on Rails sense since they’re is view logic associated with them and they don’t explicitly have .phtml files. View Helpers also aren’t like wordpress templates, with content broken up into different parts (a good example of this kind of compositional design will be coming in the Zend_Layout package to be released soon).
View Helpers seem to resemble the user control concept of in .NET, where a panel can be dropped into any form, with its codebehind managing any events or user-control specific display code.
In summary, View Helpers are great for encapsulating forms, grids and other functionality that could eventually be bound to a different data source or even be pushed back to the user through a simple AJAX call.
According to the samples provided in the Zend documentation, View Helpers seem to mix presentation with logic. My assumption is this was intended to limit the creation of Zend_View objects since Zend_View can load View Helpers dynamically.
The View Helper sample provided in the zend documentation looks like this:
class My_View_Helper_SpecialPurpose { protected $_count = 0; public function specialPurpose() { $this->_count++; $output = "I have seen 'The Jerk' {$this->_count} time(s)."; return htmlspecialchars($output); } } |
While this works for simple output, where a View Helper would be interacting with the calling object (which is typically the Zend_View), this approach doesn’t lend itself to doing rich partial templating.
As long as you keep the Helpers in one phtml file, you could add a “helpers” folder to your document tree and render the View Helper like any other view. The advantage of this is that factoring out part of your template into an AJAX control becomes really trivial. Furthermore, your display and view logic are separated, and lastly, you could even test the View Helper (including its behaviour) without a particular controller action in mind.
These javascript samples could be written more elegantly by loading the javascript through a referenced javascript file file that is tailored to a particular part of an application.
The actual index.phtml page could look something like this:
<script type="text/javascript"> // this is generic enough that it could be pushed into an external .js file function loadMyPost(id) { if ($(id+'_MyPostForm').title.value.length > 0) var params = 'title='+$(id+'_MyPostForm').title.value; new Ajax.Request('/ajax/MyPost', { method: 'post', parameters: params, onSuccess: function(transport, json) { //use and handle foo response data $(id+'_MyPostForm').insert(transport.responseText); }, on500: function(transport) { //handle error, inform user alert('error'); } }); } </script> <?= $this->MyPost(""); ?> <?= $this->MyPost(""); ?> |
Notice how I’ve called MyPost twice? each one of these view helpers will have its own javascript configuration and will fire independently of it’s predecessor, thanks to View Helpers being able to call their inheritor to see how many times they’ve been invoked on a page.
The actual helpers/MyPost.phtml page would then look like this:
<script type="text/javascript"> function ajaxLoad_<?=$this->formId ?>(event) { loadMyPost(<?=$this->formId ?>); return false; } </script> <div> <form id="<?php echo $this->formId; ?>_MyPostForm" action="/index" method="post" onsubmit="return ajaxLoad_<?=$this->formId; ?>(this);" > <?= $this->formText('title', $this->title, array('size' => 32) ); ?> <?= $this->formSubmit('POST!', "" ); ?> <?= $this->title; ?> </form> </div> |
Notice that the View Helper itself is using another View Helper (formSubmit) to simplify the submit button on the actual form. We could easily extend this example with lookups that would dictate the visibility of certain portions of the View Helper. This way, the View Helper is responsible for rendering logic and it can be given a set of data to render the View by, or rather, it can “call out” in the actual Zend_View_Helper_MyPost class.
MyPost.php (the code-behind for the View Helper):
class Zend_View_Helper_MyPost { protected $_formId = 0; public function MyPost($title) { $view = new Zend_View(); $view->setBasePath(BASEPATH); $view->formId = ++$this->formId; $view->title = $title; return $view->render("helpers/MyPost.phtml"); } } |
This is an example of a form that could be dynamically posted from any page in your application.
Notice how the javascript uses the “formId” (which could be derived through using the same method as $this_count++ in the previous example) to maintain uniqueness. This enables the creation of multiple instances of the View Helper on the page, all bound to their own javascript event listeners or custom javascript.
Using prototype, you can submit directly to that particular version of the ajaxMethod or even post the View Helpers’ form to a Controller that supports Ajax Postbacks. Luckily, proposals like Zend_Controller_Action_Helper_AjaxContext intend to facilitate this process of managing how a view is rendered from a controller. In this particular example, I’ve created a custom AjaxController for my application that looks like this:
function MyPostAction() { /* [Xml response] */ $responseDataXmlEncoded = '<div>' . $this->_request->getParam('title') . '</div>'; $this->getResponse()->setHeader('Content-Type', 'text/xml') ->setBody($responseDataXmlEncoded); } |
The custom View Helper will post to MyPostAction where the standard Zend_Controller methods can be used to collect the postback and return an XML result. This way, you can easily migrate existing postback code into an AJAX-enabled view-helper. MyPostAction has been configured to return xml instead of a regular phtml template. In order for this to work, you need to disable default rendering in the init() function of the AjaxController:
function init() { $this->_helper->viewRenderer->setNoRender(); } |
The same technique can be applied to a JSON formatted response, where prototype (or any other javascript framework one of your choice) could be used to pick up the response and create a javascript “object” that’s ready for rendering.
Pingback: Jon Lebensold » Blog Archive » Article on Zend View Helpers
This is a great post! I am in need of partials in my project and this post set me on the right path to figuring out how to do it. I’m not using AJAX but this idea is very useful. The only thing I am doing differently is that instead of instantiating a new Zend_View and setting the basepath in my View Helper I am retrieving the View Helper that is already initialized in my View Renderer like this:
$view = Zend_Controller_Action_HelperBroker::getExistingHelper(‘viewRenderer’)->view;
Then you can set variables for your view scripts and render them like this:
$view->someVar = “someValue”;
$out = $view->render(“path/to/viewScript.phtml”);
return $out;
This saves you from having to create a brand new Zend_View and trying to figure out how to set the basepath in a portable way. You do need to be mindful of the fact that you aren’t creating a new namespace for your partial so be ware of variable name clashes. Since you are reusing the same view your controller is using, anything you set using $this->view->whatever will also be available in your partial view script as $this->whatever.
@Lee: Thanks for mentioning this! I was looking for a way to call View Helpers without creating a whole new Zend_View or having to create custom classes that would be called from a view helper only for some instances.
Pingback: Zend Framework: Using View Helpers to Build Rich, Scalable, Controls
I have used this method with various php frameworks many times. I actually prefer breaking up my templates and following a half ‘naked objects’ approach where the actual item that you are working with contains the templates and business logic.
You dont need to instance a new Zend_view object, just define a setView method in your helper, like this:
public $view;
public function setView(Zend_View_Interface $view)
{
$this->view = $view;
}