Pragmatism in the real world

Extending Zend_View_Interface for use with Smarty

Zend Framwork v0.6 has introduced new MVC code and one of the changes is that Zend_View_Interface now exists to allow for easy implementation of other templating engines other than PHP based ones like Zend_View or Savant.

We use Smarty at work and when we migrate our content management framework to Zend Framework, we intend to continue using Smarty. Hence, I’ve created AkCom_View_Smarty to play with how we are going to use Smarty within the Zend Framework. I think that part of the release also has an implemention of Zend_View_Interface that uses Smarty, so it’s worth looking at that solution too!

My code is really simple:


/**
* Concrete class for handling Smarty view scripts.
*
* @category AkCom
* @package AkCom_View
* @copyright Copyright (c) 2006 Rob Allen (http://www.akrabat.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class AkCom_View_Smarty implements Zend_View_Interface
{
/**
* @var Smarty
*/
protected $_engine;

function __construct($params = null)
{
$this->_engine = new Smarty();

// force error_reporting and hope smarty can cope with E_STRICT!
$this->_engine->error_reporting = E_ALL|E_STRICT;

// I like {{ and }} for delimiters!
$this->_engine->left_delimiter = '{{';
$this->_engine->right_delimiter = '}}';

// Render variables XSS safe by default.
$this->_engine->default_modifiers = array('escape:"htmlall"');

// AkCom specific: include our default plugins
$this->_engine->plugins_dir[] = dirname(__FILE__) . '/plugins.';

// pass any param passed to the constructor to Smarty
if (is_array($params)) {
foreach ($params as $key=>$value) {
$this->_engine->$key = $value;
}
}
}

public function getEngine()
{
return $this->_engine;
}

public function setScriptPath($path)
{
$this->_engine->template_dir = $path;
}

public function __set($key, $val)
{
$this->_engine->assign($key, $val);
}

public function __get($key)
{
return $this->_engine->get_template_vars($key);
}

public function __isset($key)
{
$var = $this->_engine->get_template_vars($key);
return !is_null($var);
}

public function __unset($key)
{
$this->_engine->clear_assign($key);
}

public function assign($spec, $value = null)
{
$this->_engine->assign($spec, $value);
}

public function clearVars()
{
$this->_engine->clear_all_assign();
}

public function render($name)
{
return $this->_engine->fetch($name);
}

public function setPluginsPath($dir)
{
$this->_engine->plugins_dir = $dir;
}

public function addPluginsDir($dir)
{
$this->_engine->plugins_dir[] = $dir;
}

public function setCompilePath($dir)
{
$this->_engine->compile_dir = $dir;
}

public function setCachePath($dir)
{
$this->_engine->cache_dir = $dir;
}

public function setCacheLifetime($seconds)
{
$this->_engine->cache_lifetime = $seconds;
}
}

As you can see, most of the code is in the constructor so that I can set up the instance of Smarty in the way that I like and then I’ve implemented assign() so that it works the same as Zend_View_Abstract for consistency’s sake. I’ve also implemented a few helper functions that are Smarty specific to make setting the common properties that need setting easier.

One thing I like about this code is that it shows that it’s quite simple to create a concrete class from Zend_View_Interface and so using the template engine of your choice isn’t actually that hard!

16 thoughts on “Extending Zend_View_Interface for use with Smarty

  1. Rob,

    I like to initial the Smarty's pathes in the constructor so that I don't have to setup the pathes outside the class.

  2. Jason,

    Try something like:

    $params['plugins_dir'][] = 'view/plugins';
    $params['template_dir'] = 'tmp/templates_c';
    $params['cache_dir'] = 'tmp/cache';
    $view = new AkCom_View_Smarty($params);

    Regards,

    Rob…

  3. Hi Rob!

    In CVS version of Smarty the variables are saved in _tpl_vars instead of just _vars. But you don't even need to access directly – get_template_vars($name) returns the value if found and null otherwise, both as reference.

    You could also shorten your assign() method. What you do is the same what's done in Smarty::assign(). The only difference is it only checks for array and assumes it's a string, which is IMO better, because it could also be a class with a __toString() method. And even a resource can be type casted to a string, so it could be used as variable name.

  4. Thanks Nico!

    As you can tell, I use Smarty as a black box and should have looked up the source for the bits I didn't know.

    I've fixed it in SVN and have updated the code in the post.

    Regards,

    Rob…

  5. Great article. I'm a little confused about how to use it (new to the Framework). Have been using Smarty for a few years and it's really hard to go back to all those php tags in the Framework views.

    How have you set up your plugins and templates directories? How do you use this class within the MVC framework (controllers/models/views)?

    Any time for an example?

    Thanks.

  6. Hello,

    Rob, any idea if works fine for 0.9.x ?

    Also, i am thinking that approach for making a new view based sub class is not the best idea when is needed to mix both ZF default view and Smarty view… I am wondering if is not possible to mix both on same class…

  7. Hi Christian,

    No reason why not. Just instantiate either a Zend_View or an AkCom_View_Smarty in the controller and and assign to $view before you call initView().

    Regards,

    Rob…

  8. Rob,

    On the setCacheLifetime() function, you use $dir as the variable passing to Smarty, but you're defining it on the function call as $seconds.

    –Sam

  9. William,

    Shouldn't do as Zend_Controller_Action_Helper_ViewRenderer is written against Zend_View_Interface.

    You might need to check if Zend_View_Interface has changed since this was posted though!

    Regards,

    Rob…

  10. Can you please do a tutorial for integrating Smarty in to Zend Framework? I'm really confused of it. Everyone make it different, but non of the solution is working for me. Thanks

  11. A quick fix to use this this class with ZF1.5+ and Zend_Form.

    You have to call the Zend_View_Abstract constructor to set the paths to the helpers (like Zend_Form_Element_Text).

    Add to the top of the constructor
    parent::__construct(array()); ## add
    $this->_engine = new Smarty(); # as before

    Since you are clling the parent – you also have to extend the original

    require_once 'Zend/View.php';
    class AkCom_View_Smarty extends Zend_View implements Zend_View_Interface ….

    # It's also useful to change the AkCom_View_Smarty::__constructor default parameter to 'array()' (from: null), so it can be passed into the constructor unchanged
    parent::__construct($params); ## alternate

  12. Hi,

    I have a very bizarre problem with ZF and Smarty plugin…

    I've created helper (Smarty plugin), that just needs to output login form, or logout link based on whether user is signed in or not. Code looks something like this:
    if (!Zend_Auth::getInstance()->hasIdentity()) {
    //outputting login form
    }
    else {
    //outputting logout link
    }
    That works just fine on every controller except one. I can't even visit that controller page, because of Invalid controller specified (error) exception. I know, that exception is thrown because I haven't created Error controller, but I can't believe that that plugin is not working only at one controller!

    I found out that the problem is in Zend_Auth::getInstance()->hasIdentity() code line, because when I remove that line, everything works fine and template is outputted properly.

    Does anyone know what could cause the problem?

    Thanks.

  13. Hi Rob,

    I'm having a small problem about integration of Smarty in ZF…

    I'm developing an customized CMS, and it should be modular. For now on, there are to modules – default and admin. In those modules, there are similar (or same) names of controllers and their actions, i.e. IndexController and indexAction(). Problem is that Smarty returns the same compiled template for index actions in Index controller for both modules!

    I somehow solved that problem by creating directory for each module in template_c. But I'm wondering is there any better solution, maybe one that will tell Smarty to give names to those files in template_c in some other way.

    Thanks.

Comments are closed.