How To Make Smarty the Default View for Your Zend Action Controllers

There are many articles and enough documentation out there on how to include the Smarty PHP templating engine into your Zend Framework action controllers; however, there was no solution out there describing how to truly incorporate Smarty as the default view renderer, so I decided to figure it out and show you how.no


UPDATE: 2008-10-17 10:25: I found a better way to do all of this that uses the default view renderer to do the work for you. In fact the SmartyControllerAction class wasn’t even needed anymore.

Partial Implementations

One day, I realized that the phtml view files for my Zend Framework project looked like unmaintainable scary hack (with <?php … ?> tags and shorttags abound), so I wanted to refactor my code to make it a bit more maintainable and readable.

I stumbled upon an article in the Zend Devzone about Integrating Smarty with the Zend Framework. After reading the article and checking Smarty out, I decided that I wanted to use Smarty as my view renderer.

I found several articles “on the webz” on “integrating” the two; however, all of the “integrations” required you to specify the output template file name at the end of each controller action method as you can see below (it was more like an inclusion than an integration)…

I also found on the Zend Framework Documentation page for “50.3.2.2. Template Systems Using Zend_View_Interface” where they actually have an example implementation of a Zend_View_Smarty class which implemented the Zend_View_Interface (which is the closest I could find to my desired Zend + Smary incorporation).

However, at the bottom of the section, they talk about how to use this example Zend_View_Smary, but again I was disappointed to find out I would have to call render() at the end of every controller’s action method! See below…

Since no one described how to fully incorporate, I thought I would do it myself and let you know my results.

The Proper Marriage of Zend Framework and Smarty

Well, why couldn’t I just call render() at the end of each controller’s action?

Call me lazy; call me a stickler. However, I really liked how the Zend_Controller_Action’s default view renderer used the names of controllers and actions to automatically determine what .phtml file to display; I thought it was really nifty.

As you can see above, the default view renderer would assume there was a “views/scripts/” directory inside your application module which would contain directories of .phtml files, where the directory names were the same name as the controller, and the .phtml files under those directories would be the name of each respective controller’s actions.

I wanted to see that same sort of automatic determination of the view script when I used the Smarty templating engine as my view renderer.

This also included not making the extra call to render() at the end of each controller’s action methods.

It ended up being easier than I thought.

Zend_View_Smarty Class

I used the Zend_View_Smarty class from the Zend Documentation page, mentioned earlier, as the view.

It just required three lines of code to be changed for it to work. In the setScriptPath() method, the smarty template_dir is set; well, I also wanted it to set the cache_dir, config_dir and compile_dir properties to be in the same relative directory as the current $path passed in.

class Zend_View_Smarty implements Zend_View_Interface {
    ...

    /**
     * Set the path to the templates
     *
     * @param string $path The directory to set as the path.
     * @return void
     */
    public function setScriptPath($path) {
        if (is_readable($path)) {
            $this->_smarty->template_dir = $path;

            // Convert the smarty paths into view paths
            $this->_smarty->compile_dir = dirname( $path )
                    . '/' . $this->_smarty->compile_dir;
            $this->_smarty->cache_dir = dirname( $path )
                    . '/' . $this->_smarty->cache_dir;
            $this->_smarty->config_dir = dirname( $path )
                    . '/' . $this->_smarty->config_dir;
            return;
        }

        throw new Exception('Invalid path provided');
    }

    ...
}

Time to Use Them…

Now you want actually use the class.

1) Create config ini file…

; Config properties for Smarty templates
[Smarty]
debugging = false
force_compile = true
compile_check = true

;; Location to smarty plugins
plugins_dir = ../library/my_smarty_plugins

;; Caching
caching = false
cache_lifetime = -1

;; Dir Names to be converted to :moduleDir relative paths
cache_dir = cache
config_dir = config
template_dir = templates
compile_dir = templates_c

2) Edit your bootstrap file to contain the following…

$smartyCfg = new Zend_Config_Ini( $configFile, 'Smarty', true );

include_once( 'Smarty/Smarty.class.php');
// Path to zend_view_smary.php
require_once( 'app/view/Zend_View_Smarty.php' );
$view = new Zend_View_Smarty(null, $smartyCfg );

// Use a modified view helper to render view scripts using Smarty
Zend_Loader::loadClass('Zend_Controller_Action_Helper_ViewRenderer');
$helper = new Zend_Controller_Action_Helper_ViewRenderer($view);

// Set the base script path RELATIVE TO THIS FILE
$helper->setViewBasePathSpec(':moduleDir/views/'
                              . $smartyCfg->template_dir);
// Change view suffix from phtml to tpl
$helper->setViewSuffix('tpl');

// Replace the default View Renderer with the Smarty modified one
Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer');
Zend_Controller_Action_HelperBroker::addHelper($helper);

The important parts to take away from the above code are the following:

$helper->setViewBasePathSpec(':moduleDir/views/'
                              . $smartyCfg->template_dir);

If this line is incorrect, you most likely will be having some problems.

Any path spec makes use of the following variables that get replaced at dispatch time:

  • :moduleDir
    • Replaced with the modules dir + / + module name
  • :module
    • Replaced with dispatched module name
  • :controller
    • Replaced with dispatched controller name
  • :action
    • Replaced with dispatched action name
  • :suffix
    • Replaced with view

In the examples below, you will see that I map to “:moduleDir/views/” + “templates” for my base spec path.

$helper->setViewSuffix('tpl');

This line will switch the default view file suffix from .phtml to .tpl.

An Example…

The directory setup

The IndexController looks like most other simple controllers you have made…

class IndexController extends Zend_Controller_Action {
    public function indexAction(){
        $this->view->name = "Frank Sinatra";

        $this->view->fruits = array(
            "apples",
            "bananas",
            "papayas",
            "peaches"
        );
    }
}

The index action will look for an index.tpl file (via :action.:suffix) in the index/ directory (via :controller) of your views/ directory (via :moduleDir).

{include file="header.tpl"}

<div><b>This is the result of
          IndexController::indexAction()</b></div>

<br/>

<div>Hello {$name}!</div>

<br/>

<div>Here is {$name}'s favorite fruits:
<ul>
  {section name="favFruits" loop="$fruits"}
    <li>{$fruits[favFruits]}</li>
  {/section}
</ul>
</div>

{include file="footer.tpl"}

Combine it with the header.tpl

<html>

<head>
  <title>Test Zend_View_Smarty App</title>
</head>

<body>

Header Content
<hr/>

and the footer.tpl

<hr/>
Footer Content

</body>
</html>

The final output works just as normal.

Download The Example…

If you can think of any other features and or suggestions, let me know and I’ll add it into the view/controller-action classes. Thanks!

ZendSmarty_v4.zip

22 thoughts on “How To Make Smarty the Default View for Your Zend Action Controllers

  1. Hi engfer,

    the tutorial is good, but when I try to follow your tutorials, I found it not work. If I not set the config file compile_dir path to absolute path, It will show template_c is not a folder in error_log.

    And after set it to absolute path, the page still show the content created in layout view, but can’t show the smarty template content,

    Can you pack a zip file which include a workable projects for reference?

    Thanks,

    Pang

  2. @Chau Yun Pang – Ahhh yes I forgot to mention some things about that.

    My app is set up as such as you see in the example directory structure above; however, I also have at that same root level, where application/ resides, folders like public/, config/, log/, and library/.

    Under public/ is the index.php (bootstrap file) and .htaccess. The key for doing this was to point the web server’s document root to public/ to secure the other directories from being seen by the web server. Under there are css, images and javascript files as well. Because index.php is in that directory, ALL PATHS in the application need to be relative to that file.

    This is why you see “../application” references in the example above.

    I will edit the article and provide a v3 zip that is just a sample app.

    Thanks!

  3. Hi Hengfer,
    nice job ;)
    is there a way to use view helpers with smarty?
    for example if I want to write down a form
    <a href=”$this->url(array(‘controller’=>’index’,
    ‘action’=>’edit’, ‘id’=>$album->id))” …and so on
    thank you

  4. @Giuseppe. Yes you can, but not like you want to.

    The 1st way I thought of was NAASTY. In you action method you could do something like $this->view->urlHelper = $this->getHelper('url'); and in your tpl do:

    {php}
    print $this->get_template_vars('urlHelper')->url( 
        array( 
            "controller" => "index", 
            "action" => "edit" 
        )
    );
    {/php}

    But don’t do that; that’s ridiculous.

    You could do all sorts of stuff with the {php}...{/php} but it’s mostly ugly.

    Unfortunately, the better way is to make a smarty function plugin that calls the method for you. Somewhere in the function you could just make a call to:

    Zend_Controller_Front::getInstance()->getRouter()
            ->assemble( $params );

    I’ll think a bit more about it…

  5. I have tried your example, but when I what to generate options for a listbox {html_options options=$fruits} the script throw me a Fatal error: unrecognized tag ‘html_options’.

    Is this because of the implementation?

    Can you check and let me know?

  6. @Gogu. Sorry it took me so long. Yes there was a slight problem with the Zend_View_Smarty class. The culprit is an overwritten plugins_dir property

    The problem existed in the initialization of the Smarty object as shown in the following loop…

    foreach ($extraParams as $key => $value) {
        $this->_smarty->$key = $value;
    }

    The above code blindly sets any property; this includes the _smarty->plugins_dir, which is initialized as Array( [0] => 'plugins' ). The above code turns the array into a single string and the Smarty default plugins dir is lost and none of the plugins are loaded (hence the error).

    The code below is the fixed version of that loop. I just needed to push “stuff” onto arrays if they were originally arrays.

    foreach ($extraParams as $key => $value) {
        if ( is_array( $this->_smarty->$key ) ) {
            array_push( $this->_smarty->$key, $value );
        } else {
            $this->_smarty->$key = $value;
        }
    }

    I uploaded a new version. I hope that helps.

    **BTW: html_options won’t work with the “$fruit” variable because html_options is expecting the “options” parameter to be an associative array.

  7. I noticed that the “plugins_dir” in the config.ini file is wrong…

    The right is

    plugins_dir = ../library/app/smarty_plugins

    you might need to fix it if you wanna try the Smarty Plugin To Replace Zend Framework View Helper “Url”

  8. Your guide helps me a lot. I’ve found another reasonable implementation at anders.tyckr.com, however I prefer your straightforward method.
    The only thing I want to know more is how to use Zend_Layout with your setup. Have you tried this?
    Thanks.

  9. i have implemented your ideas , smarty is working fine. But the database configuration can not be done . Can you tell about how to configure database in your system.

  10. Hi, nice tutorial you got there. And I’ve currently implemented in on our project. I just want to ask something. I would like to use LAYOUTS on my TPL pages, is the Zend_Layout way possible? Thanks

  11. I haven’t messed aroung with zend layout. I’m sure that you can integrate it, but it would involve a custom smarty tag and attaching the zend layout object to smarty before the request processing is passed to the view script. Hope that helps!

  12. Hi, I know this article is a quite old, but is it possible to add to setViewBasePathSpec() an URL param?

    I would like something like:

    setViewBasePathSpec( ‘:moduleDir/views/’ . $smartyConfig -> template_dir . ‘section/:section’ )

    Where ‘section’ is the URL param, and ‘:section’ the param value.

    I tried and received this message:

    “A replacement identifier : was found inside the inflected target, perhaps a rule was not satisfied with a target source? Unsatisfied inflected target:”

  13. Hi Engfer,
    First of all let me congratulate to provide an excellent article to us. I have implemented this in my current project successfully which is on Zend with Doctrine. it is working fine for direct text, images and anything except FORMS.
    If we integrate form controls
    i.e $username = new Zend_Form_Element_Text(‘username’);
    $password = new Zend_Form_Element_Password(‘password’); etc
    I am getting the following error:
    Fatal error: Call to undefined method Zend_View_Smarty::formText() ..
    appreciate if you can suggest any solution for this.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>