There are a number of articles already on the web showing how to use Smarty and Zend Framework (for example, this one by Andrea Belvedere and this one on Zend's devzone) but they usually have two problems
- They are usually quite old and out of date.
- They don't cover using both Zend_View and Zend_Layout.
Setting up
I'm going to assume that you're already comfortable setting up a new Zend Framework project using the Zend Tool, zf. If you're not, here is a good video showing how to do this in Netbeans 6.9. I'm also going to assume that you already have a working PHP development environment.
First thing is to download the Smarty library and copy it into the library directory in your project. When I did this, I renamed the Smarty directory to remove the version number, so that the Smarty code is actually in 'library/Smarty/libs/'.
For Smarty to work, it needs two additional directories, 'cache' and 'templates_c', on the top level of your project (on the same level as the directories called application, library and so on). These directories need to be readable and writeable to by PHP.
Next, we need to put some configuration values into the application's config file. In Zend Framework 1.10, this is a file call application.ini in the 'application/config' directory. Paste the following under [production]
smarty.dir = APPLICATION_PATH "/../library/Smarty/libs/" smarty.template_dir = APPLICATION_PATH "/views/scripts/" smarty.compile_dir = APPLICATION_PATH "/../templates_c" smarty.config_dir = APPLICATION_PATH "/configs" smarty.cache_dir = APPLICATION_PATH "/../cache" smarty.caching = 0 smarty.compile_check = trueThese lines will be used to tell the Smarty engine where to find things. In the .ini file, APPLICATION_PATH refers to the directory called application, where your controllers, views and models reside, so 'APPLICATION_PATH "/../templates_c"' means start at that directory, go up one level, and then find a directory called 'templates_c'.
Bootstrap and Zend_View
In older versions of the Zend Framework, it was normal to put bootstrapping code into the index.php file in the public directory but this is no longer the recommended approach. In Zend Framework 1.10, your bootstrapping code goes into the class Bootstrap in the application directory. We need to override the _initView() method in the otherwise empty Bootstrap class.
protected function _initView() { require_once 'Smarty_View.php'; $view = new Smarty_View($this->getOption('smarty')); $viewRender = Zend_Controller_Action_HelperBroker::getStaticHelper( 'ViewRenderer' ); $viewRender->setView($view); $viewRender->setViewSuffix('phtml'); Zend_Controller_Action_HelperBroker::addHelper($viewRender); return $view; }This method simply changes the class of object used to represent the application's view. Normally, it would be Zend_View, but we're saying use something called Smarty_View instead. The values that we've already put into the application.ini file are put into Smarty_View's constructor.
Smarty_View extends Zend_View_Abstract; there are many versions of this on the web (including in the official Zend Framework documentation) but this version is based on Andrea Belvedere's version.
<?php class Smarty_View extends Zend_View_Abstract { private $_smarty; public function __construct($data) { parent::__construct($data); require_once $data['dir'] . "Smarty.class.php"; $this->_smarty = new Smarty(); $this->_smarty->template_dir = $data['template_dir']; $this->_smarty->compile_dir = $data['compile_dir']; $this->_smarty->config_dir = $data['config_dir']; $this->_smarty->cache_dir = $data['cache_dir']; $this->_smarty->caching = $data['caching']; $this->_smarty->compile_check = $data['compile_check']; } public function getEngine() { return $this->_smarty; } public function __set($key, $val) { $this->_smarty->assign($key, $val); } public function __get($key) { return $this->_smarty->get_template_vars($key); } public function __isset($key) { return $this->_smarty->get_template_vars($key) != null; } public function __unset($key) { $this->_smarty->clear_assign($key); } public function assign($spec, $value=null) { if (is_array($spec)) { $this->_smarty->assign($spec); return; } $this->_smarty->assign($spec, $value); } public function clearVars() { $this->_smarty->clear_all_assign(); } public function render($name) { return $this->_smarty->fetch(strtolower($name)); } public function _run() { } }This class simply maps the Zend_View_Abstract methods to the Smarty equivalents and sets up up the Smarty engine. We can test this set up with the following code. Create an action with the following code
$this->view->entries = array('moo', 'dave', 'fred', 'andy', 'jo'); $this->view->moo = 'Moo';and a view template with the code
{$moo|strtoupper} says: <ol> {foreach from=$entries item=entry} <li class="{cycle values="odd,even"}">{$entry} - {$entry|strlen}</li> {/foreach} </ol>
Zend_Layout
The previous set up is all that is needed to start using Smarty with Zend_View.Unfortunately, if you're also using Zend_Layout, this will now be broken. If you're not using Zend_Layout, 'zf enable layout' is the Zend Tool command to switch this feature on.
There are two problems we need to overcome. Firstly, Smarty can't find the layout.phtml file. This is because we've told Smarty to look for template files in 'application/views/scripts/' but the layout template is in 'application/layouts/scripts/'. This is actually very easy to correct. A little documented feature of Smarty is that you can give it an array of directories to search, and it will search each in turn until it finds the correct file.
Add the following configuration to your application.ini file.
smarty.layout_dir = APPLICATION_PATH "/layouts/scripts"Then change the following line in Smarty_View from
$this->_smarty->template_dir = $data['template_dir'];to
$this->_smarty->template_dir = array($data['template_dir'], $data['layout_dir']);The second issue to resolve is a small conflict between the way Zend_View and Smarty are implemented; They both expect to execute the view template inside their own scope. In vanilla layout template, the keyword $this would point to the Zend_View object, but now it must point to the Smarty engine. This means that we can't use the normal syntax to access the Zend_Layout object in the layout template.
The fix for this is, again, quite simple. We simply need to add the Zend_View and Zend_Layout objects to the Smarty object as template variables with the following two lines in the Smarty_View constructor.
$this->assign('_view', $this); $this->assign('_layout', $this->layout());Now, in the layout template, where we would normally use
<?php $this->layout()->content ?>we instead use
{$_layout->content}and where we might previously use
<?php $this->headLink() ?>we use instead
{$_view->headLink()}
That's all there is to it. You now can use the Smarty template engine while still being able to use Zend_View, Zend_Layout and helpers.
Lastly, notice the line 'smarty.caching = 0' in the config file. This switches off Smarty's static caching system which saves a copy of the static HTML output into the cache directory; this is fine for a development environment but you might want to consider turning it on in production.