Contrary to what my tutorial says, I'm one of those people that doesn't want to have to render my common header and footer templates in every single view script file. I prefer a standard site-wide layout script file that will display the content of the action script files within it.
The Zend_Controller_Action_Helper_ViewRenderer action helper is a great bit of code that automates rendering a view template based on which action has been called. This is very useful, but renders the action template, not my layout template. To solve this, I am experimenting with extending the Zend_Controller_Action_Helper_ViewRenderer and overriding it so that it know about my layout template. I also prefer to use the view suffix "tpl.php" for my view scripts, so I've made my class automatically set my preferred view suffix.
On to the code…
The master site template is called site.tpl.php and lives in the views/scripts/ directory. A simplistic breakdown of it looks like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8″ />
<title><?php echo $this->escape($this->pageTitle);?></title>
<link rel="stylesheet" href="<?php echo $this->baseUrl; ?>/css/site.css"
type="text/css" media="screen" />
</head>
<body>
<div id="header">
<?php echo $this->menu(); /* menu view helper */ ?>
</div>
<div id="content">
<?php echo $this->render($this->actionScript); ?>
</div>
<div id="footer">
Copyright 2007 Rob Allen
</div>
</body>
</html>
$this->actionScript is the script associated with the current action which is automatically determined by Zend_Controller_Action_Helper_ViewRenderer, so in the case of the index action with the index controller, the action script is views/scripts/index/index.tpl.php.
As Zend_Controller_Action_Helper_ViewRenderer doesn't know about site.tpl.php and we want to render that instead, we extend like this:
<?php
class Controller_Action_Helper_ViewRenderer
extends Zend_Controller_Action_Helper_ViewRenderer
{
/**
* Name of layout script to render. Defaults to 'site.tpl.php'.
*
* @var string
*/
protected $_layoutScript = 'site.tpl.php';
/**
* Constructor
*
* Set the viewSuffix to "tpl.php" unless a viewSuffix option is
* provided in the $options parameter.
*
* @param Zend_View_Interface $view
* @param array $options
* @return void
*/
public function __construct(Zend_View_Interface $view = null,
array $options = array())
{
if (!isset($options['viewSuffix'])) {
$options['viewSuffix'] = 'tpl.php';
}
parent::__construct($view, $options);
}
/**
* Set the layout script to be rendered.
*
* @param string $script
*/
public function setLayoutScript($script)
{
$this->_layoutScript = $script;
}
/**
* Retreive the name of the layout script to be rendered.
*
* @return string
*/
public function getLayoutScript()
{
return $this->_layoutScript;
}
/**
* Render the action script and assign the the view for use
* in the layout script. Render the layout script and append
* to the Response's body.
*
* @param string $script
* @param string $name
*/
public function renderScript($script, $name = null)
{
$this->view->baseUrl = $this->_request->getBaseUrl();
if (null === $name) {
$name = $this->getResponseSegment();
}
// assign action script name to view.
$this->view->actionScript = $script;
// render layout script and append to Response's body
$layoutScript = $this->getLayoutScript();
$layoutContent = $this->view->render($layoutScript);
$this->getResponse()->appendBody($layoutContent, $name);
$this->setNoRender();
}
}
All we need to do now is modify our bootstrap so that the new ViewRenderer is used rather than the default one. I do this in index.php before the first instantiation of the front controller:
<?php
// Use our ViewRenderer action helper
$viewRenderer = new Controller_Action_Helper_ViewRenderer();
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
That's all there is to it. My action scripts now just contain the HTML specific to the action and my layout template is automatically rendered with the action's HTML in the right place!
Obviously this is my first cut on this and so is fairly rough at the edges. Thoughts and improvements always welcome!