LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Tuesday, March 18. 2008

Using Zend_View Placeholders to Your Advantage

Somebody asked for some examples of how I use the headLink(), headScript(), and other placeholder helpers, so I thought I'd take a crack at that today.

First off, let's look at what these helpers do. Each are concrete instances of a placeholder. In Zend Framework, placeholders are used for a number of purposes:

  • Doctype awareness
  • Aggregation and formatting of aggregated content
  • Capturing content
  • Persistence of content between view scripts and layout scripts

Let's look at these in detail.

Doctype Hinting

The HTML specification encourages you to use a DocType declaration in your HTML documents -- and XHTML actually requires one. Simply put, the DocType helps tell your browser what is considered valid syntax, as well as provides some hints to how it should render.

Now, if you're like me, these are a pain to remember; the syntax is somewhat arcane, very long, and not something I want to type very often. Fortunately, the new doctype() helper allows you to use mnemonics such as 'XHTML1_TRANSITIONAL' or 'HTML4_STRICT' to invoke the appropriate doctype:


<?= $this->doctype('XHTML1_TRANSITIONAL') ?>
 

However, a doctype isn't just a hint to the browser; it's a contract that you need to follow. If you select a particular doctype, you're agreeing to write markup that follows the specification for it.

The doctype() helper is actually used internally in many of the placeholder helpers (as well as the form*() helpers) to ensure that the markup they generate -- if any -- adheres the the given doctype. However, for this to work, you need to specify your doctype early. I recommend doing it either in your bootstrap or in a plugin that runs before any output is emitted; typically, I will pull the view from the ViewRenderer in order to do so:


$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->initView();
$viewRenderer->view->doctype('XHTML1_TRANSITIONAL');
 

Since this sets the doctype helper's state, you can then simply echo the return value of the doctype helper later in your layout script:


<?= $this->doctype() ?>
 

Content Aggregation

Placeholders aggregate and store content across view instances. By aggregate, I mean that they store the data provided in an ArrayObject, allowing you to collect related data for later display. Since placeholders imlement __toString(), and can be collections, we've added accessors to allow you to set arbitrary text to prefix, append, and separate the items in the collection. The various concrete placeholders -- primarily the head*() helpers -- make use of this particular feature, storing each entry as a separate item in the collection, and then decorating them when called on to render.

Additionally, the concrete instances each contain some custom logic. In the case of headLink() and headScript helpers, we perform checks to ensure that when specifying files, duplicate entries are ignored. Why is this a good idea? Well, since you can _forward() to other actions, or even call the action() view helper, you could potentially have multiple view scripts loading the same stylesheets or javascript; we help protect against such situations.

So, as an example:


<? // /foo/bar view script: ?>
<?
$this->headLink()->appendStylesheet('/css/foo.css');
$this->headScript()->appendFile('/js/foo.js');
echo $this->action('baz', 'foo');
?>

<? // /foo/baz view script; ?>
<?
$this->headLink()->appendStylesheet('/css/foo.css');
$this->headScript()->appendFile('/js/foo.js');
?>
FOO BAZ!
 

It's a contrived example, for sure, but it shows the problem quite well: if two view scripts are rendered during creation of the same content, then you have the potential for duplicate content in your placeholders. However, in this case, the duplicate content will not occur, as the helpers detect the duplicate entries when they're added, and skip them.

Capturing Content

One way in which placeholders aggregate content is by capturing content. The base placeholder class defines both a captureStart() and captureEnd() method, allowing you to create content in your view scripts that you then capture for use later.

This is particularly useful for the headScript() helper, as it allows you to create javascript directly in your view that will be executed in the HTML head (or, if you use the inlineScript()) helper, you can have it executed at the end of your document, which is what Y!Slow recommends). The same goes for the headStyle() helper; you can define custom stylesheets to include directly in your document directly with the view that needs them.

As an example, Dojo ships with some custom stylesheets for rendering its various widgits, and also has the ability to load custom classes and widgets dynamically. Let's say we want to present a Dojo ComboBox in our page: we'll need a couple of stylesheets, as well as a few Dojo resources:

First, let's tackle the stylesheets:


<? $this->headStyle()->captureStart() ?>
@import "/js/dijit/themes/tundra/tundra.css";
@import "/js/dojo/resources/dojo.css";
<? $this->headStyle()->captureEnd() ?>
 

These are now aggregated in our headStyle() view helper, and we can render them later; they will not appear inline in the page as they do here in the view script.

Now, let's tackle the javascript. We need to load the main dojo.js file as a script, and then create an inline script to load our various widgets. Dojo often uses its own custom HTML attributes, and the head*() helpers typically don't like this (they like to stick to those attributes defined in the specs), so we'll need to tell the helper that this is okay so that Dojo will parse the page when it finishes loading (to decorate our widget with the appropriate, requested functionality).


<? $this->headScript()
        ->setAllowArbitraryAttributes(true)
        ->appendFile('/js/dojo/dojo.js', 'text/javascript', array('djConfig' => 'parseOnLoad: true'))
        ->captureStart() ?>
djConfig.usePlainJson=true;
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
<? $this->headScript()->captureEnd() ?>
 

What's the benefit to doing this? It allows you to keep the JS and CSS functionality that's related to the specific view script at hand with that view script -- you have everything in one place. If you need to change what JS or CSS is loaded, or modify the inline JS you're going to utilize, you can find it with the rest of the content to which it applies.

Putting it Together: the Layout

;

I keep talking about "when you render it later" in this narrative. "Later" refers to your layout script. I'm not going to go into how you initialize or define your layouts here, as it's been covered in other places. However, let's look at how we can pull in our doctype and head helpers into our layout:


<?= $this->doctype() ?>
<html>
    <head>
        <? // headTitle() is another concrete placeholder ?>
        <?= $this->headLink() ?>
        <?= $this->headStyle() ?>
        <?= $this->headScript() ?>
    </head>
    ...
 

Sure, you may want to put more in there than that -- if you have stylesheets or scripts that load on every page, you may want to define them statically in the layout... in addition to calling the placeholder helpers. But adding the placeholder helpers gives you some definite benefits: increased separation of code, more maintainable code (as the CSS and JS specific to a view is kept with the view), simpler logic in your layouts, and the ability to prevent duplicate file inclusions.

All this functionality is now standard with Zend Framework 1.5.0; if you haven't given it a try, download it today.

Note: my colleague, Ralph Schindler -- the original proposal author of Zend_Layout and a substantial contributor to the various placeholder helpers -- is giving a webinar on Zend_Layout and Zend_View tomorrow, 18 March 2008; if you're interested in this topic, you should check it out.

Updated: fixed links to layout articles.

Posted by Matthew Weier O'Phinney in PHP at 17:13 | Comments (3) | Trackbacks (0)
Defined tags for this entry: mvc, php, zend framework
Related entries by tags:
Zend Framework 1.7.0 Released
Vimgrep and Vim Project
Pastebin app updates
ZendCon08 Wrapup
Setting up your Zend_Test test suites

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

Great work, and many thanks.

P.S.
The links to other and places under the title "Putting it Together: the Layout" point to and
#1 James Dempster (Link) on 2008-03-18 20:44 (Reply)
&gt;my place&lt; &gt;rob's tutorial&lt;
#1.1 James Dempster (Link) on 2008-03-18 20:46 (Reply)
Oops -- updated those links now. :-)
#1.2 Matthew Weier O'Phinney (Link) on 2008-03-18 21:22 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back November '08 Forward
Mon Tue Wed Thu Fri Sat Sun
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

November 2008
October 2008
September 2008
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml dojo
xml dpc08
xml file_fortune
xml linux
xml mvc
xml oop
xml pear
xml personal
xml php
xml phpworks08
xml programming
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net