Quick introduction to using Zend_Layout
November 24, 2007 – 10:29 pm Tags: PHP, Zend FrameworkLately, the Zend_Layout component has been a very popular topic on #zftalk. It is indeed a very useful component, but a lot of people are having trouble understanding how it works.
Adding to the confusion is the outdated documentation regarding the component proposal and the equally outdated articles about Zend/Xend_Layout.
Let’s demonstrate the usage of the current Zend_Layout implementation, which can be found from the Zend Framework SVN or Snapshots in the Incubator directory.
Note: if you are using the Zend_Layout from 1.0.3, the stuff mentioned here will still work.
Zend Framework 1.5 users: there is an updated version of this article available here
Getting started with Zend_Layout
First, you need to grab the library and incubator/library directory from the latest snapshot. You can either copy the files from this directory to the main library directory of the snapshot, or create an another directory in your library dir, such as Zend-Incubator.
You can find the latest snapshot at Zend Framework Nightly Snapshots.
If you do create the Zend-Incubator directory in addition to the default Zend directory, remember to add it to your include path!
The next step is to initialize the layout. Put this code in your boostrap, before you dispatch the request:
$layout = new Zend_Layout(array( 'layoutPath' => './application/layouts', 'layout' => 'default' )); $view = $layout->getView(); $view->addHelperPath('./library/Zend/View/Helper');
Note for Zend Framework 1.0.3 users: the layout constructor needs a parameter to enable MVC support, so use this instead of the above:
$layout = new Zend_Layout(array( 'layoutPath' => './application/layouts', 'layout' => 'default' ), true);
First we create a new Zend_Layout instance. The parameters define where the layout files will be found and what is the name of the default layout. So create a directory under your application called “layouts” if you’re using that as your layoutPath.
Next we grab the view script from the layout. This allows us to add variables and change the view’s settings. We add a new helper path to the view, which allows us to use the new view helpers which came with Zend_Layout. If you’re using the Zend-Incubator dir, remember to add that as a helper path as well.
Now you have Zend_Layout configured. Create an empty layout file called default.phtml to application/layouts or your layout dir.
Adding things to the layout
Layouts work quite much like your normal view scripts. A lot of the extra features depend on the new view helpers.
Let’s first look at a basic layout file:
<html>
<head></head>
<body>
<div>
The site's menu is here: home | links | stuff
</div>
<div>
<?php echo $this->placeholder('Zend_Layout')->content; ?>
</div>
</body>
</html>The key here is the placeholder viewhelper. It acts kind of a like a registry, and you can access the Zend_Layout object through it. So we are basically accessing the variable content from the layout instance.
By default, all content that is not rendered in a specific response segment goes into the content variable in the layout, so if you’re letting the viewrenderer render a view script in your controller actions, the content of the rendered view script will be available in the content variable.
We can also expand on this:
<html>
<head></head>
<body>
<div>
The site's menu is here: home | links | stuff
</div>
<div>
<?php echo $this->placeholder('Zend_Layout')->content; ?>
</div>
<div>
<?php echo $this->placeholder('Zend_Layout')->sidebar; ?>
</div>
</body>
</html>Now we have a sidebar placeholder. How do we put any content in it?
We can either have some action render a view into a response segment with the same name, or we can use the layout action helper.
class ExampleController extends Zend_Controller_Action { public function exampleAction() { $this->_helper->layout()->sidebar = 'This goes into the sidebar variable in the layout!'; } public function anotherAction() { $this->render('sidebar', 'sidebar', true); //Render the action's own script $this->render('another','default'); } }
So using the layout with exampleAction would put the text string into the sidebar and the anotherAction would render the contents of the sidebar.phtml script to it.
More helpers
There are also some other helpers for your layouting and viewing pleasure.
The action helper
Some of you may have heard about subrequests. Well, this is what the subrequest turned into: The action view helper.
This can be used in your layouts and views to output content from other controllers.
<div>
<?php echo $this->action('menu', 'actions', 'default', array('activeLink' => 'Home')); ?>
</div>This would call the menuAction in ActionsController in the default module and pass a parameter called activeLink to it.
The partial and partialLoop helpers
Partial is similar to using render to include other views.
<div>
<?php echo $this->partial('profile', null, array('name' => 'Peter')); ?>
</div>This would include partial “profile.phtml” from the default module with parameter name. Note that unlike when using render(), partials will not inherit the parent view’s variables! This is why you may need to pass them in an array like in the above example.
PartialLoop is like partial, but it will act like a loop:
<table>
<?php echo $this->partialLoop('table-row', null, array(
array('col1' => 'value1', 'col2' => 'value2'),
array('col1' => 'value3', 'col2' => 'value4'),
array('col1' => 'value5', 'col2' => 'value6')
)); ?>
</table>The above would call the imaginary table-row partial three times with each sub-array as parameter. Note that the parameters don’t have to be hand-written like this, you could use database-rows or other things too.
The headScript, headTitle and others
HeadScript, HeadTitle, HeadStyle, HeadLink and HeadMeta can be used as placeholders in your layouts. For example, your layout can contain the following:
<html>
<head>
<?php echo $this->headTitle('My site'); ?>
</head>
<body>
<?php echo $this->placeholder('Zend_Layout')->content; ?>
</body>
</html>The headtitle helper will output a title element with the text ‘My site’ in it.
Now, say we want to modify the title in one of our pages. Let’s make a view script for the page:
<?php $this->headTitle()->append(' Example page'); ?>
This page changes the title and adds a javascript file.This would append Example page to the title.
The other Head* helpers work in a similar manner.
As of writing this, HeadScript helper is broken and unusable.
Conclusion
Even though it’s not finished yet (HeadScript…), the Zend_Layout component provides an easy and powerful complex view solution.
Let’s hope they get it quickly finished so we can start using HeadScript and others too.
There are some things to think of, though. For example, what would be the best way to use the sidebar-thing I showed? Should you wire up a dedicated Action helper call? Should you leave that to your controllers? I’d be interested to hear your ideas and how you’ve implemented things like that.












33 Responses to “Quick introduction to using Zend_Layout”
If I use the action helper like this:
action(’menu’, ‘actions’, ‘default’, array(’activeLink’ => ‘Home’)); ?>
it will ignore my Auth settings. If I add an action helper using actionStack from the controller, my Auth settings are followed. Is that the correct behaviour?
By GerryL on Nov 26, 2007
Hello!
I like your tutorial, it is very easy to follow. But I get a “Fatal error: Call to protected Zend_Layout::__construct()” on the very first line of code you posted. I did put it before the dispatch of the Zend_Controller. Any idea what I did wrong?
Best regards,
macorama
By macorama on Nov 26, 2007
GerryL:
I haven’t tested the layout components with ACL yet, so I would assume that what you get is correct.
macorama:
I’m not sure, but it sounds like you might have the wrong version of Zend_Layout. Make sure you have one from the current releases.
You can get an up to date version of it at Zend Framework Nightly Snapshots
By Jani Hartikainen on Nov 26, 2007
Hello,
yes indeed it was the wrong source. I got it directly from schindler’s svn… I should’ve gone with what you’d posted
By macorama on Nov 26, 2007
Is there any way to stop the delivery of the Layout in an action in case you just want to send the result of an action, for example in actions that are called with ajax?
By Tom Schultz on Nov 28, 2007
You should be able to disable the layout with the disableLayout() method in the layout object.
By Jani Hartikainen on Nov 28, 2007
Thanks!
Using the following make my Ajax Chat-UpdateAction working without sending the whole layout:
$this->_helper->layout()->disableLayout();
Nice, small Zend_Layout this is here helped me comprehend the Layout stuff.
By Tom Schultz on Nov 28, 2007
Thanks a lot! This is exactly what i needs!
But you have used two proposal ideas - Zend_Layout and Zend_View Enchanced.
We are all waiting for next release. Do they plan to include Zend_Layout & Zend_View Enchanced onto it?
By iDownload on Nov 30, 2007
the “old” Zend_Layout proposal was combined with the Zend_View_Enhanced proposal. Best ideas from both are now in the current version of Zend_Layout.
By Jani Hartikainen on Nov 30, 2007
By the way… bacause of latest updates at Zend_Layout you should modify your example to make it work with MVC… because of initMVC parameter was separated from options and set default to false.
[code]
$layout = new Zend_Layout(array(
‘layoutPath’ => ‘./application/layouts’,
‘layout’ => ‘default’
), true);
[/code]
By iDownload on Dec 1, 2007
Thanks, updated.
By Jani Hartikainen on Dec 1, 2007
Thanks for a great helpful article. Without this I would have been totally lost.
By JDempster on Dec 1, 2007
Thanks,
Nice,up-to-date, helpfull
Leo
By Leo on Dec 3, 2007
Hi, great article. Finally I’m getting some visible results!
However, I’m not getting nothing in “content” variable unless I call “$this->render(action)” in controller (to render action.html).
Is it possible to make it automatic, as ViewRenderer does unless “noViewRenderer” is set?
By vb on Dec 4, 2007
Hi I downloaded the layout code and followed your example but I get the error
Fatal error: Call to protected Zend_Layout::__construct() from invalid context in C:\xampp\htdocs\fuseCMS\index.php on line 16
Any idea would be great
Thanks
Ryan
By Ryan on Dec 6, 2007
vb: It should be automatic. Not sure what could be wrong if it isn’t.
Ryan: You are probably using the old version of Zend_Layout. Get the latest from ZF Nightly Snapshots
By Jani Hartikainen on Dec 6, 2007
Hi Jani,
I “found out” that I have a special initView() in my base action controller that sets up and registers viewRenderer. When I removed that, works like a charm.
Thanks,
vlatko
By vb on Dec 6, 2007
Hi Jani,
let me ask you for our opinion about where to put a template rendering function. I’m testing PHPTAL and it has (after some bootstrap initialization) one function for rendering:
echo $template->execute();
To me (new to MVC and ZF), the easiest way (no new classes) seems to put this function in the “default.phtml”.
Default.phtml:
template->execute();
?>
Of course, this is acceptable for testing, but…
Also, is there an API manual for Zend_Layout?
br,
vlatko
By vb on Dec 6, 2007
Hi, just came across this tutorial, and I was a bit surprised.
I’ve just implemented a solution with Zend_Layout using the Smarty template engine.
So now I’m both happy and unhappy to learn that Zend_Layout is completely changed in how it is supposed to work.
What happened to the layout requests? I thought that was the best part of the whole thing. Being able to add layout-requests from the controller to invoke actions in certain controllers to render parts of the content into layout variables.
To me it is completely wrong to have subrequests done in the view. The view should not include any logic, so I’m wondering why this was changed.
I’m going to do a write-up of my implementation soon, but at the moment I’m working on how to append scripts and styles to the header that only apply to certain actions in certain controllers. This is so that I don’t have to include all scripts and css files on all pages and still being able to use a standard layout for the site with some scripts and styles always being included.
Anyways, thanks for enlightening me on that Zend_Layout now is completely different.
By Anders Fredriksson on Dec 12, 2007
vb: for “proper” ZF integration of any templating engine, you should make a wrapper class which extends Zend_View.
Zend_Layout has been finally moved into trunk in Svn, so I’m assuming that the next stable release of ZF will include it which would mean they are also writing docs for it.
Anders: heh, I know some others who did something similar. I also had a very simple layout implementation with a replacement ViewRenderer.
You can still do “subrequests” in the controller, although it’s a bit more clunky to do with the methods I showed. Personally I think the action-view helper is a better idea because it keeps view related stuff more in the view.
By Jani Hartikainen on Dec 12, 2007
The thing is that I wanted to avoid to do logic in the view scripts. So I didn’t like the way the action-view helper worked.
What I want to set up is basically that if you think of a website to be made up in modules, I want each module to have its own action and that each controller should be responsible for the content and rendering of each module.
An easy way to set this up with the old zend layout request, was to make simple requests in the init of a parent controller_action that extended the zend_controller_action… see below:
ST/Controller/Action.php
[code]
class ST_Controller_Action extends Zend_Controller_Action {
…
public function init() {
…
$this->getHelper(’LayoutManager’) ->getLayout(’Special’);
->addRequest(new Zend_Layout_Request(’header’,'getHeader’, $this->requestObj->getControllerName(), $this->requestObj->getModuleName()));
}
public function getHeaderAction() {
//default values
$this->view->title = “testTitle”;
}
}
——–
IndexController.php
class IndexController extends ST_Controller_Action {
public function indexAction() {
$this->view->someVar = “test”;
}
//posibility to add specialized header here
//public function getHeaderAction() {
// $this->view->title = “another testTitle”;
//}
}
——
layouts/scripts/default.tpl
{$header}
{$content}
{$footer}
——
views/scripts/index/index.tpl
this is {$someVar} in index.tpl
—–
views/scripts/index/getHeader.tpl
{$title}
[/code]
I also have my own smarty implementation that works great with the autorendering viewRenderer.
So how could I do the equivalent to layout requests but with the new setup? I was also just talking to Akrabat on #zftalk and he has just made a simple tutorial too: http://akrabat.com/2007/12/11/simple-zend_layout-example/
well well.
but that’s not doing what I want either.
By Anders Fredriksson on Dec 12, 2007
Ok so the html tags from the tpl files were stripped, but you get the point…
By Anders Fredriksson on Dec 12, 2007
There’s a helper called ActionStack which could possibly be used to do something like this but it isn’t very helpful yet.
Basically you “put actions in the stack”, and the actions are then ran after the current one. There’s one problem with this approach because the current version of the helper does not let you choose which response segment the output goes to, so it will always end up in the default so it isn’t very helpful yet.
I’m also doing something like that in one of my ongoing projects. I have a foreach in my default layout, which uses the action helper to call actions. I assign the actions in my controllers by doing something like this
Probably not as nice as subrequest-like stuff, but does its job for me.
By the way, if you were wondering why your comment didn’t show up.. WordPress thought it was spam and I had to manually approve it
By Jani Hartikainen on Dec 12, 2007
This worked great except the action helper does not seem to actually run the action code. It displayed the view without running the action. Any ideas what I am doing wrong?
Thanks
Alvin
By Alvin on Dec 22, 2007
Hi. When I try to use action helper, the arguments are not passed. In my layout I put
action(’get’, ‘menu’, ‘common’, array(’activeLink’ => ‘1′)); ?>
but the activeLink argument is not taken in the getAction()
Any suggestion?
Thanks
Charly_75ar
By Charly_75ar on Jan 7, 2008
I found the workaround for the situation when the arguments are not passed in $this->action(’menu’, ‘actions’, null, array(’activeLink’ => ‘Hello’));
I can obtain the desired argument trought Request object using $this->getRequest()->getParam(’activeLink’);
But I really don’t know if this a correct way. :-S
Charly_75ar
By Charly_75ar on Jan 7, 2008
I think that is the intended behavior. The arguments array is placed as parameters in the request, not passed as parameters to the function if that is what you were trying to do.
By Jani Hartikainen on Jan 8, 2008
You can use the headScript quite easily,
layout.phtml
$this->HeadScript(’File’,'js/test.js’)
$this->HeadScript(’Script’,'javascript code’);
controller
$this->view->headScript()->appendFile(”js/test.js”);
By Gooseware on Jan 8, 2008
I can’t remember where I read this right now, but I think Javascript files should not be referenced in the top of the page, but in the bottom (at the end of the body element).
This is so the Browser will load the CSS files first and begin rendering the actual page before all the script files have finished loading.
This is supposed to significantly reduce waiting time for visitors. They can see something right ahead, instead of waiting for the JS to finish loading.
So it’s probably a good idea to remember to print the HeadScript stuff in the bottom of the BODY instead of the HEAD element.
By Daniel on Feb 11, 2008
I recall reading something similar as well. I think it was posted in the YUI blog
By Jani Hartikainen on Feb 15, 2008
Thanks for the article. It helped lot.
Mark
By Outsourcing India on Dec 2, 2008
Hmm not really easy to follow if you only started using the Zend framework yesterday
In the above example where do i put the ExampleController?
By shane on Jul 9, 2009