Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Layout Component Proposal

Proposed Component Name Zend_Layout
Developer Notes
Proposers Ralph Schindler
Matthew Weier O'Phinney
Revision 1.0 - 5 July 2007: Updated from community comments.
1.1 - 10 Aug 2007: Took out of construction and placed under review
1.2 - 30 Aug 2007: Updated proposal with new class skeletons
1.3 - 21 Sep 2007: Updated with layout view helper and Zend_View_Inflection
1.4 - 27 Sep 2007: Updated with layout variable placeholder implementation (wiki revision: 10)

Table of Contents

1. Overview

Zend_Layout is a composite component that joins the Controller, View and ViewRenderer together to implement a two-step-view pattern for solving the problem of "consistent look and feel".

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Must work with a dispatch loop and named response segments. Since we can forward between actions, or create a loop of actions to dispatch, and because the response content can exist in multiple segments, we have special needs and capabilities other frameworks don't. Rendering must be done after the dispatch loop has finished, and should use all content segments.
  • Must be able to work without MVC. While the above should be true, Layouts should be available to those not wanting to use the MVC, or using an alternate MVC.
  • Must be simple to invoke. A controller should be able to specify a layout at will, and this should be easy to do.
  • Must be configurable
    • Must be able to specify which layout to use, but allow for a 'default' layout
    • Must be able to specify location of layouts
    • Must be able to specify names of layout variables
  • Must be able to pick and choose which placeholders to render
  • Must have a simple API for specifying items to use in the <head> section In discussion with Ralph, we've decided that view variables and the collection helper are the proper solution for this.
  • Must follow same naming conventions followed in ViewRenderer
    • naming
    • suffixes
  • Must inherit from view set in view renderer (if in use). This allows view scripts as well as action controllers to set variables to use in the layout.

4. Dependencies on Other Framework Components

  • Zend_Exception
  • Zend_View_Interface
  • Zend_View_Helper_Placeholder (potentially; for variable storage and retrieval)
  • Zend_Controller_Action_Helper_ViewRenderer (will likely push inflection into a new class, and ViewRenderer and Zend_Layout will utilize that)
  • Zend_Controller_Plugin_Broker (optional)
  • Zend_Controller_Action_Helper_Abstract (optional)
  • Zend_Controller_Action_Helper_ViewRenderer (optional; for shared view object)
  • Zend_Controller_Action_HelperBroker (optional)

5. Theory of Operation

Zend_Layout attempts to solve a problem that, since rearing its ugly head over and over, has gone by many names: Composite Views, Layouts, Templates, Partial Views, and/or Complex Views. They all basically attempt to describe a common problem - that of being able to maintain a consistent look and feel throughout a site or web application while maintaining the "Don't Repeat Yourself" principals.

Zend_Layout addresses the problem with a common pattern, the two-step-view pattern ( The Two Step View pattern breaks the process up into two distinct stages. The primary stage allows the user requested action as well as layout requested actions to dispatch and execute their respective views saving them to the controllers response object. The second stage of the process takes the already dispatched actions responses and directs them to the layout where final placement of content is made (in a Layout specific view) before being sent back to the user.

Zend_Layout requires no changes to the existing codebase to accomplish its job. To maintain control over the dispatch process, Zend_Layout attaches a controller plugin - this is the essense of the Two-Step-View and a common way to implement the 2SV as we have seen already. Zend_Layout also registers its own action helper, so that the developer can directly interact with Zend_Layout from within their actions. Depending on the state of the layout subsystem (defined by either the config file, bootstrap declarations, or interactions with the action helper), Zend_Layout will engage and act as directed by the developer by either dispatching a 2SV layout, or simply not engaging at all (if so desired).

A major benefit the Two-Step-View implementation is that user requested (and layout requested) action controllers and views can inject dependencies into the final layout. For example, if the user has requested a url that would generate a form, the form view can request that the section of the overall layout include a specific form.js file, or even a form.css file be linked in the head section.

Finally, Zend_Layout can be used with the MVC, if so desired. If used in this way, it simply acts as a wrapper on Zend_View, allowing the developer to inject layout-specific variables.

6. Milestones / Tasks

If a milestone is already done, begin the description with "[DONE]", like this:

  • Milestone #: [DONE] Unit tests ...

7. Class Index

  • Zend_Layout
  • Zend_Layout_Action_Helper_Layout
  • Zend_Layout_Controller_Plugin
  • Zend_Layout_Exception
  • Zend_View_Helper_Layout
  • Zend_View_Inflection (Used by both ViewRenderer and Zend_Layout for mapping script names to actual scripts)

8. Use Cases



Action controller accessing layout

View accessing layout

Layout script

9. Class Skeletons








Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jul 06, 2007

    <p>I think it shoud be developed as front controller plugin.</p>

  2. Jul 08, 2007

    <p>Works great so far <ac:emoticon ac:name="wink" /></p>

  3. Jul 10, 2007

    <p>Zend_Layout solves a problem for me for a couple projects. It gives me some insight into how I can inject a segment of HTML based on an action into a template. I'm working on an XSLT based template plug-in for ZF, and this will help figure it all out.</p>

    <p>Also, for work, I can use this to directly solve a similar problem. I've written a framework that I want to adapt to the ZF. A 2-step layout is required, this solves it.</p>

    <p>The only problem I ran into was needing to register the Zend_Layout_ControllerPlugin_LayoutProcessor class as a plugin directly into my front controller. Took awhile to figure it, but it works like a charm now.</p>

    <p>I'm hoping that Zend_Layout makes it into ZF v1.1! Thanks for working on this Ralph.</p>

  4. Jul 16, 2007

    <p>what is the svn repo. for this? It's not in incubator yet.</p>

  5. Jul 24, 2007

    <p>I found it here (thanks Paddy): </p>

    <p><a class="external-link" href=""></a></p>

  6. Aug 07, 2007

    <p>Please provide use cases detailing a layout view script, as well as indicate how content is assigned to the layout script.</p>

  7. Aug 31, 2007

    <p><html><br />
    <head><br />
    <title><?php echo $this->title; ?><br />
    </head><br />
    <body><br />
    <?php echo $this->SomeSubRequest; ?><br />
    <?php echo $this->content; ?><br />
    </body><br />

    <p>Additionally you can find a full project at <a class="external-link" href=""></a></p>

  8. Aug 31, 2007

    <p>I should have mentioned, the VR produced output is always captured and passed to $this->content unless otherwise disabled.</p>

  9. Aug 31, 2007

    <p>Will the final version include a setConfig() feature? </p>

    <p>Something along the lines of:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    <!-- Layout view path -->

    <!-- View script suffix -->

    <!-- Default layout name -->

    <!-- Configure layout requests -->
    <!-- default.phtml -->
    <!-- <?php echo $this->menu; ?> -->

    <!-- Request params array -->

    <!-- Default module layouts -->

    <!-- Requests overrides? -->

    <!-- Blog module uses blog.phtml -->
    <staging extends="production">

    1. Sep 03, 2007

      <p>There will be the ability to specify a config object to the constructor, as well as via a setConfig() method. However, there are no plans currently to have support to switch layouts per-module/controller/or action; that would be a task for your own plugin. </p>

      <p>Additionally, we will be creating an "Inflector" that will be utilized by both the ViewRenderer and Zend_Layout for such things as setting view suffixes, path layouts, etc.</p>

      1. Sep 03, 2007

        <p>"However, there are no plans currently to have support to switch layouts per-module/controller/or action; that would be a task for your own plugin."</p>

        <p>I presume you mean from the Config... the prototypes for layout have setLayoutName and setLayoutFile which can be called from a lot of different contexts, including a controller init and an action. Passing the special value of boolean false to setLayoutName will disable the layout entirely. Passing null resets the layout to the default that was configured with setDefaultLayout during setup.</p>

        <p>The ability to switch layouts from code is important to keep, as without it, it would be a major pain to create file passthru's, or handle common front-page+content page differing layouts.</p>

        1. Dec 11, 2007

          <p>Yes, I mean from the config.</p>

          <p>As it stands in the current incarnation, you can do any of the following:</p>

          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          $layout->disableLayout(); // disables rendering layout
          $layout->setLayout(...); // switch to a different layout

          <p>setLayout() is very specific: it <strong>only</strong> sets the layout script; it cannot be used to disable it. Also, we do not have a concept of a default layout; you set the layout script, plain and simple, and the default value is used only if no value has been set.</p>

          <p>There are also action and view helpers that allow you to access the layout object in order to perform tasks such as the above.</p>

  10. Sep 27, 2007

    <p>Possibly a minor correction to use case UC-01:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // SET DEFAULT LAYOUT - this will set "APPLICATION_PATH/user/layouts/scripts/sub-path.phtml"
    // (layouts/scripts) assumes that you are using a view object of type Zend_View.

    <p>Looks like the default layout should instead be:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // SET DEFAULT LAYOUT - this will set "APPLICATION_PATH/user/layouts/scripts/sub-page.phtml"

  11. Sep 27, 2007

    <p>Probably a naive question, but how does a user override the name of the "scripts" directory?</p>

    <p>E.g., if I wanted:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[

    <p>instead of the default:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[

    1. Sep 27, 2007


  12. Sep 27, 2007

    <p>There seems to be a lot more functionality than is illustrated by the use cases section. The one use case that exists appears to need some more illustration of what exactly is happening.</p>

    <p>E.g., what exactly does the following mean?</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    public function jpegAction()

    Unknown macro: { $this->_helper->layout->disableLayout(); // optionally $this->_helper->layout->setLayout(false); }


    <p>Is <code>disableLayout()</code> an alternative for <code>setLayout(false)</code>, or are they optionally used in [sequential] combination?</p>

    <p>I think some more use cases that cover more of the available functionality would be helpful.</p>

    1. Sep 27, 2007

      <p>I've removed the setLayout(false) option; you're right, that was confusing.</p>

      <p>I've also added some text to the setLayout() function indicating that it implicitly enables layouts.</p>

      <p>I've added a use case showing a layout script, and will add more use cases later.</p>

  13. Sep 27, 2007

    <p>Some other minor issues to address:</p>

    <li>Docblock for <code>setLayout()</code> should have "<code>@param string|boolean $name</code>" if it indeed provides layout disabling behavior by accepting a value of <code>false</code> for <code>$name</code>.</li>
    <li>What does <code>getLayout()</code> return when layouts are disabled?</li>
    <li>Why is there no <code>enableLayout()</code> method, or explain that <code>setLayout()</code> performs this task.</li>
    <li>Explain more about layout variable prefixes... what is the default layout variable prefix, if any? Is the default prefix (if any) configurable?</li>
    <li>Why not support <code>isset()</code> and <code>unset()</code> for layout variables?</li>
    <li>The <code>assign()</code> docblock is unclear - how to use this method when the first param is an array?</li>
    <li>DocBlock for <code>render()</code> indicates that there is a script path stack, but this component seems to support only a single script path, not a stack thereof. Please explain.</li>
    <li>I suggest using <code>string|array|Zend_Config</code> and similar instead of <code>mixed</code> for the docblocks of <code>Zend_Layout::__construct()</code> et al, where the various types may be enumerated.</li>

    <p>Action helper:</p>
    <li>We could stand to DRY up the code a bit:
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    public function getLayout()

    public function setLayoutName($name)

    Unknown macro: { $this->getLayout()->setLayout($name); }

    public function direct($name)

    Unknown macro: { return $this->setLayoutName($name); }


    <p>View Helper:</p>
    <li>Does not declare the <code>$_layout</code> property.</li>
    <li>No need for two <code>if</code> statements in the <code>getLayout()</code> method; combine conditions using <code>&&</code>.</li>
    <li><code>getLayout()</code>: what happens if <code>$this->_front->hasPlugin($this->pluginClass)</code> is false?</li>

    <li>We should see some use cases to illustrate how to work with this.</li>

    1. Sep 27, 2007

      <li>Updated setLayout() docblock; no longer can disable using that</li>
      <li>getLayout() will return false if layouts are disabled</li>
      <li>documented that setLayout() enables layouts</li>
      <li>layout variable prefixes will no longer be used; you will pull variables from the layout object instead.</li>
      <li>assign() docblock updated. If first param is an assoc array, it assigns each element as a layout variable</li>
      <li>render() will actually use the view object's script path stack, but assign the layout script path, if any, prior to attempting to render the layout script. Hence the language referring to the stack. <ac:emoticon ac:name="smile" /></li>
      <li>I'll update the docblocks to use alternatives instead of mixed</li>

      <p>Action helper:</p>
      <li>Good idea – I'll implement that when accepted</li>

      <p>View helper:</p>
      <li>Will declare $_layout property in final version</li>
      <li>Will combine conditions</li>
      <li>if hasPlugin() is false, it will throw an exception.</li>

      <li>I'll post use cases shortly.</li>

  14. Sep 27, 2007

    <p>It would be cool to see a use case that shows how to implement the Two-Step-View for providing differently marked-up views (e.g., HTML and XML) of the same model data.</p>

  15. Sep 27, 2007

    <p>When using layouts with Zend_Layout, <code>Zend_View_Abstract::render()</code> will always return the view script wrapped in the layout. This is of course desirable for the primary call to <code>render()</code> but if the view script then renders others within itself, e.g. <code><?php echo $this->render('book.phtml'); ?></code>, then the output of that nested call to <code>render()</code> will also be wrapped in the layout, which will often not be what you want.</p>

    <p>It would be useful for there to be a means to prevent the layout re-rendering within the view script for cases like this, perhaps on a per-render() basis.</p>

    1. Sep 27, 2007

      <p>Zend_Layout assumes that the applications have rendered their own view scripts, and is simply taking the rendered content and injecting it into the layout; there will likely not be any instances where this will be an issue.</p>

  16. Sep 28, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comments</ac:parameter><ac:rich-text-body>
    <p>This proposal is approved for immediate inclusion in the incubator, with the following requirements:</p>

    <li>Please provide additional use cases showing usage of the inflector</li>
    <li>Please DRY up the action helper code (direct()/setLayoutName() methods, specifically)</li>
    <li>Do not have setLayout(false) disable layouts; stick to disableLayout() only</li>
    <li>Use placeholder helper container for storing layout variables, not variable prefixes</li>

  17. Oct 17, 2007

    <p>Could it perhaps be of any use to be able to specify the content-type and encoding of the output and as such having a special header sent out?</p>

    <p>What I'm thinking about is having this as a header:<br />
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </p>

    <p>Which would be something like:<br />
    $this->getResponse()->setHeader('Content-Type', 'text/html; charset=iso-8859-1');</p>

    <p>As far as I know an header is to be preferred over a meta-tag and it would be nice to easily be able to send the correct headers for the layout.</p>

    1. Dec 11, 2007

      <p>This would be a better task for the doctype() view helper. <ac:emoticon ac:name="smile" /></p>

  18. Nov 05, 2007

    <p>What would be the Zend_Layout equivalant for Xend_Layout "subrequests"?</p>

    1. Nov 05, 2007

      <p>We've separated out the subrequests into a separate action helper, as they could be useful with or without Zend_Layout. In the incubator, you'll want to look at Zend_Controller_Action_Helper_ActionStack and Zend_Controller_Plugin_ActionStack; the former allows you to add actions to the stack, the latter processes them during postDispatch().</p>

  19. Nov 14, 2007

    <p>Hi, </p>

    <p>I suggest to implement in the constructor the possibility to define the layout suffix (tpl, php, ...)?</p>

    <p>Good Work <ac:emoticon ac:name="smile" /></p>

    1. Dec 11, 2007

      <p>Good comment. I've made a note of it, and will open an issue for it in the coming days. This feature will likely be in the released version.</p>

  20. Nov 30, 2007

    <p>I noticed that <a href="">controller plugin in the incubator</a> renders the layout script on postDispatch, while the class skeleton listed here has it rendering the layout script on dispatchLoopShutdown. What's the advantage to rendering on postDispatch?</p>

  21. Dec 10, 2007

    <p>Maybe I'm just not understanding Zend_Layout, but can someone explain how to use it in a modular directory layout?  My directories are set up like:</p>
    <li> application


    <p>Is there any way to set up Zend_Layout to either have one default layout for all modules, or default layouts per module?</p>

    1. Dec 11, 2007

      <p>Yes, and pretty easily as well.</p>

      <p>First off, Zend_Layout has 'layout.phtml' as its default layout. It also allows you to specify a layout path. If used, it will look for layouts in this path first, before descending into any other view script paths. As an example usage:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      Zend_Layout::startMvc(array('layoutPath' => 'path/to/layouts/'));

      <p>If you want to choose a different layout, you can do so either in view scripts or action helpers:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      // calling an action helper within an action controller:

      // calling via a view script:
      <?php $this->layout()->setLayout('alternate'); ?>

      <p>When using with the MVC, you can also access the layout object using getMvcInstance():</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[

      <p>This latter notation could be used in a plugin, for instance, to switch the layout used based on the current module.</p>

      1. Dec 12, 2007

        <p>Oh, I see. Is there any way to set different default layouts per module?</p>

        1. Dec 12, 2007

          <p>There are no plans to have this functionality integrated in the core component; you'll need to do it yourself.</p>

          <p>As mentioned above, there is already an accessor for changing the layout; you simply need to call setLayout() with a layout script name.</p>

          <p>Write a plugin that checks for the current module, and sets the layout based on that using the setLayout() method.</p>

  22. Dec 10, 2007

    <p>Is there an example a little more detailed?</p>

    <p>I am trying to use the example here, and I am not having success.</p>

    1. Dec 11, 2007

      <p>Zend_Layout is now in core, with complete documentation. Please pull from current svn trunk, and read the documentation located under documentation/manuals/en/module_specs/ for examples and uses cases.</p>

  23. Jan 28, 2008

    <p>i didn't think the MVC part is well designed for now.</p>

    <p>Why did i need to get an instance of Zend_Layout in the MVC by my self, it looks terrible, why did i need to "flag" the Zend_Layout as MVC?</p>
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $layout = new Zend_Layout("",true);
    $front = Zend_Controller_Front::getInstance();
    <p>i think the right way would be this way:</p>
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $front = Zend_Controller_Front::getInstance()
    ->registerPlugin(new Zend_Layout_Controller_Plugin_Layout($path));
    <p>in this situation it is completlly clear that the Zend_Layout is using in an MVC envierment. Internal Zend_Layout_Controller_Plugin_Layout should check if $_layout is empty and create the Zend_Layout with Default configuration. If i need to change something there should be somethink wellknown like </p>
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $layout = Zend_Layout::getInstance();

    1. Jan 28, 2008

      <p>With a component like this, there are many "right ways" to do it. Your opinion is as valid as those of the many who helped develop this one. However, it does not mean we will change anything; the time to raise such issues was during the proposal phase.</p>

      <p>Zend_Layout could not be a singleton. Many developers expressed an interest to use Zend_Layout without the MVC and/or use multiple layout scripts. A singeleton would not answer that issue, yet we needed access to a single instance at a time so that each of the action helper, plugin, and view helper could have access to the same Zend_Layout object. Additionally, we decided that it made most sense to keep most of the Layout components under the same tree so that people knew where to look for them, instead of needing to hunt in a variety of trees. As a result, the Zend_Layout class needs to register additional helper paths with some objects.</p>

      <p>To keep access as simple as possible, we chose to have this access through the Zend_Layout class itself, and hence the 'startMvc' and 'getMvcInstance()' methods were born. These are substantially easier to type and remember than Zend_Controller_Front::getInstance()->getParam('Zend_Layout') or Zend_Registry::get('Zend_Layout') (which was another possibility we considered). Additionally, startMvc() ensures that the all the appropriate coupling points are engaged, without needing to go and manually register plugins and helper paths.</p>

      <p>We try very hard in ZF not to instantiate things behind the scenes, particularly when they interact with existing components such as the MVC. In this instance, we have a number of things to consider: where should the layout object look for layout scripts, and should Zend_Controller have any awareness of it? Requiring an explicit registration of the object seemed like the safest solution to ensure both forward and backward compatability. </p>

      <p>BTW, Currently, the layout action helper <strong>will</strong> create a new Zend_Layout object if the MVC instances is not found. It does <strong>not</strong>, however, create an instance for use with the MVC, as the user has not indicated that they want the plugin enabled.</p>

      <p>Finally, if you don't like how Zend_Layout works, you're free to subclass it to get the syntax you desire.</p>

      1. Jan 29, 2008

        <blockquote><p>BTW, Currently, the layout action helper will create a new Zend_Layout object if the MVC instances is not found. It does not, however, create an instance for use with the MVC, as the user has not indicated that they want the plugin enabled.</p></blockquote>
        <p>and what is the reason that Zend_Controller_Plugin_Layout didnt do that? I mean it's pretty clear Zend_Layout is used in MVC, so why not handle it like in the Action helper. Additinal, just give the Zend_Layout_Controller_Plugin_Layout the $path for the View Scripts so it can create an Zend_Layout instance with the right configuration. </p>

        <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
        public function __construct(Zend_Layout $layout = null)
        if (null !== $layout)

        Unknown macro: { $this->setLayoutInstance($layout); }


        Unknown macro: { $layout = Zend_Layout}

        <p>seams that Zend_Layout_Controller_Action_Helper_Layout creates an MVC instance if no Zend_Layout is given. </p>