Smarty + Zend_View, take three
Tuesday, 31. July 2007, 22:21:01
So here's take three on integrating Smarty with Zend_View, this time hopefully staying useful for a longer time since the Zend Framework has reached version 1 now.
In this post I will show you a way to integrate Smarty with Zend_View as a new subclass and how to use this class with the new ViewRenderer helper to automatically display templates.
So let's begin with the implementation of SmartyView, the new class based on Zend_View_Abstract that uses Smarty as the template engine.
<?php
class SmartyView extends Zend_View_Abstract
{
protected $_smarty;
public function __construct($config = array())
{
$this->_smarty = new Smarty();
if(!isset($config['compileDir']))
throw new Exception('compileDir is not set for '.get_class($this));
else
$this->_smarty->compile_dir = $config['compileDir'];
if(isset($config['configDir']))
$this->_smarty->config_dir = $config['configDir'];
if(isset($config['pluginDir']))
$this->_smarty->plugin_dir[] = $config['pluginDir'];
parent::__construct($config);
}
public function getEngine()
{
return $this->_smarty;
}
public function __set($key,$val)
{
$this->_smarty->assign($key,$val);
}
public function __isset($key)
{
$var = $this->_smarty->get_template_vars($key);
if($var)
return true;
return false;
}
public function __unset($key)
{
$this->_smarty->clear_assign($key);
}
public function assign($spec,$value = null)
{
if($value === null)
$this->_smarty->assign($spec);
else
$this->_smarty->assign($spec,$value);
}
public function clearVars()
{
$this->_smarty->clear_all_assign();
}
protected function _run()
{
$this->strictVars(true);
$this->_smarty->assign_by_ref('this',$this);
$templateDirs = $this->getScriptPaths();
$file = substr(func_get_arg(0),strlen($templateDirs[0]));
$this->_smarty->template_dir = $templateDirs[0];
$this->_smarty->compile_id = $templateDirs[0];
echo $this->_smarty->fetch($file);
}
}
?>
Now that we have that, it's only a small snippet of code to our index.php / front controller file to do the rest:
//Create the view and set the compile dir to template_c
$view = new SmartyView(array(
'compileDir' => './template_c'
));
//Create a new ViewRenderer helper and assign our newly
//created SmartyView object as the view instance
$viewHelper = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewHelper->setViewSuffix('tpl');
//Save the helper to the HelperBroker
Zend_Controller_Action_HelperBroker::addHelper($viewHelper);
With that code the view will autorender your templates. Also, you can assign the smarty instance as a parameter to the request and still use it as you would normally use Smarty and assign template variables etc. and they will be reflected in the automatically rendered template.
That is all. Simpler than you expected?








Anonymous # 23. September 2007, 14:14
Its really working with new updated Zend.
Great work here. Thanks
Anonymous # 2. October 2007, 21:08
I don't know if I am doing anything wrong, but when using a bootstrap file like the one on http://akrabat.com/zend-framework-tutorial/ it works okay.
until you introduce modules (like I did). Even there everything works fine, until you start using duplicate controller/action combo's. like index/index. it will use the cached version.
the problem lies in the _run(), because it strips the directories from the file and sets the template_dir to the module's view dir.
Doing this, smarty thinks that it's the same file.
zomg # 2. October 2007, 21:16
Add this to SmartyView class, inside _run function after $templateDirs = $this->getScriptPaths();
Anonymous # 2. October 2007, 21:28
WOW!:eyes: that was fast,
I've a, somewhat more larger fix.
$front = Zend_Controller_Front::getInstance();
$this->_smarty->compile_dir .= '/' . $front->getRequest()->getModuleName();
just before the
echo $this->_smarty->fetch($file);
Anonymous # 12. October 2007, 07:53
Thank you! I was able to integrate Smarty with ZF using your SmartyView. Now I don't have to use the registry anymore :)
Anonymous # 29. November 2007, 23:00
I might be silly, but how do I then assign variables? In my controller, what do I do where I might previously have had:
$this->view->pagename = "blah";
to have it populate within {$pagename} in my .tpl file?
zomg # 30. November 2007, 04:43
Anonymous # 6. December 2007, 22:39
Very cool. How can I make a logical decision on which tpl to display from within my controller?
My usual way of working is to have a file called layout.tpl which is static throughout a site, and then assign different tpl files to divs within that larger layout. But the way it is now, it seems that I have to follow the convention of displaying thing/edit.tpl as the main template to edit my thing object.
Best,
Matt W
Anonymous # 6. December 2007, 23:24
I modified your class to allow the default template to be over-ridden. Perhaps a little hack-y, but it will give me a little bit more flexibility if I so desire.
In my controller I do this:
$this->view->tpl = "test.tpl";
In SmartyView.php I added a member tpl:
class SmartyView extends Zend_View_Abstract
{
protected $_smarty;
public $_tpl;
========
and changed two methods:
========
public function __set($key,$val)
{
$this->_smarty->assign($key,$val);
if($key=="tpl"){
$this->_tpl = $val;
}
}
and:
protected function _run()
{
$this->strictVars(true);
$this->_smarty->assign_by_ref('this',$this);
$templateDirs = $this->getScriptPaths();
$this->_smarty->compile_id = $templateDirs[0];
if(isset($this->_tpl)){
$file = $this->_tpl;
} else {
$file = substr(func_get_arg(0),strlen($templateDirs[0]));
}
$this->_smarty->template_dir = $templateDirs[0];
$this->_smarty->compile_id = $templateDirs[0];
echo $this->_smarty->fetch($file);
}
=======
I don't know if this is useful to anyone, but I figured I'd throw it out there.
Best.
MattW
zomg # 9. December 2007, 15:03
Anonymous # 16. December 2007, 17:25
ViewRenderer you say... I'll check it out.
Also, there is a little typo in the class that prevents custom smarty plugins from working. It should be:
$this->_smarty->plugins_dir
instead of
$this->_smarty->plugin_dir
Or at least I had to make that change.
Best,
M
Anonymous # 14. February 2008, 19:23
I tried utilizing Zend_View's cascading system where you have multiple view script paths and that broke the SmartyView. I altered the method to allow this feature, but it makes a major assumptions about how things are set up: all of the 'skins' are under a single directory called "scripts"
protected function _run() {
$this->strictVars(true); // undefined variables used will trigger Notice
$this->_smarty->assign_by_ref('this',$this);
$templateFullPath = func_get_arg(0);
list($filepath, $file) = preg_split('/\/scripts\//', $templateFullPath, 2);
$filepath .= '/scripts/';
$this->_smarty->template_dir = $filepath;
$this->_smarty->compile_id = $filepath;
echo $this->_smarty->fetch($file);
}
So, lets say you have a 'default' skin in "scripts" and a customized skin in "scripts/custom". If you set the scriptpaths in the right order, then it'll look in "scripts/custom" first, and then drop back to "scripts" if it can't find one in there.
Anonymous # 14. February 2008, 19:51
ugh.. the above fouls up when you try to do {include}'s as both skins will search in the root 'scripts' directory
Anonymous # 10. March 2008, 22:14
One problem with this is that, since the view object is created in front controller, there is no simple way you can choose among different view objects based on the action (like HtmlView, FileDownloadView, JsonView...)
Anonymous # 30. September 2008, 15:16
BC: You should be able to manage context within the action controllers themselves, and then handle the logic.
yo dude, sick. drop it right in the viewrenderer! never even crossed my mind.