Hello World

Practical programming... and stuff...

Smarty + Zend_View, take three

, ,

I've seen two articles describing how to integrate Smarty with Zend_View, Integrating Smarty with the Zend Framework at Zend Developer Zone and Zend Framework: Using Smarty as template engine at Dmytro Shteflyuk's blog. Both of these are very outdated by now.

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? smile

Common crossbrowser CSS issuesA problem with the Zend MVC pattern

Comments

Anonymous Sunday, September 23, 2007 2:14:42 PM

Ges writes: Its really working with new updated Zend. Great work here. Thanks

Anonymous Tuesday, October 2, 2007 9:08:16 PM

Paulus writes: 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.

Janizomg Tuesday, October 2, 2007 9:16:04 PM

You're right, but I have a fix! wink

Add this to SmartyView class, inside _run function after $templateDirs = $this->getScriptPaths();

$this->_smarty->compile_id = $templateDirs[0];


Anonymous Tuesday, October 2, 2007 9:28:31 PM

Paulus writes: 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 Friday, October 12, 2007 7:53:56 AM

Kiên Phạm writes: Thank you! I was able to integrate Smarty with ZF using your SmartyView. Now I don't have to use the registry anymore :)

Anonymous Thursday, November 29, 2007 11:00:14 PM

Rob writes: 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?

Janizomg Friday, November 30, 2007 4:43:47 AM

Nothing. That's exactly how this works. If it worked with Zend_View, it should work with this.

Anonymous Thursday, December 6, 2007 10:39:11 PM

Matt W writes: 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 Thursday, December 6, 2007 11:24:05 PM

Matt W writes: 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

Janizomg Sunday, December 9, 2007 3:03:35 PM

MattW, looks somewhat similar to my ViewRenderer layout plugin. Anyways, for the layout-like behavior, I'd suggest checking out Zend_Layout which is available in the incubator dir in the latest Zend Framework snapshots or in SVN.

Anonymous Sunday, December 16, 2007 5:25:07 PM

Matt W. writes: 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 Thursday, February 14, 2008 7:23:12 PM

Tim writes: 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 Thursday, February 14, 2008 7:51:08 PM

Tim writes: ugh.. the above fouls up when you try to do {include}'s as both skins will search in the root 'scripts' directory

Anonymous Monday, March 10, 2008 10:14:03 PM

BC writes: 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 Tuesday, September 30, 2008 3:16:06 PM

Anonymous writes: 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.

Anonymous Monday, January 25, 2010 8:19:28 PM

Анонімний writes: The piece of your work seems to be marvelous. Persons, which purchase the online term paper from purchase term paper service have to know about your fantastic article. With that knowledge it supposes to be more simple to reach a success.

Anonymous Friday, June 11, 2010 2:13:58 PM

marko writes: Hi! Great article! I'm using Zend 1.10. and I manage to use smarty variables in layout.tpl, but I do not know how to render result from controller. It used to be layout()->content ?> in layout.tpl and in controller I have: $userMapper = new Application_Model_UserMapper(); $allUsers = $userMapper->fetchAll(); if (count($allUsers)) { $this->view->users = $allUsers; } else { $this->view->users = null; } But I don't get a content? Can someone please help me?

Anonymous Tuesday, September 7, 2010 1:25:05 PM

Anonymous writes: Avec ZF1.9 + smarty Je n’arrive juste pas à faire appel à des fonctions, même simples dans la vue {$this->headStyle()}, par contre, j’arrive bien à afficher la valeur de mes variables telles que {$book|upper} ou à en créer de nouvelles directement dans ma vue. si qql voit pourquoi ?

How to use Quote function:

  1. Select some text
  2. Click on the Quote link

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

If you can't read the words, press the small reload icon.


Smilies