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_View Enhanced Component Proposal

Proposed Component Name Zend_View Enhanced
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_View Enhanced
Proposers Pádraic Brady
Matthew Weier O'Phinney, Zend liaison
Revision 1.2 - 26 June 2007 (wiki revision: 25)

Table of Contents

1. Overview

The purpose of the Zend_View Enhanced proposal is to propose the addition of Zend_View_Abstract methods, and Zend_View_Helper_* classes, which in combination provide for the ability to generate output in a flexible manner which adheres to the separation of Controller and View, advocates decoupling, and maintains backwards compatibility with the 1.0.0GA version of the Framework, and subsequent minor releases.

At present, Zend_View is a strict implementation of the Template View design pattern. It excels at templating, but has no native functionality for handling several key requirements of the View. This typically includes decorating templates with a common layout, supporting the rendering of templates in their own independent variable scope, allowing templates to transport additional presentation logic into other templates transparently, making calls into the Controller for additional embeddable output, and capturing common presentation logic as View Helpers. Suggested terms for these features:

1. Layouts
2. Partials
3. Controllers
4. Placeholders
5. View Helpers (as now)

The common perception is that the Controller is required to facilitate many of these View features. Unfortunately, this is misleading. These approaches have no such dependency on the Controller unless such a dependency is implemented purposefully. All of the above are already standard for templating engines in several frameworks - even Smarty has native capabilities for many of these. The Controller is not a requirement for their implementation.

The argument this idea springs from is that Controller based solutions inevitably require far more code. This in turn requires more processing (and a possible performance hit), and the writing of a multitude of additional Controllers. Controller based approaches usually violate KISS (Keep It Simple Stupid) and are not a replacement for View based functions which are available as part and parcel of the template syntax.

The proposal, however, makes no assumptions about the Controller. The only such assumption is that a minor issue may be resolved (#ZF-1545). If no resolution is forthcoming, an alternate means of implementation may be used.

Wherever possible, View Helpers are preferred in place of adding code directly to Zend_View_Abstract. A number of these View Helpers have been proposed which capture the above four concepts, and add specialised variants to simplify template authoring. These expand the reach of View Helpers primarily to the <head> section of a HTML document where a lot of activity around Javascript, CSS, meta tags, etc. may be controlled outside a Layout within the actual sub-templates (a common requirement of any nested View design).

The proposed additions maintain Zend_View's decoupling from Zend_Controller. They should (as a requirement) have no conflict with Controller integrated strategies for similar features. There are therefore no conflicts with the addition of Controller based variants for Layouts such as the introduction of a Two-Step View implementation. However, layouts will be dealt with under a separate proposal.

The additions are therefore quite straight forward, easy to implement, and pose no threat to backwards compatibility.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Must implement the -4-3 view construction strategies (Layout, Partial, Controller, Placeholder) (Note: Layouts will be dealt with in a separate proposal)
  • Must implement several specialised View Helpers for controlling Element additions to the <head>
  • Must maintain backwards compatibility with Zend_View and Zend_Controller_Action_Helper_ViewRenderer
  • Must (where feasible) be implemented as default View Helpers
  • Should not prevent or restrict Controller derived strategies for output construction
  • Must be accompanied by a full set of integration tests prior to acceptance in Core
  • May optionally require adoption of the setters suggested in Zend_Font - Karol Babioch#ZF-1545

4. Dependencies on Other Framework Components

  • Zend_View_Abstract
  • Zend_View_Exception
  • Zend_Controller_* (only for Controller strategy)

5. Theory of Operation

Zend_View Enhanced advocates a template driven approach to output generation. As templates are rendered by Zend_View, the templates themselves may invoke Partials and Controllers, as well as set Placeholder content (template specific or default) which is centrally stored for higher level and subsequent templates (such as Layouts) to utilise. Layouts are applied at the end of any Zend_View instance's rendering process to take advantage of Placeholders, and their specialised variants.

Partials: Partials simply render a specified template within it's own separate variable scope. This is useful for reusable template fragments, avoids variable name clashes, and allows configuration of Partials to include from independent Modules (i.e. using that Module's helpers/filters/paths). At a higher level, Partials implement the Composite View design pattern allowing for the generation of a tree of View objects for even more flexibility.

Controllers: Dispatches a request to the Controller which returns output for embedding in a template. This must be compatible with ViewRenderer. May be used for querying the Model, but it's generally recommended to use a View Helper where possible. Most likely useful where authentication or authorisation must also be queried before Model access, or where existing Controllers can be reused as an alternative to implementing more View Helpers.

Layouts: Decorates the main output of a Zend_View instance with a Layout template composed of common page elements and Placeholders for context specific additions which are defined by templates prior to the Layout rendering. Layouts in Zend_View are implemented in a simple manner - they are the only feature requiring editing of Zend_View_Abstract.

Placeholders: Allows templates to set centrally registered content for inclusion in any subsequently rendered template or Layout. Placeholders are sufficiently decoupled from templates so that templates from different variable scopes can still communicate data to each other. Set Placeholders are only ever available to templates rendered after the Placeholder is set. Placeholders may be used for HTML elements which require a strict order of addition (e.g. Javascript <script> tags), or alternatively a specialised HTML generating Helper (e.g. Zend_View_Helper_HeadScript) can be used instead. See next.

HTML <head> View Helpers: Utilise Placeholders in a pre-defined namespace to dynamically control the construction of a Layout's <head> section. This eases the common use cases of using sub-templates to dynamically define additional Javascript, CSS, Metas, and other such elements for inclusion in a Layout.

6. Milestones / Tasks

  • Milestone 1: Write unit tests capturing the agreed interface and behaviour
  • Milestone 2: Write the code required to pass all unit tests
  • Milestone 3: Write integration tests to cover various edge cases
  • Milestone 4: Verify that code does not compromise backwards compatibility with ZF 1.0.0GA
  • Milestone 5: Complete documentation

7. Class Index

  • Zend_View_Abstract
  • Zend_View_Factory
  • Zend_View_Helper_Placeholder
  • Zend_View_Helper_Controller
  • Zend_View_Helper_Partial
  • Zend_View_Helper_HeadTitle
  • Zend_View_Helper_HeadScript
  • Zend_View_Helper_HeadLink
  • Zend_View_Helper_HeadMeta
  • Zend_View_Helper_HeadStyle
  • Zend_View_Helper_Doctype

8. Use Cases

UC-01

Partial Use Case

Partials render a template fragment at a specific location within another template. They are rendered in a separate variable scope with a Model supplied by the parent template. It is assumed Partials can therefore be easily rendered from different Modules.

Partials implement the Composite View Pattern defined in J2EE Design Patterns. Although template fragment rendering is likely the majority use case, partials are infinitely nestable and can thus compose a nested tree of Zend_View objects for above average complex Views.

Since the primary use case of partials is for use in loops, an additional helper, partialLoop, will be created. This will take the passed array and loop over it, passing each element to the partial view script requested.

./src/default/views/scripts/blog/index.phtml

./src/default/views/scripts/blog/_entry.phtml

./src/default/views/scripts/blog/index_alt.phtml

UC-02

Controller Use Case

The Controller View Helper enables templates to dispatch any given Controller and Action. The contents of the Response object resulting from this dispatch are then rendered in the calling template. Controller should never be used to alter the Model in this fashion - but are highly usable to enable the embedding of an existing Controller's output into an application.

Note: actions may either _forward() to another action or _redirect(). These are considered invalid responses, and result in an empty string being returned. Developers should be careful to only invoke actions that do not _forward() or _redirect().

./src/default/views/scripts/blog/index.phtml

UC-03

Placeholder Use Case

Placeholders are centrally registered pieces of content which all subsequently rendered templates may access. Since Layouts are always rendered last - they may access all set Placeholders. Placeholders may be set with a specific order attached. This is beyond the scope of the the following Use Case which does not differentiate between <head> descendant elements.

Placeholders can either hold scalars or collections. In the case of collections, placeholders provide a mechanism for ordering and sorting. In either case, a placeholder would allow specifying content with which to either prefix or postfix the placeholder content, and in the case of collections, separator content. They will implement __toString(), allowing them to be echo'd directly. Finally, they will also allow capturing template content into a placeholder.

./src/default/view/scripts/layout.phtml

./src/default/view/scripts/blog/index.phtml

9. Class Skeletons

Zend_View_Helper_Partial

Zend_View_Helper_PartialLoop

Zend_View_Helper_Controller

Zend_View_Helper_Placeholder

By allowing templates within the Composite View tree (i.e. nested render() calls, partials etc.) access a central object which all View objects may access as a central Registry, it becomes possible for nested templates, irrespective of nest depth, to influence their top-level decorating Layout, or subsequently rendered templates, by setting values these may later refer to.

All values are stored in array objects. This enables Placeholders to maintain an indexed set of values, where the order of inclusion at a rendering point is determined by this index. Because it implements ArrayObject, any PHP array sorting function may be used to sort the placeholder values.

This strategy is built into a set of a pre-defined Placeholders for the <head> element of HTML documents in the proposed Zend_View_Helper_Head* helpers. More similar helpers may be proposed if their utility proves valuable. Their purpose is primarily to offer specialised usage of the basic Placeholder class using Proxies which are capable of outputting valid XHTML/HTML using the values set by the template authors, and optionally specific to a defined Doctype.

Zend_View_Helper_HeadTitle

A small helper delegating to Zend_View_Helper_Placeholder which allows the user to utilise a default Placeholder for the express purpose of defining a page title for insertion into a Layout head element.

Zend_View_Helper_HeadMeta

A small helper delegating to Zend_View_Helper_Placeholder which allows the user to utilise a default Placeholder for the express purpose of defining page <meta> tags for insertion into a Layout head element.

Zend_View_Helper_HeadStyle

A small helper delegating to Zend_View_Helper_Placeholder which allows the user to utilise a default Placeholder for the express purpose of defining page <style> tags for insertion into a Layout head element.

Zend_View_Helper_HeadScript

Include a Script file, or code block with a <head> element. Can avail of indexation to ensure Scripts are included in a preferred order.

Zend_View_Helper_Link

Add a Link element based on an array of attributes to be added to the <head> section of a template or layout. Although this can also handle style sheets, it may prove more useful to use the HeadStyle View Helper to segregate styles from other types of relationship links.

Zend_View_Helper_Doctype

Obtain the Doctype string based on the Standard name for HTML, XHTML, MathML and SVG.

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jun 13, 2007

    <p>I've installed this (revision 73) and it seems to work with ZF 1.0 RC2. I have also read your blog series (up to part 6).</p>

    <p>I am a bit confused as to the variable scoping. For instance, how should a layout.phtml get a baseUrl property into scope? I find if I set it up in the controller's init() method it works, but as the layout is typically set in the bootstrap code and so globally in use, that means my layout.phtml would rely on all controllers it's used for to provide baseUrl to the view, and so with strictVars on, it breaks if a controller doesn't happen to have assigned a baseUrl.</p>

    <p>I should mention I'm a novice on ZF but I think this proposal could be very useful.</p>

    <p>Thanks in advance for any comments,</p>

    <p>Mark</p>

    1. Aug 20, 2007

      <p>Hi,</p>

      <p>I'm fairly new to some of this, but I'm sold on what Padraic has suggested....particularly the "placeholder" support. (Which, btw, Smarty has in a SmartyDoc plugin.) I'm excited about being able to move forward with ZF without having to bring in 3rd-party solutions all over the place.</p>

      <p>Anyway....my big question is, how do I do like you did and install this...?</p>

      <p>The code in the proposal is just stubbed out. How do I go about downloading/installing/using this...?</p>

      <p>Also, a couple questions:</p>
      <ul class="alternate">
      <li>Does anyone want to sound off as to how likely they think it is that Zend_View_Enhanced actually gets accepted and added to the core?</li>
      <li>Assuming it gets accepted, does anyone want to offer any ideas as to when it might be "release-ready"?</li>
      </ul>

      <p>Thanks in advance for any help/answers.</p>

      <p>Take care,</p>

      <p>Travis</p>

  2. Jun 14, 2007

    <p>It's a common problem (getting baseUrl). Instead of commenting on a solution I'll post a link to a mailing list post from earlier today with a useful View Helper you can use. The logic behind it is that the application already knows the baseUrl - you just need the interface to grab it:</p>

    <p><a class="external-link" href="http://www.nabble.com/Generating-relative-urls-for-images-tf3920769s16154.html#a11119053">http://www.nabble.com/Generating-relative-urls-for-images-tf3920769s16154.html#a11119053</a></p>

    <p>It's also worth noting - just to clarify it for others - that Layouts need not be global. Since they are bound to Zend_View instances you can use layouts on a site-wide, per-module, or even per-partial (in this case not as a tiny snippet template, but in its role as Composite View). I have not added this tidbit yet since it's not hugely important to the core idea. Something a possible Zend_View_Factory could enable by comsuming a config file.</p>

    <p>Bear in mind the sample code, while functional, is not fully integrated tested and subject to change in the event this proposal is accepted. <ac:emoticon ac:name="wink" /> I am continuing to work on it however as a preliminary implementation to encourage feedback.</p>

  3. Jun 14, 2007

    <p>I really like the ideas you have going on here. Keep up the good work! Also, I've been following your blog entries on this subject, which have been immensely useful.</p>

  4. Jun 18, 2007

    <p>At revision 79 I am getting:</p>

    <p>Fatal error: Call to undefined method Zend_View_Helper_Partial::getfactory() in /home/zf/zf_proposals/Zend_View_Enhanced/Zend/View/Helper/Partial.php on line 45</p>

    1. Jun 18, 2007

      <p>The fix seems to be to convert the failing line to this:</p>

      <p>$view = $this->view->getFactory()<span style="text-decoration: line-through;">>createInstance($module, $viewModel, $this</span>>view);</p>

      <p>But I've hit more possible bugs in trying out a partial in that it didn't have a moduledir defined in factory.php (shouldn't these settings be in camel caps for consistency?).</p>

      <p>Anyway, I'm trying to avoid setting paths all over the place with ZF, and instead working with a conventional modular layout using default dir names, so I would hope I could just set the module dir and the rest would be worked out by default in the controller side, but ZF 1.0 RC2 doesn't seem to be playing with this Proposal's code in propagating default paths.</p>

      <p>I also have a feature request. With complex views in general in the past, I have found myself doing View Source in the browser and trying to work out where on earth a dodgy bit of HTML originated. A simple solution is to put a hard coded comment like <!-- RENDERING: /home/zf/app/modules/default/views/scripts/index/index.phtml --> into the view files.</p>

      <p>But I it's a pain to remember to do that manually in umpteen files and risks going out of date so, how about conditionally having something like this in Zend_View_Abstract's render method?</p>

      <p>echo '<!-- RENDERING: ' . $this->_file . " -->\n";</p>

      <p>Then you get to know in your browser's View Source window exactly which file the HTML is coming from. Of course, it would need to be in a conditional to only do this inside HTML/XML output and also only when activated by an option, typically when in a development mode as you might not want to expose file paths during production. I had a go at attempting this but the problems mentioned earlier with partials left me a bit stuck to test it.</p>

      1. Jun 19, 2007

        <p>Reply to second comment from Mark:</p>

        <p>A lot of View Factory logic is now defined by Zend_Controller_Action_Helper_ViewRenderer. I'll be sticking very closely to this as a default in Zend_View_Factory so the Factory should (if not immediately) reflect this View convention. The previous edits make this more possible - just define a Module Directory (only one for now) and it should immediately create the conventional basePath for views based on a Partial's referenced Module name.</p>

        <p>Take a quite look (and run the unit tests to be sure I got it). Feedback appreciated <ac:emoticon ac:name="wink" />. Sorry for being a little behind in syncing my unit tests to the public repo. When the proposal is more mature I'll revisit your request and see where it can be added.</p>

        1. Jun 24, 2007

          <p>Hi Padraic. Could you put me back on track here?</p>

          <p>I'm getting:</p>

          <p>Hello, World!</p>

          <p>Fatal error: Uncaught exception 'Zend_View_Exception' with message 'no view script directory set; unable to determine location for view script' in /home/zf/zf_proposals/Zend_View_Enhanced/Zend/View/Abstract.php:1008 Stack trace: #0</p>

          <p>Here's the bootstrap I'm using:</p>

          <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
          <?php
          // customize PHP behaviour
          require '/home/zf/lib/Mm/Helper.php';
          $appDir = Mm_Helper::getAppDir();
          Mm_Helper::prependIncludePath($appDir. DIRECTORY_SEPARATOR . 'library');
          Mm_Helper::prependIncludePath($appDir. DIRECTORY_SEPARATOR . 'proposals');
          $configEnv = Mm_Helper::isProductionServerDetected() ? 'production' : 'staging';
          Mm_Helper::customizePhpErrorReporting($configEnv);

          // load application config
          require_once 'Zend/Config/Ini.php';
          $configFile = $appDir . Mm_Helper::portablePath('/config/config.ini');
          $config = new Zend_Config_Ini($configFile, $configEnv, true);

          // ensure versions requirements are being met
          Mm_Helper::checkVersionRequirements($config->versions);

          // set up registry
          require_once 'Zend/Registry.php';
          $registry = Zend_Registry::getInstance();
          $registry['appDir'] = $appDir;
          $registry['configEnv'] = $configEnv;
          $registry['config'] = $config;

          // set up view
          require_once 'Zend/View.php';
          $view = new Zend_View();
          require_once 'Zend/View/Factory.php';

          // config->view only contains settings for encoding, escape, and strictvars
          $view->setFactory(new Zend_View_Factory($registry['config']->view));

          // not sure I should need to do this for default file layout
          $view->setBasePath(Mm_Helper::portablePath($appDir . '/modules/default/views'));

          // add the path to layout.phtml
          $view->addScriptPath($appDir . Mm_Helper::portablePath('/modules/default/views/scripts'));

          // add path to my main library of helpers such has Base.php for the base URL
          $globalAppHelperDir = $appDir . Mm_Helper::portablePath('/library/Mm/View/Helper');
          $view->addHelperPath($globalAppHelperDir, 'Mm_View_Helper');

          $view->setLayout('layout.phtml');

          require_once 'Zend/Controller/Action/Helper/ViewRenderer.php';
          $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer;
          $viewRenderer->setView($view); // do I need to clone $view here?
          require_once 'Zend/Controller/Action/HelperBroker.php';
          Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

          // set up controller
          require_once 'Zend/Controller/Front.php';

          $front = Zend_Controller_Front::getInstance();

          if ('production' !== $configEnv) {
          $front->throwExceptions(true);
          }
          $front->returnResponse(true);
          $front->addModuleDirectory($appDir . DIRECTORY_SEPARATOR . 'modules');

          // set up request object
          if ('cli' == php_sapi_name()) {
          require_once 'Mm/Controller/Request/Cli.php';
          $uri = '/';
          if (count($GLOBALS['argv']) == 2)

          Unknown macro: { $uri = $GLOBALS['argv'][1]; }

          $uri = 'http://' . self::getAppName() . $uri;
          $request = new Mm_Controller_Request_Cli($uri);
          } else {
          require_once 'Zend/Controller/Request/Http.php';
          $request = new Zend_Controller_Request_Http();
          }
          $front->setRequest($request);

          // set up response object
          if ('cli' == php_sapi_name()) {
          require_once 'Zend/Controller/Response/Cli.php';
          $response = new Zend_Controller_Response_Cli();
          } else {
          require_once 'Zend/Controller/Response/Http.php';
          $response = new Zend_Controller_Response_Http();
          }
          $front->setResponse($response);

          if ('production' !== $configEnv) {
          $response->renderExceptions(true);
          }

          // run request
          $front->dispatch();

          // output results
          $response = $front->getResponse();
          $response->sendResponse(); // send final results to browser, including headers

          exit;
          ]]></ac:plain-text-body></ac:macro>

    2. Jun 19, 2007

      <p>Hi Mark. I'll revisit the source code soon. Just bear in mind the API is shifting around a bit particularly as regards the factory. Reference should be to Zend_View_Abstract::getFactory(). I've changed some of the related code immediately and added a Partial Unit Test to the mix to catch a re-occurrence.</p>

  5. Jun 18, 2007

    <p>Hi, <br />
    I've been following your posts on your blog and found them incredibly useful. I've actually managed to write my own view class that extends the default Zend_View, and implements partials and layout, although admittedly in a very hacky way (the partials at least).</p>

    <p>One thing I found with the layout part, from following the tutorial on your blog, is that I ran into a problem when wanting to declare a variable within the main content view, and then use that in the layout (eg. a value for the <title> tag). This is because in the example on your blog, the layout is rendered first, and then the content is rendered into the layout. I have modified this in my implementation so that the content is rendered first, saved into a variable ($this->_content) and then the layout is rendered, with a call to a $this->getContent(), which returns the already rendered content. Do you plan to implement such a system in this proposal?</p>

    <p>Another thing that I have implemented is a collection (I think I got the idea from RoR). Its bascially a wrapper for the $this->partial() method, but includes the foreach loop. You call it in the same way you call partial, only you give it and array of arrays, rather than one, and the partial in rendered once for each item in the "collection".</p>

    <p>I look forward to the development on this, advanced views are something ZF really needs.</p>

    1. Jun 18, 2007

      <p>The method for that collection thing is simply $this->collection().</p>

      <p>Also, something I've not implemented, but done before in a previous framework, is the idea of multiple layouts that wrap around each other (I actually called this "wrapper(s)" rather than layout(s)).</p>

      <p>The advantage of this is that you can reduce duplicating code that is common to all layouts. For example you might have your standard site layout, and a simple centred white layout for error messages or something, but they both use the same html/head/body tags.</p>

      <p>In this system you can have one template that just contains the html, head, title, link, ..., and body tags, and then two more templates that have the two different layouts. The main content is inserted into the layout, and then the layout is inserted into the "page". This can of course extend to as many "wrappers" as you like. What do you think?</p>

  6. Jun 19, 2007

    <p>Yep, the blog example from a few weeks back rendered the Layout first. The proposal changes the order to yours so that Placeholders can be rendered into the Layout, and the Layout is always rendered last. The implementation in subversion (see the References above) is the current one. It's also a bit "hackish" <ac:emoticon ac:name="wink" />, mainly because the API is still being tied down and there are small areas I haven't completely polished (like the Factory). I hope to polish it up in another week and increase the unit test coverage. Avoid any more problems folk like Mark are running into.</p>

    <p>If you have an implementation for Wrappers/Containers you're willing to part with you can post it here as a comment. Once the proposal is being reviewed the reviewers can assess it for inclusion in the proposal implementation. That's assuming the reviewers look kindly on Zend_View Enhanced <ac:emoticon ac:name="smile" />.</p>

  7. Jun 19, 2007

    <p>I think this is a really elegant solution to a problem that has been popping a lot lately. There's one little problem I can see. The appendPlaceholder method can be used to append things like javascript and css to the head section of the html in a layout. When appending javascript (and to some extent css) the order in which they appear in the head matters.</p>

    <p> I may be missing something here, but does your implementation provide control over the order in which appends happen?</p>

  8. Jun 22, 2007

    <p>Hi Maurice,</p>

    <p>I think the important part is that the Placeholders have no predefined names (there is no "HEAD" unless I create that Placeholder key). The use case is actually misleading in that way I think. What one can do is refer to three different Placeholders in the <head>, so CSS, JAVASCRIPT and HEAD (case here isn't important). I was discussing with Ralph a while ago over whether to use an array in the Placeholder registry so order could be defined. It would add a second level of possible references which could make Placeholders more helpful:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[<?php echo $this->getPlaceholder('JAVASCRIPT', 'ajax_search'); ?> // $registry->JAVASCRIPT->ajax_search]]></ac:plain-text-body></ac:macro>

    <p>A default behaviour could then possibly be added, so if the second level reference is missing the Placeholder will have array elements concatenated just as they would be now. The sub-keys can equally be integers over naming which would allow the concatenation order to be set:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$this->addPlaceholder('CSS', 0, '<link rel="stylesheet" href="/base.css" type="text/css" media="screen" />');
    $this->addPlaceholder('CSS', 1, '<link rel="stylesheet" href="/code.css" type="text/css" media="screen" />');]]></ac:plain-text-body></ac:macro>

    <p>Javascript would have been a better example of course <ac:emoticon ac:name="wink" />.</p>

    1. Jul 10, 2007

      <p>Hi Maurice,</p>

      <p>I've gotten some free time to elaborate on Placeholders. Placeholders now have a slightly more generic API and store values in an Array which allows for index setting. I've also added a method for allowing users to define a callback function to which Array ordering is deferred (current default is ksort() for now) for those needing more customisation where simple number/alphanumeric indexation isn't sufficient.</p>

      <p>To top this all off, the revised Placeholder has been given a number of Proxy helpers which zero in on generating markup for specific elements. For example, Zend_View_Helper_HeadScript will take a Javascript file path, and output the full <script> element for this, applying the sorting function (ksort() if not defined) to the output.</p>

      <p>I'm refactoring some of ZVE code this week to get a more robust implementation into Subversion. Hopefully this strikes your fancy - and feedback is appreciated as to changes or other improvements you'd like to see.</p>

  9. Jun 25, 2007

    <p>Does anyone else have this proposal's code testing out ok? I posted a reply to Padraic including my boostrap which is nested in a few comments above but fear it will easily get missed by Padraic given it looks like he's replied but in fact to an older question. Perhaps I should have posted it here at the bottom?</p>

    <p>How much interest is there in this proposal too? I read a fair bit of noise about other impending proposals and general complex view chatter but this actual proposal seems the most comprehensive to me so far.</p>

  10. Jun 25, 2007

    <p>Padraic, I think perhaps your advice was to setModuleDirectory() in View_Factory I was only doing it in the controller thinking DRY etc. But I may be wrong. I'd kind of like to delete my comments above really. Perhaps I should be posting this stuff on the mailing list to save adding confusion to this page. Please feel free to delete my comments if you think so too.</p>

  11. Jun 26, 2007

    <p>Yep, you can post any questions to the mailing list. I'm pretty sure I can't delete comments here but it's fair to say your comments demonstrate one of the main issues in implementing a Factory for views - so it has value remaining here.</p>

    <p>I'll be revisiting the Factory as soon as time permits - the reference code is incomplete in that area so I need to pull up my coding boots and get it done <ac:emoticon ac:name="wink" />.</p>

    <p>As for interest in the proposal - it seems relatively high. Ralph's alternate Controller based solution has a lot of interest also, going by activity on the #zftalk freenode channel on IRC. I also assume Ralph has complete code whereas I've been happy to let the proposal sit around for longer. Ralph should be posting a formal proposal in the near future and I expect it will borrow a few of the ideas from Zend_View Enhanced to up the ante <ac:emoticon ac:name="smile" />.</p>

  12. Jun 26, 2007

    <p>I kind of wince at these method names:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    partial()
    controller()
    contentForLayout()
    ]]></ac:plain-text-body></ac:macro>

    <p>Consider these instead:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    getPartial()
    dispatchController()
    getLayoutContent()
    ]]></ac:plain-text-body></ac:macro>

  13. Jun 27, 2007

    <p>Third I will move to, the first two I viewed more as verbs than nouns hence the naming... Mostly I just like keeping the first two as short as possible. </p>

    1. Jul 15, 2007

      <p>Could I suggest:-</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      renderPartial()
      renderController()
      ]]></ac:plain-text-body></ac:macro>

      <p>only because it gives a more accurate representation of what those two methods return.</p>

      <p>I wonder if changing the interface to render($name, Array $namespaceVars = array()) would be a better long-term solution, as it removes the need for the 'partial' distinction - why couldn't every view have its own namespaced variables anyway?</p>

      <p>I've been weighing up this and the Zend_Layout implementation. I prefer the fact that a layout is built into this View component, as they are so tightly integrated anyway.</p>

      <p>My hesitation is really only with the view helpers. I'm less keen on the method adding content and rendering. I'd rather see some more finessed methods built into the View object and being a derivative of View.</p>

      <p>E.g. Zend_View_Xhtml would remove the need for the docType helper, be able to control the behaviour of open/close tags and allow for manipulation of content in either the 'default' or 'body' section or the head (e.g. set, append, prepend, clear). It could also potentially identify namespace clashes for selecting document ID attributes.</p>

      <p>I think there may be a few more problems to think through that may remove the need for these helpers and perhaps pave the way instead for more specialised View objects.</p>

      1. Aug 07, 2007

        <p>The term 'partial' is pretty widely known – it's used in Rails and Symfony, and other projects as well. I personally feel that 'render' is implied. I dislike the idea of prefixing helpers with extra verbiage; in most cases, they should actually be <strong>nouns</strong>, as they are placeholders for extra content. See the various 'Form' view helpers already available for examples. partial() makes sense to me. controller() might need a better name, but the nice part about the current name is that it makes it clear that you're invoking an action in a controller; renderController() makes no sense at all, as you're not actually rendering anything; you're retrieving content generated by a controller action. And dispatchController() is just too damn long.</p>

        <p>As for every view having its own namespaced variables, several things come to mind:</p>
        <ul>
        <li>It would likely require a complete reworking of Zend_View (Zend_View currently does an 'include' on the view script from within a method, which is why $this contains all variables)</li>
        <li>How would you re-use a view object if every script has its own namespaced variables?</li>
        <li>If you don't use $this for namespaced variables, does that mean they're currently in scope – e.g., $foo, $bar? If so, it becomes much more difficult to determine where these are coming from – are they coming from a parent view script? or the view object?</li>
        </ul>

        1. Aug 08, 2007

          <p>That doesn't make sense to me at all Matthew - if that's true then even the 'render' method doesn't make sense since you're simply fetching content from an include. Using the 'render' prefix means that the developer will understand it's a behaviour similar to the default 'render'.</p>

          <p>Let me clarify what I mean about partials - there's no need for them if the opportunity to pass namespaced variables to each script exists. As you suggested, the variables would be passed in scope (so you'd refer to $foo rather than $this->foo) which would clarify the distinction. There's no difficulty in my mind about the 'source' of these variables - if a 'partial' view is created by a developer then they'll surely know that they need to pass variables to that script to have it work as expected.</p>

          <p>BTW, what did you think about end-use-specific classes for Zend_View_Xhtml?</p>

  14. Jul 11, 2007

    <p>A couple comments based on <a href="http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/View">the code from your SVN repo</a>:</p>
    <ol>
    <li><a href="http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/View/Helper/HeadTitle.php">Zend_View_Helper_HeadTitle</a>::setSuffix() would make a nice complement to setPrefix()</li>
    <li>What's the purpose of the $parentView parameter in <a href="http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/View/Factory.php">Zend_View_Factory</a>::createInstance()? It doesn't seem to be doing anything.</li>
    </ol>

  15. Jul 11, 2007

    <p>Hi Aaron,</p>

    <p>I'll look into adding a Suffix method whenever I escape the really weak point in the current SVN code - Zend_View_Factory. Which kind of leads into your second question!</p>

    <p>The parentView param was originally used to impose an inheritance path for implementing the Composite View (ref. "Core J2EE Patterns"). It's likely to be removed soon - the current partial() helper allows the passing of declared parameters rather than relying on any form of View Model inheritance so its almost a deprecated parameter. Hopefully I'll get around to patching Zend_View_Factory tomorrow as I spruce up the ZVE code.</p>

  16. Jul 20, 2007

    <p>I really like where this is going.</p>

    <p>One thing that would be nice however, would be the ability to set certain placeholders from within the view script itself. For instance, javascript. When you are working within a nested view, you might have javascript that is specific to that view only, but you want it in the HEAD tags.</p>

    <p>The clear answer would be to use a placeholder method to set it from within the controller or from within the view. The problem is that you need to pass it as a string, and having to do work scripting or a bunch of HTML into a string is non-intuitive. What about something like the following from within the view script?</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    <?php $this->headScriptStart() ?>

    <script language="javascript'>
    function someBigFunction()

    Unknown macro: { // lots of stuff that I don't want to put in a giant string. }

    </script>

    <?php $this->headScriptEnd() ?>
    ]]></ac:plain-text-body></ac:macro>

    <p>The above would catch the output between the start and the end and add it to the stack of stuff that goes in <HEAD>. This would also be a lot more user friendly in a PHP IDE as you would be dealing with regular PHP code in between the functions.</p>

    1. Jul 24, 2007

      <p>I read your comment and while i was playing with ZVE I figured that it was a perfect case to learn a little about Helpers. Since I typically need to be able to put code in both the header and footer this handles "blocks" of code in a similar way to placeholders(). </p>

      <p>What might be interesting is adding ob_start() .. ob_get_contents() .. style placeholders to the ZVE proposal, thus removing the need for this.</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $this->placeholder()->capture('scriptFooter');
      // script code here...
      $this->placeholder()->captureStop();
      ]]></ac:plain-text-body></ac:macro>

      <p>Until that point, here's a Script Helper that should handle things.</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      class Zend_View_Helper_Script {
      public $view = null;
      private $pos = 'HEAD';
      private $scripts = array();

      public function script()

      Unknown macro: { return $this; }

      public function get($pos = 'HEAD') {
      if (isset($this->scripts[$pos]))

      Unknown macro: { return join("n", $this->scripts[$pos]); }

      return "";
      }

      public function has($pos = 'HEAD')

      Unknown macro: { return isset($this->scripts[$pos]); }

      public function start($pos = 'HEAD')

      Unknown macro: { $this->pos = $pos; ob_start(); }

      public function end()

      Unknown macro: { $this->scripts[$this->pos][] = ob_get_clean(); }

      /**

      • Set view object
        *
      • @param Zend_View_Interface $view
      • @return Zend_View_Helper_Partial
        */
        public function setView(Zend_View_Interface $view)
        Unknown macro: { $this->view = $view; return $this; }

      /**

      • Clone the current View within resorting to a Factory call
        *
      • @param Zend_View_Interface $view
      • @return Zend_View_Helper_Partial
        */
        protected function _cloneView(array $viewModel = null) {
        $view = clone $this->view;
        $view->clearVars();
        if (!is_null($viewModel))
        Unknown macro: { foreach($viewModel as $key->$value)
        Unknown macro: { $view->$key = $value; }
        }

        return $view;
        }
        }
        ]]></ac:plain-text-body></ac:macro>

      1. Sep 03, 2007

        <p>Please note that the revised version of the placeholder helper now has support for capturing content. <ac:emoticon ac:name="smile" /></p>

  17. Aug 29, 2007

    <p>Hi Pádraic,</p>

    <p>An excellent addition to the framework in my opinion. I stumbled upon one of your blog posts just as I was getting stuck in to the Zend Framework. I mistakenly assumed your proposal to already be a part of the latest stable. Imagine my disappointment when I discovered it wasn't.</p>

    <p>Having used a number of other frameworks I notice many of the ideas you have proposed are already commonly in use (they are accepted patterns after all.)</p>

    <p>My only reservation with your implementation is with your placeholders and specifically withthe html HEAD section helpers.</p>

    <p>Have you considered using an abstract class from which these all derive their base functionality? Essentially a "formatted placeholder abstract class". This would allow others to define their own similar classes for storing blocks of output that are rendered in parent views.</p>

    <p>Perhaps I could extend that class to build a FooterDebugInfo helper for instance. I continually append debug info to this and spit it all out in a format I like at the bottom of the page? (Bad example but I hope you are getting my drift.)</p>

    <p>This also allows developers to impment any number of their own project specific namespaces for output.</p>

    <p>I will have a look at the code in a little more detail and send you a proposal of my idea.</p>

    <p>Final, small detail: Error on line 202 of View/Helper/HeadScript.php</p>

    1. Sep 03, 2007

      <p>In reviewing the proposal, my recommendation to Paddy is exactly that: each of the head*() helpers should extend or wrap the Placeholder helper in order to perform their functionality; they would simply have their own placeholder containers and/or namespaces.</p>

  18. Sep 07, 2007

    <p>Hello,</p>

    <p>I'm trying to use headLink() and headScript() in a partial, and having no luck. It's as though those additions are ignored. Is this supported? Do I need to "pass" something through to the partial in order to get things to work out properly?</p>

    <p>Also, is there a place to report bugs? In addition to the error noted a couple of posts back, there's another error in the same method: javascript is listed twice.</p>

    <p>Thanks for the time and the continued improvements.</p>

    <p>Travis</p>

  19. Sep 26, 2007

    <p>I tried layouts examples from this articles with classes from here <a class="external-link" href="http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/View">http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/View</a> <br />
    Some errors in it: <br />
    Warning: Missing argument 2 for Zend_View_Helper_Placeholder::set(), called in u:\home\zf.my\www\application\admin\views\scripts\index\index.phtml on line 3 and defined in u:\home\zf.my\www\library\Zend\View\Helper\Placeholder.php on line 112<br />
    Notice: Undefined variable: value in u:\home\zf.my\www\library\Zend\View\Helper\Placeholder.php on line 120<br />
    Warning: Invalid argument supplied for foreach() in u:\home\zf.my\www\application\admin\views\scripts\index\index.phtml on line 6<br />
    Fatal error: Call to undefined method Zend_View_Helper_Placeholder::captureStart() in u:\home\zf.my\www\application\admin\views\scripts\index\index.phtml on line 11</p>

    <p>Can you update svn or examples to work together?</p>

    1. Sep 27, 2007

      <p>Hi Ravil,</p>

      <p>Please be aware the svn code is only intended as an experimental example and it has known issues which may require hand editing. It has served its purpose to demonstrate what actual production code could be implemented.</p>

      <p>Pending acceptance (which is certain) you will see fully tested and supporting code being committed to the official ZF repository in the incubator.</p>

      <p>If you have any questions regarding official support - I would suggest emailing the mailing lists. But in the meantime I am not able to contribute time to supporting the experimental code - sorry, but I have a full day so it's not possible.</p>

      <p>Hope you understand,<br />
      Paddy</p>

  20. Sep 28, 2007

    <p>Sorry in advance for the long comment; I'll try to format it such that it should be more legible:</p>

    <p>I posted <a href="http://framework.zend.com/issues/browse/ZF-1545#action_16999">a comment (2007-09-27)</a> for <a href="http://framework.zend.com/issues/browse/ZF-1545">ZF-1545 - Allow setting of arbitrary Request/Response objects on ViewRenderer</a>.</p>

    <p>In the requirements section, I read:</p>
    <blockquote><p>Must (where feasible) be implemented as default View Helpers</p></blockquote>
    <p>What does this mean - to be a default View helper?</p>

    <p>Zend_View_Factory</p>
    <ul>
    <li>Where is the class skeleton, or is this not needed any more?</li>
    </ul>

    <p>Zend_View_Helper_PartialLoop</p>
    <ul>
    <li><code>$item</code> is not used and/or <code>$model</code> is iterated but passed in its entirety?</li>
    </ul>

    <p>Zend_View_Helper_Placeholder_Container</p>
    <ul>
    <li>Consider <code>get/setSuffix()</code> instead of <code>get/setPostfix()</code>?</li>
    <li>Consider <code>get/setInfix()</code> or <code>get/setGlue()</code> instead of <code>get/setSeparator()</code>?</li>
    </ul>

    <p>Ensure that all <code>set*()</code> methods are fluent where they currently return void (e.g., <code>Zend_View_Helper_Placeholder::setView()</code>)</p>

    <p>Zend_View_Helper_Head*</p>
    <ul>
    <li><code>Zend_View_Helper_HeadTitle::HEADTITLE_NAMESPACE</code> could instead be <code>NAMESPACE</code>?</li>
    <li>Explain why values such as <code>"ZEND_HEAD_TITLE"</code> make sense for the namespace constants?</li>
    <li>Above two items may apply to other <code>Head*</code> helpers, too</li>
    <li>All <code>Head*</code> helpers should extend a common [abstract] class (Placeholder?)</li>
    <li>Zend_View_Helper_HeadStyle
    <ul>
    <li>How does this helper actually insert styles? (e.g., @import, <link />). The proposal mentions defining <style> tags, which may be misleading unless it actually adds <style> tags.</li>
    </ul>
    </li>
    </ul>

    <p>Many of these may simply be a matter of synchronizing the proposal with the current code; I have not examined the latter as of yet.</p>

    1. Sep 28, 2007

      <p>Darby, to answer your questions:</p>

      <ul>
      <li>Zend_View_Factory will not be part of this proposal.</li>
      <li>partialLoop will accept a single variable to loop over. If $module is a string, it will look for the partial script in that module's view scripts; otherwise, if an array, that will be used for the $model value.</li>
      <li>Placeholder_Container: postFix is correct in this case, as we don't want to confuse it with a view script filename suffix. I'll discuss with Ralph and Paddy regarding setInfix/setGlue as alternatives to setSeparator.</li>
      <li>I'll encourage fluent interfaces for all setters</li>
      <li>Zend_View_Helper_Head:
      <ul>
      <li>Yes, just NAMESPACE would be sufficent as the constant name</li>
      <li>As for the namespace constants, these need to be named uniquely, and using the class title makes documenting reserved namespaces easier.</li>
      <li>All Head* helpers will definitely extend a common abstract class</li>
      <li>HeadStyle: there will be two different helpers, one for setting a stylesheet link, another for setting inline styles. headLink() would be for setting a stylesheet link, headStyle for inline styles. It will surround the CSS provided to the helper with <style></style> tags.</li>
      </ul>
      </li>
      </ul>

  21. 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 accepted for immediate inclusion in the incubator, with the following requirements:</p>

    <ul>
    <li>The 'controller' helper should be renamed 'action' to better indicate that it will execute a single action, and to prevent confusion with controllers.</li>
    <li>The 'placeholder' helper will not be a persistent container across view object instances (though it will persist between view scripts rendered with the same object). Persistence of the view object will be encouraged instead, via means such as Zend_Layout and/or the ViewRenderer.</li>
    <li>Zend_View_Factory will not be part of this proposal</li>
    <li>Fluent interfaces should be used for all setters</li>
    <li>All 'head*' helpers should extend a common abstract helper class.</li>
    </ul>
    </ac:rich-text-body></ac:macro>

  22. Sep 28, 2007

    <p>In partialLoop shouldn't this:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $content .= $this->partial($name, $module, $model);
    ]]></ac:plain-text-body></ac:macro>
    <p>be</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $content .= $this->partial($name, $module, $item);
    ]]></ac:plain-text-body></ac:macro>
    <p>?</p>

    <p>Also, it would be useful to know the current index from within the partial, as well as the total number of items. This would be required by anyone wanting alter behaviour based on the item position e.g. alternate rows, special conditions on first/last item, e.t.c.</p>

  23. Oct 25, 2007

    <p>Last weekend i checked out the incubator repos because I need to use a partial and headScript helpers. But headScript helper did not work so I made some changes;</p>

    <p><strong>C:\www\ZendFramework\trunk\incubator\library\Zend\View\Helper\Placeholder\Container\Abstract.php</strong></p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    @@ -90,7 +90,7 @@
    */
    public function __construct()

    Unknown macro: {- parent}

    /**
    @@ -263,7 +263,8 @@
    */
    public function nextIndex()

    Unknown macro: {- return $nextIndex = max(array_keys($this->_items)) + 1;+ $keys = array_keys($this->_items);+ return ((count($keys) > 0)? max($keys) }

    /**
    ]]></ac:plain-text-body></ac:macro>
    <p><strong>C:\www\ZendFramework\trunk\incubator\library\Zend\View\Helper\HeadScript.php</strong></p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    @@ -27,7 +27,7 @@

    public function append($sourceFile, $type = 'text/javascript')

    Unknown macro: {- $this->_offsetSet($this->nextIndex(), $script, $type, self}

    @@ -39,13 +39,13 @@

    public function offsetSet($index, $sourceFile, $typeOrAttrs = 'text/javascript')

    Unknown macro: {- $this->_offsetSet($index, $script, $typeOrAttrs, self}

    public function offsetSetScript($index, $script, $typeOrAttrs = 'text/javascript')

    Unknown macro: {- $this->_offsetSet($index, $sourceFile, $typeOrAttrs, self}

    @@ -56,7 +56,7 @@
    'mode' => $mode
    );

    • if (is_array($type))
      Unknown macro: {+ if (is_array($typeOrAttrs))
      Unknown macro: { $valueArray = array_merge($typeOrAttrs, $valueArray); }
      else
      Unknown macro: { $valueArray['type'] = (string) $typeOrAttrs;@@ -85,19 +85,19 @@ break; case self}
      }

    // make sure this method of this object is not publically available.

    • protected function exchangeArray($input)
      + public function exchangeArray($input)
      Unknown macro: { return parent}
    • public function toString()
      + public function toString($indent = null)
      {
      $useCdata = false;

    @@ -107,7 +107,7 @@

    $output = '';

    • foreach ($this->_scripts as $script) {
      + foreach ($this->_items as $script)
      Unknown macro: { $output .= '<script '; @@ -124,9 +124,14 @@ $output .= $name . '="' . $attr . '" '; }

    + if($mode == self::FILE)

    Unknown macro: {+ $baseUrl = rtrim(Zend_Controller_Front}

    +
    $output = rtrim($output) . '>';

    • if ($content) {
      + if ($content && $mode == self::SCRIPT) {
      if ($useCdata)
      Unknown macro: { $output .= PHP_EOL . $this->_indent . '//<![CDATA[' . PHP_EOL . $this->_indent . $content . PHP_EOL . $this->_indent . '//]]]]><![CDATA[>' . PHP_EOL . $this->_indent; }

      else {
      ]]></ac:plain-text-body></ac:macro>

  24. Nov 17, 2007

    <p>Zend_View Enhanced and Zend_Layout are excellent ideas. Now I can get almost everything for my View part in real web development.</p>

    <p>The most needed feature in View part is inclusion of dynamic components on page. For that two methods are suggested:</p>

    <p>1) Calling model directly from View in read only fashion and get the data to display.<br />
    2) Using Controller Helper which dispatches desired action and returns output of that action.</p>

    <p>Now please consider this case, I have a blog page. In side bar, I want to display some information about the owner of the blog. (Which is obviously dynamic.)</p>

    <p>Here, I don't prefer the first way as in future I may put access restrictions to some of the information. (Actully for any case, I think the controller must be an agent for all inputs and outputs. We should not bypass it at any way to maintain real MVC architecture.)</p>

    <p>According to the second way I have to call view action in profile controller of user module. For a while I ignore about the slower execution. But another problem here is, I will get the whole profile page. I need a tiny block which displays few information about the blog owner. So it is just a component not a whole page. Making a separate action for this task is also not preferable as we do not want a separate URL for this task.</p>

    <p>This is just an example, there may be many cases like this.</p>

    <p>So, along with Controller Actions there must be a separate provision for this type of components.</p>

    <p>In Zend Framework, Action methods are dispatched according to Http Request object. As far as I understand, the dynamic components in a page do not require Http Request but still they must be a part of controller logic.</p>

    <p>Can you please suggest me a way?</p>

  25. Jan 09, 2008

    <p>In the documentation, the example 38.8 (partialLoop) says:</p>

    <p><? // partialLoop.phtml ?><br />
    <dt><?= $key ?></dt><br />
    <dd><?= $value ?></dd></p>

    <p>Should it be instead:</p>

    <p><? // partialLoop.phtml ?><br />
    <dt><?= $this->key ?></dt><br />
    <dd><?= $this->value ?></dd></p>

    <p>I get the undefined variable notice</p>