Integrating Smarty with the Zend Framework

April 4, 2006

Tutorials, Zend Framework

Inspired by this article I started to play around a bit to integrate the Smarty template engine into the Zend Framework. My ambition was to minimize the required code in the controller actions but stay close to the given Zend_View API. I also wanted to integrate the Smarty caching feature. Here is the code I came up with.

Class location

The class file is named by my company (Travello) and is placed in the Travello directory beneath the Zend Framework library include path. Its advantage is that the class will be auto loaded in my setup.

  /Zend
    /View
  /Travello
    /View
      Smarty.php

Class definition and constructor

Let’s start with the class definition and the constructor part. My class Travello_View_Smarty is extending the Zend_View_Abstract class. In the constructor the parent constructor from Zend_View_Abstract is called first. After that a Smarty object is instantiated, configured and stored in a private attribute.

Please note that I use a configuration object from the object store to get the configuration data for Smarty.

class Travello_View_Smarty extends Zend_View_Abstract
{
    private $_smarty = false;
    
    public function __construct($data = array())
    {
        parent::__construct($data);
        
        $config = Zend::registry('config');
        
        $this->_smarty = new Smarty();
        
        $this->_smarty->caching = $config->getSetting('smarty', 'caching');
        $this->_smarty->cache_lifetime = $config->getSetting('smarty', 'cache_lifetime');
        $this->_smarty->template_dir = $config->getSetting('smarty', 'template_dir');
        $this->_smarty->compile_dir = $config->getSetting('smarty', 'compile_dir');
        $this->_smarty->config_dir = $config->getSetting('smarty', 'config_dir');
        $this->_smarty->cache_dir = $config->getSetting('smarty', 'cache_dir');
    }

Implement _run() method

The method _run() is the only method that needs to be implemented in any subclass of Zend_View_Abstract. It is called automatically within the render() method. My implementation just uses the display() method from Smarty to generate and output the template.

    protected function _run($template)
    {
        $this->_smarty->display($template);
    }

Overwrite assign() method

The next part is an overwrite of the assign() method from Zend_View_Abstract, which works in a similar way. The big difference is that the values are assigned to the Smarty object and not to the $this->_vars variables array of Zend_View_Abstract.

    public function assign($var)
    {
        if (is_string($var))
        {
            $value = @func_get_arg(1);
            
            $this->_smarty->assign($var, $value);
        }
        elseif (is_array($var))
        {
            foreach ($var as $key => $value)
            {
                $this->_smarty->assign($key, $value);
            }
        }
        else
        {
            throw new Zend_View_Exception('assign() expects a string or array, got '.gettype($var));
        }
    }

Overwrite escape() method

The next part is an overwrite of the escape() method from Zend_View_Abstract. It works both for string and array values and also uses the escape() method from the Zend_View_Abstract. The advantage of this is that I don’t have to care about each value of an array to get properly escaped.

    public function escape($var)
    {
        if (is_string($var))
        {
            return parent::escape($var);
        }
        elseif (is_array($var))
        {
            foreach ($var as $key => $val)
            {
                $var[$key] = $this->escape($val);
            }

            return $var;
        }
        else
        {
            return $var;
        }
    }

Print the output

The next method output() is a wrapper on the render() method from Zend_View_Abstract. It just sets some headers before printing the output.

    public function output($name)
    {
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Cache-Control: no-cache");
        header("Pragma: no-cache");
        header("Cache-Control: post-check=0, pre-check=0", FALSE);

        print parent::render($name);
    }

Use Smarty caching

The last two methods were created to simply integrate the Smarty caching mechanism in the View class. With the first one you can check for cached template and with the second one you can set the caching on or of.

    public function isCached($template)
    {
        if ($this->_smarty->is_cached($template))
        {
            return true;
        }
        
        return false;
    }

    public function setCaching($caching)
    {
        $this->_smarty->caching = $caching;
    }
}

That was the complete code of my Travello_View_Smarty class.

How to use the class

The usage of this class is quite simple. In your bootstrap file you can initialize it like this. The $viewConfig is used to setup the Zend_View paths. After creation the object is registered in the object store for later usage.

$viewConfig = array();
$viewConfig['scriptPath'] = $config->getSetting('framework', 'view_dir');

$view = new Travello_View_Smarty($viewConfig);
Zend::register('view', $view);

Within your controller an action method could look like this.

    public function indexAction()
    {
        $temp_file = 'homepage.htm';
        
        $view = Zend::registry('view');
        
        if (false === $view->isCached($temp_file))
        {
            $vars = array();
            $vars['title'  ] = 'Page <title>';
            $vars['text'   ] = 'A text is a text & a text is a text.';
            $vars['numbers'][0] = 12.9;
            $vars['numbers'][1] = 29;
            $vars['numbers'][2] = 78;
            
            $vars = $view->escape($vars);
            
            $view->assign($vars);
        }
        
        $view->output($temp_file);
    }

Conclusion

That was basically it. I have a couple of ideas how to extend this solution, but it already does the work for me. Any comments are welcome.

Updated on April 18, 2006

Please have also a look at the follow-up article on Integrating Sarty and ez Components with the Zend Framework.

Digg This!

26 Responses to “Integrating Smarty with the Zend Framework”

  1. mszkodzinski Says:

    Integrating Smarty 3 with Zend Framework:
    http://technology.mediovski.pl/2009/11/20/smarty-3-zend-framework

  2. sagarmd Says:

    Hi,

    I got some error then When I change the
    Zend::register(‘smarty’, $smarty);
    to
    Zend_Registry::set(‘smarty’, $smarty);

    It is start to work. Thanks

    Mark
    http://www.outsourceitindia.com

  3. carsunlimitted Says:

    http://www.philtvguide.com

    Can anyone confirm if this site uses Zend or PHP. I want to create a similar site. Thanks

  4. carsunlimitted Says:

    Can anyone confirm if this site uses Zend or PHP. I want to create a similar site. Thanks

  5. artlover Says:

    Usefull Article !!
    I’ve translated this article into Chinese.

    [url]http://www.codebit.cn/pub/html/php_mysql/tutorial/integrating_smarty_with_the_zend_framework/[/url]

  6. bububgirl Says:

    Can you write instruction step by step how use it?

    I have tried run this, but something is wrong and I don’t know what. I have created files ant/SmartyView.php (with first code) and I wrote right after Zend_Controller_Action class yours Ant_Controller_Action (second code). I have changed controller and now it extends Ant_Controller_Action. But how use it now

    I have a little view in Zend and I don’t have idea how it should works… can you give some example code…

    <a href=http://review.divx-blog.net/>Review films</a>

  7. dubaut Says:

    It seems that Zend_View_Abstract had been changed and does not accept any arguments any more. For this reason you should set the template file as a not public attribute which is used in _run():

    private function _run()
    {
    $this->_smarty->display($this->_template);
    }

  8. _____anonymous_____ Says:

    //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);

    Best Regards,
    http://allmusichit.com

  9. codemann Says:

    I already did something like this for a project I am working on. My solution was to create a class that extended Zend_View_Abstract. That way, you can still use all the methods with the Zend View API. My basic implementation is as follows:

    <code>
    class My_Smarty_View extends Zend_View_Abstract
    {
    private $_smarty;

    public function __construct($config = array()) {
    parent::__construct($config);

    $this->_smarty = new Smarty();

    //i have a config object for my application
    $root = Zend::registry(‘config’)->get(‘root’, ‘project’);

    $this->_smarty->template_dir = $root.’application/views/’;
    $this->_smarty->compile_dir = $root.’tmp/templates_c/’;
    $this->_smarty->config_dir = $root.’tmp/configs/’;
    $this->_smarty->cache_dir = $root.’tmp/cache/’;

    }
    public function assign($spec) {
    $args = func_get_args();
    call_user_func(array(‘parent’, ‘assign’), $args);

    if (is_string($spec)) {
    $arg = func_get_arg(1);
    $this->_smarty->assign($spec, $arg);
    } elseif (is_array($spec)) {
    foreach ($spec as $k=>$v) {
    $this->_smarty->assign($k, $v);
    }
    }
    else {
    throw new Zend_View_Exception(‘assign() expects a string or array’);
    }
    }
    }
    <code>

    Regards,
    <a href="http://www.mp3smonster.net/">mp3</a&gt;

  10. unixvps Says:

    hello,

    Good article and start point. Smarty is a powerful template tools. Zend is a powerful MVC framework. Together this is a best solution.
    I used it in develop many sites with great success.

    Best Regards,
    http://www.unixvps.com

  11. borec86 Says:

    Hello.

    I am currently developing my own framework based on ZF and decided to use TemplateLite as template engine.

    In response to octavian82 question, I give you Phlame_View_Tpl integrating TemplateLite with Zend View.

    You can view the source here:
    http://dev.inetive.pl/phlame/Phlame_View_Tpl.phps

    And here is some example.

    The template: http://dev.inetive.pl/phlame/test_template.tpl

    The example code: http://dev.inetive.pl/phlame/tpl_test.phps

    And the result: http://dev.inetive.pl/phlame/tpl_test.php

    I would appreciate any comments and ideas.

  12. scubamatrix Says:

    The code does not appear to work in RC1 or RC2 versions of the framework

    Help please.

  13. myaticav Says:

    I’m getting this error when running the implementation.

    "Uncaught exception ‘Zend_Exception’ with message ‘File "Zend\View\Helper\run.php" was not found"

    Seems that the file run.php is not part of the Zend Framework.

    Any Idea ?

  14. crocodile2u Says:

    I have aquired your approach in my application.
    However, I have made just 4 methods in my class:
    three that you have mentioned (__construct, assign, _run) and also _script() – to avoid redundant Zend_Exception:
    /**
    * Overrides parent’s method.
    * @param $name string The base name of the script.
    * @return void
    */
    protected function _script($name)
    {
    return $name;
    }

    The main issue in your code (IMHO) is the following: you are extending the Zend_View_Abstract interface by adding several new methods that are not present in the abstract class. This cannot be considered a problem until one day you decide that Smarty is no good – and you should fall back to using Zend_View instead.
    Then you can extend the Zend_View and make Torello_View_Zend class with stub methods, and that will be ok – but I think you should mention this in your article.

  15. tlmarker Says:

    Ralf,
    I just wanted to drop a comment to tell you how much I appreciate this tutorial. I was in the process of starting a new project, and wanted to use a more stuctured approach. I was all ready using smarty without the Zend framework.
    I knew I wanted to use the framework in my new project, but also wanted to use smarty. Your tutorial was the deciding factor in my changing to the Zend Framework. After spending most of yesterday downloading, and installing Apache I was able to get it working. (After spending a few hours learning how th configure Apache. Oh well, the burden of running your own server.)
    Anyway, thanks again.
    Troy

  16. frille Says:

    Hi Marc,

    you could easily assign the View object to Smarty as well by doing something like this in the _run() method:

    protected function _run($template)
    {
    $this->_smarty->assign(‘view’, $this);
    $this->_smarty->display($template);
    }

    But I haven’t tested this yet, so I might be wrong.

    Best Regards,

    Ralf

  17. frille Says:

    Hi Hermann,

    thanks for your comments on internationalization. Sounds very good to me. For myself, I haven’t come to that part yet, but your approach sounds reasonable to me.

    Until now what I did was to assign an array called "translate" to Smarty and to access the translation via {$template.to_be_translate}.

    Best Regards,

    Ralf

  18. octavian82 Says:

    Nice work. I wonder if it is possible to make use of the Zend_View Helpers out of the templates? I didn’t manage to get it working.
    For example if you created a Zend_View_Helper_MakeUrl and want to invoke it through a template:

    <a href="{makeUrl(‘index’,'login’)}">Login</a>

    I don’t think it is possible by using your approach because the helper method are unknown to the smarty object.
    In my code is assigned a Zend_View Object that includes all output variables to the smarty object by doing:

    $view = new Zend_View();
    $view->template = ‘index.html’;
    //add other output variables in the actioncontroller
    $smarty = new Smarty();
    //smarty cfg stuff

    //at the end of the script
    $smarty->assign(‘view’, $view);
    $smarty->display($view->template);

    By doing this, smarty will automatically invoke the Zend_View::__call() method to unknown funktions, and that invokes the helper methods. the disadvantage is that you have to use:

    <a href="{$view->makeUrl(‘index’,'login’)}">Login</a>

    which is a little more code, but it works for me.

    One more Question: Did anyone manage to make TemplateLite (former SmartyLite) working with ZF?

    regards, Marc

  19. hzubieta Says:

    It sounds good!!; the integration with Smarty is saving-time idea. I had used in my own Application Framework for while, it had save me a lot of time, I used in conjunction with Dreamweaver and the software interface is complete independent from the application coding: Zend Studio for Coding and Dreamweaver to designing.

    If you redefined the Smarty Tags to "<?" and "?>" and uses Templates it allows you to see a really clean design in your design editor (there are other ways to do that, but this is quick!!). Templates also make it easy to develop huge interface changes in all your application on the fly without touching your code (the customers like that!!!) and taking advantage of the smarty cache management.

    Another thing that helped me was the idea of Tom Anderson of adding a "T" block for Internationalization support. With this you, can even make your design in English and tagged all translatable text with "T" blocks and after use a Smarty-T- block plugin to support your application in different languages. I used in Japanese and I developed my own t block plug in like this:

    function smarty_block_t($params, $content, &$this)
    {
    if (!$content)
    return $content;
    return i18ngettext(trim($content));
    }

    The i18ngettext is a function that search into the localization files and returns the content with the translated phrase if it exists. I rewrite the i18ngettext in order to cached the files.

    In design time text will look like:

    <?t?>Translate me<?/T?>

    Bye,

    Hermann

  20. frille Says:

    The HTML markup doesn’t seem to work in the comments. But hopefully you get the point.

  21. frille Says:

    Hi,

    currently I am using the ezcConfiguration and ezcConfigurationIniReader from the ezComponents
    http://ez.no/doc/components/view/latest/(file)/introduction_Configuration.html

    To integrate the ezComponents within the Zend Framework I put the ezComponents directory in my include_path and use a slightly different __autoload() function:

    <pre>
    include_once ‘Zend.php’;
    include_once ‘Base/src/base.php’;

    function __autoload($class)
    {
    if (‘ezc’ == substr($class, 0, 3))
    {
    ezcBase::autoload($class);
    }
    else
    {
    Zend::loadClass($class);
    }
    }
    </pre>

    To use the Configuration component I set it up and store the configuration object in the object store of the Zend Framework.

    <pre>
    $reader = new ezcConfigurationIniReader();
    $reader->init(‘path/to/your/ini/files’, ‘settings.ini’);
    $config = $reader->load();
    Zend::register(‘config’, $config);
    </pre>

    That is basically it.

    HTH, Ralf

  22. jmkeith Says:

    Are you using some sort of Config class to handle the getSetting() method, etc?

  23. devzone_editor Says:

    PHPDeveloper.org just pointed out a related article on caching using Smarty. This will fit nicely with what Ralf has written here.