Implementing Zend Layout and Smarty using Zend Framework MVC

Lately I have received some requests on how Zend Layout can be used while not using the default templating system in Zend Framework (ZF), but instead using Smarty as a templating engine. So here is a write up on how to do so. The way to implement this may vary, so this is just one way of doing it. :)

This “tutorial” requires that you have at least some sense on how ZF works and a little bit on MVC in general.

1) Get Zend Studio for Eclipse as this will help A LOT on structuring your applications in a good way.

2) Document folder structure:

Having a good document folder structure will also help you build clean and maintainable applications. I try to mimic the ZF coding standard and naming conventions as much as possible. Here’s my setup:

shared/
	lib/
 		Zend/
 		Smarty/
	mySiteName/
	 application/
	 	default/
	 		controllers/
	 		models/
	 		views/
	 			filters/
	 			helpers/
	 			layouts/
	 				default.tpl
	 				(layoutname).tpl
	 			scripts/
	 				(controller)/
	 					(action).tpl
	 config/
	 	config.xml
	 data/
	 	cache/
	 lib/
	 	EZ/
	 		View/
	 			Smarty/
	 				Plugin/
	 					Abstract.php
	 					Broker.php
	 					Exception.php
	 					Standard.php
	 			Smarty.php
	 public_html/
	 	css/
	 	js/
	 	pics/
	 	.htaccess
	 	index.php
	 templates_c/

3) The config file

< ?xml version="1.0"?>

        www.somesite.com

            mysqli

	my_session

http://www.somesite.com

	http://

		tpl

		content
		Special
		tpl

	false

		3600
		../data/cache/

	true

4) The bootstrap index.php

In order for things to work with Smarty and Zend Layout, we need to set som extra variables and inflector scripts so that view scripts will be resolved and parsed appropriately.

/**
 * ezApplicationBase - A Zend Framework implementation with Smarty and Zend Layout
 *
 * Bootstrap file for mysite.com
 *
 * @author Anders Fredriksson (http://anders.tyckr.com)
 * @version 0.1
 */
//three folders "above" index.php
define('ROOT_DIR', dirname(dirname(dirname(__FILE__))));
define('APP_DIR',dirname(dirname(__FILE__)));

set_include_path('.' . PATH_SEPARATOR . APP_DIR . '/lib/' . PATH_SEPARATOR . APP_DIR . '/application/default/models/' . PATH_SEPARATOR . ROOT_DIR . '/shared/lib/' . PATH_SEPARATOR . get_include_path());
//This requires that your Zend library lies in ROOT_DIR/shared/lib/

//make classes autoload without doing require
require_once('Zend/Loader.php');
Zend_Loader::registerAutoload();

if(defined('ENV') !== TRUE) {
	define('ENV','production');	//change staging to production to go to production settings
}

$config = new Zend_Config_Xml(APP_DIR . '/config/config.xml', ENV);
Zend_Registry::set('config',$config);

//init session
$session = new Zend_Session_Namespace($config->session_name);
Zend_Registry::set('session',$session);

Zend_Db_Table::setDefaultAdapter(Zend_Db::factory(Zend_Registry::get('config')->database));

/**
 * Init the Smarty view wrapper and set smarty suffix to the view scripts.
 */
$view = new EZ_View_Smarty($config->smarty->toArray());

//use the viewrenderer to keep the code DRY
//instantiate and add the helper in one go
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setView($view);
$viewRenderer->setViewSuffix($config->smarty->suffix);

/**
 * Set inflector for Zend_Layout
 */
$inflector = new Zend_Filter_Inflector(':script.:suffix');
$inflector->addRules(array(':script' => array('Word_CamelCaseToDash', 'StringToLower'),
			 								  'suffix'  => $config->layout->suffix));

// Initialise Zend_Layout's MVC helpers
Zend_Layout::startMvc(array('layoutPath' => ROOT_DIR.$config->layout->layoutPath,
							'view' => $view,
							'contentKey' => $config->layout->contentKey,
							'inflector' => $inflector));

$front = Zend_Controller_Front::getInstance();

$front->setControllerDirectory(array(
    'default' => '../application/default/controllers',
    'blog'    => '../application/blog/controllers',
));

$front->throwExceptions(true);

//enable logging to default.log
$writer = new Zend_Log_Writer_Stream(APP_DIR.'/data/log/default.log');
$logger = new Zend_Log($writer);

//give easy access to the logger
Zend_Registry::set('logger',$logger);

try {
    $front->dispatch();
} catch(Exception $e) {
    echo nl2br($e->__toString());
}

5) The Smarty implementation of Zend_View. For this I want to give Naneau credit as it is mostly the same as the implementation he did a while back.

EZ/View/Smarty.php

/**
 *
 * @since Dec 7 2007
 * @author Anders Fredriksson
 */
/**
 * Zend View Base Class
 */
require_once 'Zend/View.php';

/**
 * Smarty templating engine
 */
require_once 'Smarty/Smarty.class.php';

/**
 * @category
 */
class EZ_View_Smarty extends Zend_View_Abstract {
	/**
	 * Smarty object
	 * @var Smarty
	 */
	protected $_smarty;

	protected $_plugins;

	/**
	 * Constructor
	 *
	 * Pass it a an array with the following configuration options:
	 *
	 * scriptPath: the directory where your templates reside
	 * compileDir: the directory where you want your compiled templates (must be
	 * writable by the webserver)
	 * configDir: the directory where your configuration files reside
	 *
	 * both scriptPath and compileDir are mandatory options, as Smarty needs
	 * them. You can't set a cacheDir, if you want caching use Zend_Cache
	 * instead, adding caching to the view explicitly would alter behaviour
	 * from Zend_View.
	 *
	 * @see Zend_View::__construct
	 * @param array $config ["scriptPath" => /path/to/templates,
	 *			     "compileDir" => /path/to/compileDir,
	 *			     "configDir"  => /path/to/configDir ]
	 * @throws Exception
	 */
	public function __construct($config = array()) {
		$this->_smarty = new Smarty ( );
		//smarty object

		if (! array_key_exists ( 'compileDir', $config )) {
			throw new Exception ( 'compileDir must be set in $config for ' . get_class ( $this ) );
		} else {
			$this->_smarty->compile_dir = $config ['compileDir'];
		}
		//compile dir must be set
		$this->_smarty->debugging = true;
		if (array_key_exists ( 'configDir', $config )) {
			$this->_smarty->config_dir = $config ['configDir'];
		}
		//configuration files directory

		parent::__construct ( $config );
		//call parent constructor

		$this->_plugins = new EZ_View_Smarty_Plugin_Broker($this);
		$this->registerPlugin(new EZ_View_Smarty_Plugin_Standard());

	}

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

	/**
	 * Return the template engine object
	 *
	 * @return Smarty
	 */
	public function getEngine() {
		return $this->_smarty;
	}

	/**
	 * register a new plugin
	 *
	 * @param EZ_View_Smarty_Plugin_Abstract
	 */
	public function registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null) {
		$this->_plugins->registerPlugin ( $plugin, $stackIndex );
		return $this;
	}

	/**
	 * Unregister a plugin
	 *
	 * @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
	 */
	public function unRegisterPlugin($plugin) {
		$this->_plugins->registerPlugin ( $plugin );
		return $this;
	}

	/**
	 * fetch a template, echos the result,
	 *
	 * @see Zend_View_Abstract::render()
	 * @param string $name the template
	 * @return void
	 */
	protected function _run() {
		$this->strictVars ( true );

		$vars = get_object_vars ( $this );
		foreach ( $vars as $key => $value ) {
			if ('_' != substr ( $key, 0, 1 )) {
				$this->_smarty->assign ( $key, $value );
			}
		}
		//assign variables to the template engine

		$this->_smarty->assign_by_ref ( 'this', $this );
		//why 'this'?
		//to emulate standard zend view functionality
		//doesn't mess up smarty in any way

		$path = $this->getScriptPaths ();

		$file = substr ( func_get_arg ( 0 ), strlen ( $path [0] ) );
		//smarty needs a template_dir, and can only use templates,
		//found in that directory, so we have to strip it from the filename

		$this->_smarty->template_dir = $path [0];
		//set the template diretory as the first directory from the path

		echo $this->_smarty->fetch ( $file );
		//process the template (and filter the output)
	}
}

My additions are basically the possibility to add plugins to Smarty without putting them in the Smarty lib directory. This is needed so that a nicer implementation of ZF’s view helpers can be used.

6) Smarty plugin loader

EZ/View/Smarty/Plugin/Standard.php

class EZ_View_Smarty_Plugin_Standard extends EZ_View_Smarty_Plugin_Abstract {

	public static function layoutFunction($params, &$smarty) {
		$section = $params ['section'];
		$toReturn = "";
		try {
			//oppress errors
			@$toReturn = $smarty->_tpl_vars ['this']->placeholder ( 'Zend_Layout' )->$section;
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}

	public static function headTitleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			//oppress errors
			if ($method != - 1) {
				@$toReturn = $smarty->_tpl_vars ['this']->headTitle ()->$method ( $args );
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headTitle ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}

	public static function headScriptFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->headScript ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->headScript ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headScript ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}

	public static function jsLocaleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->jsLocale ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->jsLocale ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->jsLocale ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}

	public static function headStyleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headStyle()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->headStyle ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->headStyle ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headStyle ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}

	public static function parseArgs($args) {
		if (preg_match_all ( "/([^\[^\]]+)/", $args, $matches )) {
			$params = explode ( ',', $matches [1] [0] );
			$toReturn = array ( );
			foreach ( $params as $p ) {
				@list ( $key, $value ) = explode ( '=>', $p );
				$toReturn [preg_replace ( "/[\' ]+/", "", $key )] = preg_replace ( "/[\' ]+/", "", $value );
			}
			return $toReturn;
		}
		return $args;
	}

	/**
	 * Smarty block function, provides gettext support for smarty.
	 *
	 * The block content is the text that should be translated.
	 *
	 * Any parameter that is sent to the function will be represented as %n in the translation text,
	 * where n is 1 for the first parameter. The following parameters are reserved:
	 *   - escape - sets escape mode:
	 *       - 'html' for HTML escaping, this is the default.
	 *       - 'js' for javascript escaping.
	 *       - 'url' for url escaping.
	 *       - 'no'/'off'/0 - turns off escaping
	 *   - plural - The plural version of the text (2nd parameter of ngettext())
	 *   - count - The item count for plural mode (3rd parameter of ngettext())
	 */
	public static function tBlock($params, $text, &$smarty) {
		$text = stripslashes ( $text );

		// set escape mode
		if (isset ( $params ['escape'] )) {
			$escape = $params ['escape'];
			unset ( $params ['escape'] );
		}

		// set plural version
		if (isset ( $params ['plural'] )) {
			$plural = $params ['plural'];
			unset ( $params ['plural'] );

			// set count
			if (isset ( $params ['count'] )) {
				$count = $params ['count'];
				unset ( $params ['count'] );
			}
		}

		// use plural if required parameters are set
		if (isset ( $count ) && isset ( $plural )) {
			$text = EZ_Language::getInstance ()->translatePlural ( $text, $plural, $count );
		} else { // use normal
			$text = EZ_Language::getInstance ()->translate ( $text );
		}

		// run strarg if there are parameters
		if (count ( $params )) {
			$text = self::strarg ( $text, $params );
		}

		if (! isset ( $escape ) || $escape == 'html') { // html escape, default
			$text = nl2br ( htmlspecialchars ( $text ) );
		} elseif (isset ( $escape )) {
			switch ( $escape) {
				case 'javascript' :
				case 'js' :
					// javascript escape
					$text = str_replace ( ''', '\'', stripslashes ( $text ) );
				break;
				case 'url' :
					// url escape
					$text = urlencode ( $text );
				break;
			}
		}

		return $text;
	}

/**
 * Replaces arguments in a string with their values.
 * Arguments are represented by % followed by their number.
 *
 * @param	string	Source string
 * @param	mixed	Arguments, can be passed in an array or through single variables.
 * @returns	string	Modified string
 */
	public static function strarg($str) {
	$tr = array();
	$p = 0;

	for ($i=1; $i < func_num_args(); $i++) {
		$arg = func_get_arg($i);

		if (is_array($arg)) {
			foreach ($arg as $aarg) {
				$tr['%'.++$p] = $aarg;
			}
		} else {
			$tr['%'.++$p] = $arg;
		}
	}

	return strtr($str, $tr);
  }
}

This file is basically to allow for view helper function calls that are needed to get Zend Layout to work properly. The Smarty compiler has a limitation of just allowing one level of function calls {$this->someHelper()} works, but {$this->someHelper()->someHelperFunction()} does not work. Zend Layout requires you to do $this->placeholder("Zend_Layout")->section to output the data inside the layoutsection “section”. With my helper the syntax for that would be {layout section='section'}

The other helpers in there are some of the standard helpers that comes with Zend View. I kept them in there to give you an idea of what can be done.

The main purpose for using Zend Layout with Smarty is that you can give your site a clean look without having to duplicate code.

7) The default template for the layout. I use YUI Grids CSS to get a good clean standard css layout.

{$this->doctype()}
	{headTitle}
	{headScript}
	{$this->headLink()}{headStyle}
	{$this->headMeta()}
	{*debugging turned on as a default during development *}
	{if $debugging}
		{debug}
	{/if}
{$this->placeholder(“header”)}
{layout section=”content”}
{$this->placeholder(“leftColumn”)}
{$this->placeholder(“footer”)}

8) Get some action to render into a section, eg. “leftColumn”.

For this to work with a Zend Layout section you need to set the responseSegmentName of that action, which is done with:

$this->_helper->viewRenderer->setResponseSegment('section');

Doing this will probably put you in a rough spot when you try to make calls to specific actions from within other actions or preDispatch. To avoid this there is a simpler method that I just switched to.

To render action “testAction()” from OtherController into a “section” leftColumn for instance, and to do that from another action call “indexAction()” we do the following:

public function indexAction() {
    $this->view->placeholder('leftColumn')->set($this->view->action('test','Other'));
    $this->view->someVar = "this will get rendered into the index.tpl";
}

And that pretty much wraps it up for now.

Please feel free to ask me any questions.

Update: I guess it will help more if I post the code for the Smarty plugin helpers :)

EZ/View/Smarty/Plugin/Abstract.php

abstract class EZ_View_Smarty_Plugin_Abstract {

	protected $_functionRegistry;
	protected $_namingPattern ='/([a-zA-Z1-9]+)(Function|Block)$/';

	/**
	 *
	 */
	function __construct() {

	}

	/**
	 * @return array
	 */
	public function getClassFunctionArray() {
		$type = get_class($this);
		$methods = get_class_methods($this);
		foreach($methods as $value) {
			if(preg_match($this->_namingPattern,$value,$matches)) {
				$this->_functionRegistry[$matches[1]] = $type."::".$value;
			}
		}
		return $this->_functionRegistry;
	}

	/**
	 * change the default naming pattern for functions that should be mapped to smarty functions
	 */
	public function setNamingPattern($pattern ='/([a-zA-Z1-9]+)(Function|Block)$/') {
		 //"/([a-zA-Z1-9]+)Function$/";
		 $this->_namingPattern = $pattern;
	}
}

EZ/View/Smarty/Plugin/Broker.php:

/**
 * EZ_View_Smarty_Plugin_Broker
 *
 * This class registers smarty plugins with the current smarty view
 */
class EZ_View_Smarty_Plugin_Broker {

	/**
	 * Array of instance of objects extending EZ_View_Smarty_Plugin_Abstract
	 *
	 * @var array
	 */
	protected $_plugins = array ( );
	protected $_view;

	/**
	 *
	 * @param EZ_View_Smarty $view
	 */
	public function __construct($view) {
		$this->_view = $view;
	}

	/**
	 * Register a plugin.
	 *
	 * @param  EZ_View_Smarty_Plugin_Abstract $plugin
	 * @param  int $stackIndex
	 * @return EZ_View_Smarty_Plugin_Broker
	 */
	public function registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null) {
		if (false !== array_search ( $plugin, $this->_plugins, true )) {
			throw new EZ_View_Smarty_Exception ( 'Plugin already registered' );
		}

		$stackIndex = ( int ) $stackIndex;

		if ($stackIndex) {
			if (isset ( $this->_plugins [$stackIndex] )) {
				throw new EZ_View_Smarty_Exception ( 'Plugin with stackIndex "' . $stackIndex . '" already registered' );
			}
			$this->_plugins [$stackIndex] = $plugin;
		} else {
			$stackIndex = count ( $this->_plugins );
			while ( isset ( $this->_plugins [$stackIndex] ) ) {
				++ $stackIndex;
			}
			$this->_plugins [$stackIndex] = $plugin;
		}

		ksort ( $this->_plugins );
		$this->reLoadPlugins();
		return $this;
	}

	/**
	 * Unregister a plugin.
	 *
	 * @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
	 * @return EZ_View_Smarty_Plugin_Broker
	 */
	public function unregisterPlugin($plugin) {
		if ($plugin instanceof EZ_View_Smarty_Plugin_Abstract) {
			// Given a plugin object, find it in the array
			$key = array_search ( $plugin, $this->_plugins, true );
			if (false === $key) {
				throw new EZ_View_Smarty_Exception ( 'Plugin never registered.' );
			}
			unset ( $this->_plugins [$key] );
		} elseif (is_string ( $plugin )) {
			// Given a plugin class, find all plugins of that class and unset them
			foreach ( $this->_plugins as $key => $_plugin ) {
				$type = get_class ( $_plugin );
				if ($plugin == $type) {
					unset ( $this->_plugins [$key] );
				}
			}
		}
		$this->reLoadPlugins();
		return $this;
	}

	/**
	 * Is a plugin of a particular class registered?
	 *
	 * @param  string $class
	 * @return bool
	 */
	public function hasPlugin($class) {
		$found = array ( );
		foreach ( $this->_plugins as $plugin ) {
			$type = get_class ( $plugin );
			if ($class == $type) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Retrieve a plugin or plugins by class
	 *
	 * @param  string $class Class name of plugin(s) desired
	 * @return false|EZ_View_Smarty_Plugin_Abstract|array Returns false if none found, plugin if only one found, and array of plugins if multiple plugins of same class found
	 */
	public function getPlugin($class) {
		$found = array ( );
		foreach ( $this->_plugins as $plugin ) {
			$type = get_class ( $plugin );
			if ($class == $type) {
				$found [] = $plugin;
			}
		}

		switch ( count ( $found )) {
			case 0 :
				return false;
			case 1 :
				return $found [0];
			default :
				return $found;
		}
	}

	/**
	 * Retrieve all plugins
	 *
	 * @return array
	 */
	public function getPlugins() {
		return $this->_plugins;
	}

	/**
	 * Load all plugins
	 */
	public function loadPlugins() {
		foreach($this->_plugins as $plugin) {
			$type = get_class($plugin);
			//get an array of all the classfunctions that should be mapped to smarty functions
			$functions = $plugin->getClassFunctionArray($type);
			foreach($functions as $key => $value) {
				if(preg_match('/Block$/',$value)) {
					$this->_view->getEngine()->register_block($key,$value);
				} else {
					$this->_view->getEngine()->register_function($key,$value);
				}
			}

		}
	}

	public function unLoadPlugins() {
		foreach($this->_plugins as $plugin) {
			$type = get_class($plugin);
			//get an array of all the classfunctions that should be mapped to smarty functions
			$functions = $plugin->getClassFunctionArray($type);
			foreach($functions as $key => $value) {
				if(preg_match('/Block$/',$value)) {
					$this->_view->getEngine()->unregister_block($key);
				} else {
					$this->_view->getEngine()->unregister_function($key);
				}
			}

		}
	}

	public function reLoadPlugins() {
		$this->unLoadPlugins();
		$this->loadPlugins();
	}
}

Now, that should do it.

74 comments

  1. Jonathan Kushner says:

    Very nice. However, I can’t complete it until you let me know what the Smarty Plugin Base classes are… I am under the impression they just extend various ZF Classes, but who wants to break this great tutorial with guesses!

    Hurry up! :)

  2. Jonathan Kushner says:

    Nevermind. FIgured it out. I will post a comment when it’s implemented. :)

  3. It’s coming alright. Im trying to connect the controller’s output and that seems to be giving me issues. Also, the implementation you have with using {layout} or {doctype} seems to be malfunc.

    Besides that, it seems to be a implementation. Any ideas?

  4. anders says:

    Are you using all of the code above? If you are the {layout section=content} should be working fine. Those are loaded in the EZ_View_Smarty_Plugin_Standard. I haven’t implemented all the view helpers as smarty functions, and doctype is one of the ones I haven’t done. No reason really, but I just haven’t had use of it from the view yet, as I set the doctype form the controllers. If these aren’t working, something is probably wrong with your setup.

    //Anders

  5. Great article. I have one question. I want to use AjaxContext that comes with the new ZF1.5 and I also use your Smarty integration.

    When I force json context, it works as expected. When I try to use html context (to render action.ajax.phtml) things don’t work though: the default template is rendered and the suffix seems not to be used. Any suggestions?

  6. anders says:

    Hi Thijs,

    I haven’t worked with AjaxContext that comes with ZF, but it should be fairly straight forward.

    I use my own contextswitch that extends the ZF contextswitch, and there I init the context and set html as default.

    I have a vague memory of having similar problems when trying out the ContextSwitch, and I think the problem was some sort of “buggish” thing in there. :)

    Have you tried to do an XMLHttpRequest just to try it out? Because that is basically the only way you can test ajaxContext in a good way. If you want to render it in html context it should render the normal template and not the action.ajax.phtml.

  7. Hi anders,

    bit late reply because of timedifference; I am indeed testing with XMLHttpRequest (with the right headers). I can see the AjaxContext is created, and initialised in my logfile. Inside Zend_Controller_Action_Helper_ViewRenderer::_translateSpec() I get not the updated suffix (‘edit.ajax.phtml’) but just ‘edit.phtml’. I’m going to dig deeper; see if there is a bug or if I dont understand the usage correctly.

  8. I found the problem, it’s a side-effect of your code; it might be called a bug ;)

    In the bootstrap index.php you do:
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();

    This leads to problems, because you are making a *new* ViewRenderer object. This new one will be used instead of the previously created one. The first one was already created when I created a new AjaxContext object in the init() of my Controller. AjaxContext construction eventually tries to load some Helpers.

    I noticed this when I saw two calls to the constructor of Zend_Controller_Action_Helper_ViewRenderer when tracing to the program execution with XDebug. The problem is that the correct suffic from the AjaxContext is set in the *first* ViewRenderer instance, and when you create the 2nd one, it does not have the correct suffix.

    The solution, do not explicitly create a new ViewRenderer:
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');

    This will first try to load the existing ViewRenderer. Besides solving this bug; it’s also a little bit more efficient I guess :) Hope this is also useful for you!

  9. anders says:

    Thanks Thijs!

    Good call. I havn’t looked much at my bootstrap since I first created it, but indeed you are right that it should not be created twice for efficiency at least. For some reason this is not a problem for me. But I’ll change my code to use the getStaticHelper method instead.

  10. I have another question; is it possible to do nested layouts somehow?

    I am trying to make a widget page. My layout includes the main data, just like you do:
    {layout section="content"}

    But if I try to use
    $this->view->placeholder('leftColumn')->set($this->view->action('test','Other'));

    things break down. $this->view->action() break everything it seems; I dont’t know why. It sounds like exactly what I would need though.

    Instead I tried to use the actionStack helper to render actions and then the action decides the named segment. This works fine, but the resulting named segments are only available inside the layout file; not inside the content view. If I understand correctly, each view is first rendered and then finally assembled into the overall view. So using the actionStack, the other action would never be available in time for the render().

    I would like this:

    But with the actionStack, I can only get them appended after the content?

  11. anders says:

    This is a fairly newly fixed bug. If you update to the the 1.5 version this should be fixed. The problem was that the viewRenderer was disabled when using the action() helper. You can easily test if this is in fact the issue you have by calling render(‘myTemplate’) in the action you use to call the action() helper.

    I tried to work with the actionstack at first too, but after a while I gave that up because of all the problems that come with going through the dispatchloop another lap again.

    I ended up using a LayoutController where I have some of my layoutactions which I do call with the action() helper.

  12. Great advice Anders! I was using the final version of ZF 1.5, but that one still had the bugs in it. On your advice I downloaded and installed the latest nightly release and now everything works as exected. I hope Zend quickly releases 1.5.1, because this is a serious bug for people trying to use the Zend_View_Helper_Action from within an Action. Thanks again :)

  13. Cobby says:

    Hi,

    I have used the fundamental parts of your implementation (just the base class), and it seamed to be working great for a very short period, then found a problem.

    I am using the modular directory structure, as defined Here.

    I have extended that Zend_Controller_Action to create my own. I have it set up so that by default, the ‘content’ placeholder in my layout will be a render of the relevant template. i.e http://project/user/login would render ‘login/index.phtml’, and user (which is the module) is already predefined in the call to getScriptPaths() function, and this is rendered in the postDispatch method.

    I also have in my custom controller a method called setContent, which will override the default rendering done in postDispatch.

    Here is my problem:
    consider the following requests-
    http://project/user
    http://project/blog

    As I said earlier, the getScriptPaths with already include the module as part of the directory, so this will be unique. But the controller and action are identical (since they aren’t specified, its defaults to ‘index’ for both controller and action).

    Which results in render(‘index/index.phtml’), this works fine. But the real problem exists within smarty, as the compiled template file name is solely derived from what is being rendered (in this case, its index/index.phtml).

    So despite have two separate index.phtml files, smarty doesn’t recognise this as the template directory is changing. If you go to either of the above URLs, it will render the same template (which will vary depending on which address you go to first).

    I hope I have explained the issue well enough.

    Do you know how to fix this, I will further investigate this myself and post a solution if I find one.

    Thanks.

  14. anders says:

    Ah,

    I see your problem.

    The problem is that the compiled templates catalog uses the same name for the files or at least thinks it is the same.

    I’m not exactly sure how to solve this, but I think you might be able to use a smarty config variable called compile_id, which you could set to the name of the module. I haven’t tried it myself, but that’s what I would look at. more at:
    http://phpdoctor.sourceforge.net/examples/smarty/smarty/smarty.html

  15. Cobby says:

    Thanks for that, works a treat. I was just about to create a separate global template folder, referenced by module. I thought smarty might have a quick fix, but the smarty site was really slow last, but its better now.

    Again, thank-you :)

  16. Axel says:

    I have problems with that:

    $path = $this->getScriptPaths ();

    $file = substr ( func_get_arg ( 0 ), strlen ( $path [0] ) );
    //smarty needs a template_dir, and can only use templates,
    //found in that directory, so we have to strip it from the filename

    $this->_smarty->template_dir = $path [0];
    //set the template diretory as the first directory from the path

    My script is string(49) "appmodulesdefaultviewsscriptserror/error.tpl"
    and the path array is that:

    array(4) {
    [0] => string(38) "appmodulesverzeichnisviewsscripts"
    [1] => string(34) "appmodulesdefaultviewsscripts"
    [2] => string(18) "appviewslayouts"
    [3] => string(26) "appviewslayoutsscripts"
    }

    i.e my path is at inex 1 instead of 0. Why you assume, that the right path is always on index position 0?

    Thanks, Axel

  17. anders says:

    That is a very good question that I don’t have a good answer to at the moment. I’ve never came across the problem so far, but I see what you mean. Do you add the other view script path manually? because I thought that this script path was added automatically by the viewrenderer. And if you’re in another module you should only access those viewscripts. But I may be mistaken on the use as I have not been working a lot with different modules so far.

  18. Axel says:

    I think, they will be set automatically by the ViewRenderer based on the path spec: ->setViewBasePathSpec(‘app/modules/:module/views’). To be honest, the relationsship between view, viewRenderer and layout still seems to me a bit cryptically.

    I will try a bit around with the path settings to get a deeper understanding.

    I fixed this issue above in this way:

    $aPath = $this->getScriptPaths();
    foreach($aPath as $sPath) {
    $aScript = str_split(func_get_arg(0), strlen($sPath));
    if($aScript[0] == $sPath) {

    $this->_oSmarty->template_dir = $aScript[0];
    echo $this->_oSmarty->fetch($aScript[1]);
    }
    }
    throw new Sitechips_View_Exception ("Sitechips_View_Smarty::_run(): No valid script path provoded");

  19. Bovril says:

    Is there scope for partials and partialLoop using the Smarty plugin?

  20. anders says:

    it is very easy to add new smarty plugins using the plugin loader. just make a function in the Standard.php and call it partialFunction and you’ll have access to it in smarty.

  21. Tomas B says:

    Nice article, but I got one question (perhaps i misunderstood something, but anyway)

    What is the use of writing plugins in this case?
    For example you wrote plugin layoutFunction, but i have tried and you can reach layout object by calling $this->layout() and assigning it to var (int template) with {assign} plugin (In this way you can fix that one function call issue) and then reach all it’s values, so what is the point of writing that layout plugin ?
    In the same way you can call other helpers: partial, headLink etc.

    So what for is those plugins then, if you can reach helpers even without plugins?

  22. anders says:

    I guess that is also a way to do it, but I think it looks a lot cleaner and less repetitive code if you don’t have to repeat the assign procedure all the time.

    So writing the plugins was in part trying to make the code look better, while solving a problem.

    I’ve come to not use many of the helpers anyways, and the ones I use I can access anyways directly on the $this object. I now tend to use the viewhelpers in the controller instead of in the view directly.

  23. Tomas B says:

    Another downside i noticed is that Smarty_view becomes a heavy object. Hadn’t invested yet where is a problem but thing is that content is repeated as much times as there are registered plugins. So let’s say if content is 1MB and you got 4 functions (plugins) it becomes 4MB :/

  24. anders says:

    I don’t really see why the content should be loaded repeatedly for each plugin that is registered?

    The plugins are only loaded once and there is only one smarty view. One “problem” though that exist is that by assigning the $this object as a smarty variable, all of the smarty variables will be added twice, which is not that neat. So that is why the smarty view becomes a “heavy” object and probably not because of the plugins since they are only static methods.

    I’m going to look into the problem of having $this assigned at a later stage as it is not a problem for me at the moment

  25. Michael K. says:

    Hello,
    first thanks for this great article. It is working good, but I have a problem with the {layout section=”content”}.

    If I implement it in a Controller with
    $this->_helper->viewRenderer->setResponseSegment('section');
    I get correctly shown my template which has to be included.

    But now I does not want to write this code in every Controller action and if i understand rightly, this code is bad!? Please tell me what I am missunderstanding. ;-)

    Ok.. then I tried to pack these bit of code in a preDispatch() method and generate a new class which extends the class with preDispatch().
    But if I do so, I get a “Fatal error: Allowed memory size of 8388608 bytes exhausted “.
    I don’t understand what I am doing wrong :/

    Hope u can help me.
    Michael

    Sorry for bad english, I am german.

  26. anders says:

    Michael, this is a very good question and your English is fine. :)

    The reason for the fatal error is that each time you make a call to the helper in a predispatch it will be called once again in that loop. This creates a never ending loop which finally takes all the script execution memory.

    The solution that I’m using now and which I tried to explain briefly above is basically not using the “layout section=” method anymore just because of these issues.

    Now I’m using the action helper, which is not really helping to the extent I would like, but it is better than before. and that is using the part above in section 8.


    $this->view->placeholder('leftColumn')->set($this->view->action('test','Other'));

    This will render testAction in OtherController into a placeholder (“section”) called leftColumn.

    So you really don’t want to use the set response segment in this setup. But you might want to use it in other circumstances

  27. Mickael : I did got exactly the same problem. It was my contentKey that was bad.

    Look at your index.php, the line with Zend_layout::startMvc(…..);
    check the value of the ‘contentKey’ params. try to put the string ‘content’ to check.

    By the way, Anders, you did forget the class Exception.
    I did add that an Exception.php class in the EZ/View/Smarty/Plugin dir with this content :

    I think it’s ok ?

  28. Michael K. says:

    First thanks for your replies. Now I think I am understanding how it works.

    But there must be one more error on my side. So I don’t have to use the
    $this->_helper->viewRenderer->setResponseSegment('section');
    and the
    {layout section="content"} (?)

    I don’t know how to get the content into my layout.tpl. I tried:
    {$this->placeholder("content")}
    “content” is the contentKey I gave in:
    Zend_Layout::startMvc(array('layoutPath' => ROOT_DIR.$blv->getConfig()->site->layout->layoutPath,
    'view' => $view,
    'contentKey' => 'content',
    'inflector' => $inflector));

    But I see nothing from my views/scripts/index/index.tpl.

    Which hint do I oversee?

    Thanks for your answers.
    Michael

  29. anders says:

    No, you are right, you should still keep the
    {layout section="content"}
    for it to work. The placeholders should be used if you wanted to place data in other sections than the main content.

    I’m trying to work out the best way of handling this once you get to the point of needing sections within the layout… there are a few that have made suggestions, but I haven’t seen a good solution yet, apart from an early implementation of Zend_Layout witch had a concept called Layout_Requests which I really liked. But I haven’t figured out how to solve that with the current implementation.

    //Anders

  30. Michael K. says:

    Hello,
    thanks for your reply, it has taken me a step forward.
    I hope I am not going on your nerves, but I figured out, that if I add a placeholder decleration like
    $this->view->placeholder('footer')->set($this->view->action('footer','Layout'));
    in the Controller (there is no difference between adding it in predispatch or directly) the
    {layout section="content"}
    crashes. If I comment the placeholder declaration I see my content section stuff.. if it is uncommentet there is nothing.
    So the placeholder decleration is going to crash the layout section..!

    Any Ideas?

    Thanks Michael

  31. Michael K. says:

    Hello,
    solved the problem by myself by downloading the latest nightly build. Now it works.
    Thanks for your help!
    Michael

  32. Robert Raszczynski says:

    Hi Anders,

    thank you very much for sharing with this implementation.
    I found small problem with your EZ_View_Smarty class, when you set up more than one base path and your template file is not in first path from array, Smarty will trigger unable to read resource error. Here is quick fix I’ve done for myself:


    protected function _run()
    {
    $this->strictVars( true );

    //why 'this'?
    //to emulate standard zend view functionality
    //doesn't mess up smarty in any way
    $this->_smarty->assign_by_ref( 'this', $this );

    $paths = $this->getScriptPaths();

    //smarty needs a template_dir, and can only use templates,
    //found in that directory, so we have to strip it from the filename
    foreach ( $paths as $path ) {
    // only interested in compiling script from path that was pass as argument
    if ( 0 === stripos(func_get_arg(0), $path) ) {
    $file = substr( func_get_arg( 0 ), strlen( $path ) );
    //set the template directory as the first directory from the path
    $this->_smarty->template_dir = $path;
    $this->_smarty->compile_id = $path;
    break;
    }
    }

    //process the template (and filter the output)
    echo $this->_smarty->fetch( $file );
    }

  33. Sentry Chen says:

    Hi Anders
    Thanks for your article,can you share me ‘EZ_Language’ class code? If I don’t need Zend_Translate ,how to modify this:
    if (isset ( $count ) && isset ( $plural )) {
    $text = EZ_Language::getInstance ()->translatePlural ( $text, $plural, $count );
    } else { // use normal
    $text = EZ_Language::getInstance ()->translate ( $text );
    }

  34. anders says:

    Sure no problem. Here it is, but I’m still using Zend_Translate for the locale, and there is caching involved aswell. Have fun with it… :)

    class EZ_Language {
    protected static $theInstance;
    protected $translator;

    public static function getInstance() {
    if(!self::$theInstance instanceof EZ_Language)
    self::$theInstance = new EZ_Language();

    return self::$theInstance;
    }

    protected function __construct() {
    $cache = Zend_Cache::factory('Core', 'File', array('lifetime' => 3600), array('cache_dir' => APP_DIR . "/data/cache"));
    Zend_Translate::setCache($cache);

    $this->translator = new Zend_Translate("gettext", APP_DIR . "/data/language", null, array('scan' => Zend_Translate::LOCALE_DIRECTORY));
    $this->translator->setLocale(User_Settings::getLocale());
    }

    public static function setLocale($locale) {
    self::getInstance()->translator->setLocale($locale);
    }

    public static function translate($text) {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return self::getInstance()->translator->translate($text);
    }

    public static function translatePlural($text, $plural, $count) {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return self::getInstance()->translator->translate($text);
    }
    public static function _($text) {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return self::getInstance()->translator->translate($text);
    }

    public static function getTranslator() {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return self::getInstance()->translator;
    }

    protected static function verifyMessage($message) {
    if(!Zend_Registry::isRegistered('config') || !(Zend_Registry::get('config')->debugging))
    return;

    if(!self::getInstance()->translator->isTranslated($message))
    self::logFailed($message);
    }

    protected static function logFailed($message) {
    $fileName = APP_DIR . "/data/language/dummyTranslations.php";
    $contents = file($fileName);
    $str = "translate("{$message}");n";

    if(array_search($str, $contents))
    return;

    while(count($contents) > 0 && !preg_match("/^\?>/", trim($lastLine = array_pop($contents)))) {
    }
    array_push($contents, $str);
    array_push($contents, $lastLine);

    file_put_contents($fileName, $contents);
    }
    }

  35. anders says:

    oops… sorry for the missed formatting…

  36. Sentry Chen says:

    Thank very much! and I have one problem anout this

    public static function translatePlural($text, $plural, $count) {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return self::getInstance()->translator->translate($text);
    }

    maybe it should like that:?

    public static function translatePlural($text, $plural, $count) {
    if(!is_string($text) || strlen($text) == 0)
    return "";

    self::verifyMessage($text);

    return sprintf(self::getInstance()->translator->translate($text),$plural,$count);
    }

  37. anders says:

    Yes I think that’s how I meant. I haven’t been using the translate_plural so far. Thanks for spotting that copy paste bug. :)

  38. Laurent says:

    Hi,

    Thanks for sharing with us your smarty adapter for Zend Framework. Could you give us the license under which the code is published?

  39. anders says:

    Feel free to use it in anyway you like. You could pay me back by linking back to this site. But if you want a special license on it I guess you could use MIT or BSD license which basically allow you to use it in any way you wish.

    I’m not much for licenses. I believe that if I help someone, then I will get helped some other time by someone. :)

  40. SJD says:

    Not sure if I’m missing something obvious… but I can’t work out what format the args param should be in. For example, if I want to pass a ‘src’ parameter in args should it be something like this:

    {headScript method=’appendFile’ args=’src=/includes/scripts/tiny_mce/tiny_mce.js’}

    Thanks for your help, and the original article.

  41. anders says:

    The headscript is not intended to be used in that way. If you want to use appendfile you should do that from the controller.

    just do:
    $this->view->HeadScript()->appendFile('/includes/scripts/tiny_mce/tiny_mce.js');

    For it to work the way you are attempting you need to fix the TODO which is marked in the headScriptFunction on line 45. I stopped halfway for some reason. Probably that I found that I didn’t want to use it that way.

    I think it is important to keep as many of the helper function calls to the controllers in order to have a more strict MVC approach.

  42. Axel says:

    Hi,

    I run into problems by using the new Zend_Paginator component and also with the ViewScript Decorator of Zend_Form. It seems to be a problem with the partial view helper, wich will be used by both components. Do you have some hints at the collaboration of your Smarty integration and the partial view helper?

    Thank you,

    Axel

  43. anders says:

    Yes, I haven’t tried this myself yet, but you should be able to wrap all of the available view helpers in the plugin wrapper. Just make functions in the EZ_View_Smarty_Plugin_Standard class and form them in the same way.

    The functions there are essentially just wrappers to the regular “this” object that is used within the view.

    Once I find myself needing this myself, I’ll put it in there.

    //Anders

  44. Axel says:

    Thank you.
    I will try that. To be honest, I could not get what is the problem in this special case. I need to wrap helpers in the plugin class in those cases, where I need more than one access level of the helper object ($this->helpMe() will work $this-helpMe()->somethingMode() won’t work). Often I avoid ths situaltion by calling the helper within the controller. The partial heeler will be implicitely called within the pagiantion controll resp. the ViewScript decorator of Zend_Form. It normaly doesn’t need a deeper access level.

    Best Regards

    Axel
    Axel

  45. Max says:

    Hello. I’ve a next trouble. I want render template to the layout and next render another template to the template. I do:
    part of layout:

    {$this->placeholder(“BODY”)}

    part of template:
    {$this->placeholder(“content”)}

    IndexController: $this->view->placeholder(‘content’)->set($this->view->action(‘index’,'index’,'page’));
    $this->view->placeholder(‘BODY’)->set($this->view->render(‘index/index.tpl’));

    In result I want that in content render another layout, but when I change layout in Page module, layout change and in a IndexController.
    P.S. Sorry my english)

  46. Cas says:

    Hi Anders & other people,

    First my thanks, this guide has already helped me greatly with my Zend application. There’s only one thing I don’t understand. When I render Controller index and action index it requires layout.tpl & index.tpl, but it only uses layout.tpl. It’s probably really obvious but I want the result of the index.tpl within the layout.tpl. Maybe you could provide some directives? Anyhow great thanks!

    Cas

  47. anders says:

    It should actually need both layout.tpl and index.tpl as the layout template has the general site layout, and the index.tpl includes what is specific for the current controller/action.

    Maybe you are missing the


    {layout section="content"}

    inside your layout.tpl?

    //Anders

  48. Cas says:

    Thanks for your reply. I have the line with {layout section="content"} in my layout.tpl and when index.tpl doesn’t exists I get an Fatal error. But anything I write in index.tpl does not get rendered. I do not have anything written in my controllerAction(), should I put certain code there to get it to work?

  49. anders says:

    hmm

    actually I’ve been seeing that same issue sometimes when I make a new install of the system. For some reason I cannot replicate the problem on my site, but I think it has something to do with write permissions in the project directories.

    Please write back here if you find the problem.

    //Anders

  50. Cas says:

    Solution:

    $this->_helper->viewRenderer->setResponseSegment('content');

    =)

  51. Cas says:

    Hmm still experiencing problems. The index.tpl and other action scripts are getting rendered nicely except for the variables. Here’s the layout of my index.tpl:
    Welcome
    {$someVar}

    Action controller:
    public function indexAction()
    {
    $this->view->someVar = "test";
    $this->_helper->viewRenderer->setResponseSegment('content');
    }

    And when it renders I only get the message “Welcome”. Thanks for your help so far anyhow!

  52. anders says:

    The solution you provided above is not correct. The rendering into the layout section content is already taken care of by Zend_Layout.

    That’s why you get the additional problems.

    I have the exact above setup, and the not rendering of viewscripts in layouts is a bug that I know I solved, but I can’t remember what it was.

    It either had something to do with the version of Zend Framework I was using, or it was something with write access to some path on the server.

  53. anders says:

    I now know what the problem above comes from. It is a bug in Zend Framework 1.5 which was fixed in 1.5.1. The problem is that when downloading Zend Studio for Eclipse, it is version 1.5 that comes along with it. It is probably fixed now that 1.6 is released.

    So if your view script does not render into the layout section “content” your problem is the version of Zend Framework.

    //Anders

  54. Yves says:

    I use Zend Framework 1.6 but the content isn’t loaded automatically into the
    layout section “content” .

    I also have to use the $this->_helper->viewRenderer->setResponseSegment(‘content’); into my actions.

  55. madRenEGadE says:

    Hello I noticed a problem when using partials.
    I have added the functionality for partialLoops.
    When I use two partialLoops with the same model I get this message:
    Warning: Invalid argument supplied for foreach() in /home/madrenegade/public_html/madlest/library/Zend/View/Helper/PartialLoop.php on line 78

    {partialLoop file=index/entries.html model=$news}
    {partialLoop file=index/entries.html model=$news}

    Does somebody have had the same problem?

  56. Tom Anderson says:

    Using version 1.6 the placeholders have been renamed. changing the $toReturn line to ['this']->placeholder($section); works while leaving the ‘Zend_Layout’ alone. My replacement function is:


    public static function layoutFunction($params, &$smarty) {
    $section = $params ['section'];
    $toReturn = "";
    try {
    //oppress errors
    $x = $smarty->_tpl_vars ['this'];
    $key = Zend_Layout::getMvcInstance()->getContentKey();
    if ($section == $key) {
    $toReturn = $smarty->_tpl_vars ['this']->placeholder('Zend_Layout')->$section;
    } else {
    // Normal sections are not filed under Zend_Layout
    $toReturn = $smarty->_tpl_vars ['this']->placeholder($section);
    }
    return $toReturn;
    } catch ( Exception $e ) {
    return $toReturn;
    }
    }

  57. Kevin W says:

    Thanks so much, this got me more than started on a great application.

    I noticed in your xml config file you made all element names lowercase and when you use some of them in the php code you use camelCase. It took me forever to figure out what was happening when on line 52 of the bootstrap index.php where you use contentKey and in the xml you use contentkey.

    This was causing the index actions to not automatically add the content into the section loops. Maybe this was causing the problem people speak of above.

  58. zheng says:

    i can’t find the Plugin/Exception.php file?

  59. dantan says:

    Works great.
    I used Naneau View. With your implentation i do get Php Notice errors when a variable is not set.
    Ok, I can decrease Error_reporting, but it used to work with error_reporting(E_ALL).

  60. Hi,

    Thanks for the wonderful document.

    I try to get the help for ZEND Frame work 1.7, but I could not find any document like this. It is working with ZF 1.7 alos, Once again Thanks

    Mark

  61. Is there sample project for this example, because I could not setup this project. If you have any sample project pls give the link.

    Mark

  62. [...] Implementing Zend Layout and Smarty using Zend Framework MVC | anders.tyckr.com [...]

  63. manuscle says:

    Thanks a lot!

    It’s working perfectly on my project!

  64. Libor says:

    Hey man, thanks for a great tutorial… There are some files missing though:

    1) EZ/Language.php (used in EZ/View/Smarty/Plugin/Standard.php)
    2) EZ/View/Smarty/Exception.php (used in EZ/View/Smarty/Plugin/Broker.php)

  65. [...] valde bort att koda massa grejer själva genom Zend Framework (och det hjälpte ju lite att ha med Zend-experten Anders Fredriksson vid vårt [...]

  66. ewi says:

    problem due to

    $this->_helper->viewRenderer->setResponseSegment('content');

    I solved it after hours with an upper case:

    I didn’t use Zend_Config_Xml but Zend_Config_Ini and there are values case sensitive.

    layout.contentKey = “content”

  67. Rod says:

    Hi All,

    I’m extremely new to the zend framework. I’ve tried following the example above to integrate smarty, but i get the following errors. If someone could send me a working sample i’d appreciate it very very much:

    Catchable fatal error: Argument 1 passed to Zend_Controller_Action_Helper_ViewRenderer::setView() must implement interface Zend_View_Interface, instance of Smarty given, called in C:svn_workingwwwzendhelloworldpublicindex.php on line 44 and defined in C:svn_workingwwwzendhelloworldlibraryZendControllerActionHelperViewRenderer.php on line 217

    Cheers guys

  68. devv says:

    doesn’t work – integrating Smarty shouldn’t be this complicated.

  69. Niamh says:

    this is great, thanks for posting.

  70. muse says:

    Seems that with ZF 1.9 it does’nt work, maybe it’s because Zend has changed it’s loader feature??

  71. Paul says:

    Hi, we’ve been using this and it’s great … just wondering whether it’s been updated recently?

    Also, do you have an example of the headTitle et. al. functions in action (smarty)? I can’t figure it out.

    Thanks for posting this article BTW. It’s been a real project saver.

  72. Logan Waggoner says:

    Updated Method for Smarty 3.0

    I’m just getting started with zend framework, but I know Smarty and 3.0 beta 7 is out. With the changes to Smarty the config has to be changed anyway and it looks like the above link uses possibly a simpler newer method.

  73. anders says:

    Sorry I haven’t updated this for a long time. I haven’t used Smarty 3 but I’m sure there are easier ways to do the above now. However, from a quick look, I’m not sure that the link you provided does the same thing as my setup. I might update this when I get some more time. But for now, I’m “stuck” using ASP.NET MVC… sorry for swearing in this PHP world. :) But I must say, apart from all the licensing hassle from Microsoft, ASP.NET MVC is very nicely done.

  74. John Redisson says:

    How to change value of section content from Controller like this: as example $this->view->section(‘content’)->set(‘sometext’); ?

Leave a Reply

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

*