Tutorials

Zend Framework Blog Application Tutorial – Part 9: Exploring Zend_View and Displaying Blog Entries

Oct 28, 2008 insic 16 Comments

With the release of Zend Framework 1.5, the Zend_View component received a long overdue boost in its functionality. It is now no longer a simple template engine, but a powerhouse of features intent on making the dynamic generation of a View as simple and as flexible as possible. This update introduced a few new concepts along with existing ones which form the backbone of the changes. The main ones are as follows:

1. View Helpers
2. Layouts
3. Partials
4. Placeholders

We’ve already met Layouts as represented independently from Zend_View by Zend_Layout. The purpose of Layouts is to setup the HTML which is common to all Views of the current Module, i.e. headers, footers, sidebars, etc. Specific templates can then be rendered, have their output strategically inserted in the Layout body, and the final result send to the client browser. I haven’t gone into huge detail on its use other than to setup global Layouts which is its simplest and most common use. But some digging will reveal more of its functionality, for example, chaining action calls to aggregate output.

View Helpers we’ve also touched on briefly. They are classes which attach new callable methods to Zend_View and were introduced to allow presentation logic to be segregated for reuse in a separate class. For example, Zend_Form uses View Helpers to generate a form by calling on individual helpers such as Zend_View_Helper_FormTextarea, etc. There are also View Helpers for tasks as specific as URL generation, form elements and HTML lists. You can even write your own, and indeed it’s encouraged to keep your templates clear of duplicated code. The next two items on our list are introduced separately, but they are themselves View Helpers with more far reaching uses.

Partials were designed to offer the same benefit of reusable presentation logic that View Helpers offer, only this time for template code itself. For example, our index page involves displaying a list of blog entries. Each entry’s HTML is essentially identical except for the content injected into it. So it would be a good idea for us to create a template for one single entry, add this template as a reusable Partial, and simply render one in sequence for each entry we want to display. The same idea is adaptable for lists, menus, and more.

Then, instead of lots of templates with Entry markup, there is only one! Other templates can simply include it, render it from iterated data, etc. But the biggest selling point is variable scope – a Partial is completely blind to any data other than the data you deliberately give it. This trumps using includes and render() since it significantly reduces surrounding code and removes any chance of variable name clashing in a large application. In fact there’s a subclass called the PartialLoop helper which makes looped rendering of Partials incredibly easy.

Placeholders were conceived for a slightly different reason. The problem with having Layouts, templates, Partials, and View Helpers is that one View can be built up using a lot of different parts, which are unable to communicate with each other without introducing some custom wiring. Placeholders ensure that such wiring is standardised – if you have an entry template which outputs content and needs to change the title of the page, it can use a Placeholder to set the title value, and then you can add a sprinkling of code in your Layout to read that value and inject it into the Layout’s

We’ll see these facets of Zend_View in action as we continue.

Step 1: Standardising Output as XHTML 1.0 Strict and UTF-8

In previous parts we’ve been using View Helpers without even noticing it. Zend_Form doesn’t generate forms by itself, rather it delegates most of the HTML generation to a set of View Helpers like Zend_View_Helper_Form. The problem with such output, is that View Helpers can only generate XHTML 1.0 Strict output if we actually inform them of the standard to use. Since we haven’t yet done that, they have all defaulted to using HTML 4.01 Transitional. For example, all our forms are not XHTML 1.0 Strict compliant because input tags are not closed.

What we should do, is make the Doctype of our View more dynamic. This is achievable by using the Doctype View Helper. Now, since we’re using a View Helper which sets a value accessible globally thoughout our View, we can safely assume (correctly) that the Doctype helper is actually a specialised Placeholder. If there is any Placeholder factoid to remember, it is that a Placeholder value is only available for templates rendered subsequent to the setting template. More importantly, the final template ever rendered is always the current Layout.

Because of this, the best place to set Placeholder defaults for later use in other templates, is as early as possible. Either the Bootstrap, or an initialisation script. Bear in mind that a Layout is the last template rendered, and any Placeholder data set there is never known to other preceding templates, and not even to Zend_View itself, since they get rendered before the Layout is reached.

What we should also do is remove the post-correction of our Response object. This was fine at the time, but if we eventually intend outputting RSS or JSON, then we must not set a Content-Type of text/html at the last minute! Instead we’ll create a new default Response object before any Controllers are dispatched – so later changes to headers can overwrite this default entirely. This is a far cleaner solution.

Note: The Zend Framework’s Zend_Controller_Response_Abstract class does not set a default Content-Type header. Instead the default is system driven, generated automatically by PHP based on the php.ini values of default_mimetype and default_charset. You can change these values instead of using a Response object to set a default Content-Type header, however since configuring php.ini is not always possible or desirable, it’s still makes sense to do it internal to the application.

Note: A recent interesting observation, if only remotely related, is that setting a character encoding of UTF-8 for Zend_View, while currently required (see Bootstrap), appears to have been an unintentional oversight. The default Zend_View encoding is intended to be UTF-8, in line with most other components, and an issue has been submitted to correct this. This should have no impact on existing applications since IS0-8849-1 is incorporated as a subset of UTF-8.

Here’s our revised version of /application/Bootstrap.php where the setupView() method now sets the default Doctype, and setupFrontController() now sets a default Response object with a preset Content-Type header, rather than overriding it at the last moment.

What we should also do is amend our two Layout templates so that the Doctype they output is now based on the preset default. It’s interesting to recall what’s happening. We set the Doctype on Zend_View using the doctype() method which proxies to Zend_View_Helper_Doctype. This is then used by other View Helper to generate valid output for that Doctype. Now we can use it again to add the Doctype to our Layout (rather than explicitly typing it in).

Make the following change to the start of both /application/views/layouts/common.phtml and /application/admin/views/layouts/admin.phtml:

With the new changes, re-examine the output of your forms and you’ll discover input elements are now self-closing, as required for XHTML 1.0 Strict.

Step 2: Retrieving Blog Entries from the Database

Let’s get back to the task of displaying blog entries. The first task is to get what entries we intend displaying. We’re going to commence with the index page (remembering there will also be a different page and URL for showing just one entry). The index page should show a specified number of entries. I’ve selected 10 as a default. Note that I have not added a means of configuring this (or the blog title, or the encoding, or…) which is a concern for another day ;-). I’m currently displaying just 4 current entries on my own blog since it reduces the size of the page and the processing needed for output.

Typically you have two options for retrieving anything from a database for display. You can use the Controller to query the Model and pass the data to the View, or you can implement a View Helper which can do the same thing skipping the Controller altogether (only in that case it must be limited to read only access – Views must never write data to a Model). I’ll stick with the Controller focused approach, bearing in mind I actually prefer the View Helper method since the best Controller is a Thin Controller where code is minimized in favor of other more specific external classes.

Here’s an update to /application/controllers/IndexController.php:

It’s reasonably straightforward. But stop. Think a little carefully. Question number one you should aways ask when adding code to a Controller is whether or not it’s reusable. In our case, I’d bet it is. Our RSS feed would need a specified number of entries, our index page needs a specified number of entries, our Atom feed would… So let’s step back, and instead modify the relevant Model at /application/models/Entries.php:

The code is now reusable. I should note we are retrieving records ordered by date by default. Entries during the normal course of editing/drafting will have ids whose order does not necessarily relate to a publish date.

Now let’s amend our IndexController class.

Our Controller is now a lot thinner! If we took my preferential route of allowing the View to query the Entries Model, the Controller wouldn’t even have code ;-). You should also note, that we are not doing anything to the retrieved rows (encapsulated in an instance of Zend_Db_Table_Rowset) such as casting to an array. Zend_View can handle objects which implement the SPL Traversable interface, or which alternatively can be cast to an array (e.g. has a toArray() method), and not just arrays so there’s little point to it other than burning some additional RAM.

Note: At the time of writing, the Partial Loop View Helper doesn’t correctly use objects such as Zend_Db_Table_Rowset so internally the result object is still converted to an array. I’ll amend this entry the moment a fix is in place but for now Partials will have variables whose names equate to field names from the database whereas if handled correctly you could use setObjectKey() on the helper and have the object assigned to that variable name inside the Partial. This is really handy if you want Partials to also access the object’s methods.

Step 3: Creating A Dynamic Index View for Blog Entries

We have the data, now we need to go assemble the matching View. The index page, to a large extent, is a listing of entries. Each entry has the same basic HTML markup which is a sign of potential reuse.

As described earlier, reuse of template markup is the domain of the Partial. A Partial is a template which can be rendered into any other template. You can have differing uses for Partials, such as rendering a single Partial, or rendering the same Partial many times inserting new data across a loop. This is not the same as using nested includes or render() calls, Partials have their own separate variable scope which is a key difference. A Partial is usually passed a set of data to render it’s markup for and will have no access to any external data.

In our case, we will loop over all the Entries we retrieved from the database, and use a Partial as the basis for rendering each Entry. To make it easier, we don’t even need to do the looping! There is a Partial related View Helper called Zend_View_Helper_PartialLoop which can iterate over any array or traversable object automatically rendering a Partial for each.

Let’s take a look at the revised version of /application/views/scripts/index/index.phtml.

Since all entries are rendered through a Partial, the needed code is minimal. Just call partialLoop providing the path of the Partial to render, and the data to render with. In this case we’ll pass the rowset containing all entries.

The path passed to partialLoop() is relative to the /application/views/scripts directory. Create a new directory called “partials” at /application/views/scripts/partials and save a new _entry.phtml file there. The filename is just a simple convention – the underscore denotes this template as a Partial. It would be a good idea not to create any PartialsController class so this directory is kept only for Partials.

Bearing in mind that you really need to add a few mock blog entries to see this in full effect, revisit the index page from a browser pointed at http://zfblog.

Step 4: Using a Custom View Helper to generate Entry URLs

One thing missing entirely from the View we created above are references to both the extended body (if any) of the entry, and also a link to it’s standalone page (i.e. the page where an extended body is visible along with comments and other entry specific information).

Before we can do this, we need to consider what the entry link should be. In theory it should be the title of the entry, stripped of any non-alphanumeric or more specifically characters not allowed in a URL, with spaces replaced with a hyphen, and also have the actual entry’s database id included somewhere. I will not be including any element of the date in the entry URL – it’s not unique data, it’s changeable, it’s already included in the entry output, and everybody get’s confused as to whether it’s US style or not ;-). You can throw it in however if you prefer entry URLs to show a little more context.

Let’s take for example:

http://zfblog/I-Won-The-Lottery-2

The URL ignores any mention of a Controller/Action/Module since it’s the single most important URL (other than index!) for your blog, so let’s get straight to the point! The starting part of the URL fragment following the domain name is a reduced form of the entry title stripped of any punctuation with spaces replaced with hyphens. It is finished with the id number of the entry as the very last few characters. Placing the id at the very end or the very start ensures any regular expression can easily pull it out.

This should tell you a few things, the most important being that for the purposes of retrieving the correct entry, only the id is important. The remainder of the URL fragment exists only for search engine optimization.

So how to generate this URL? Obviously it’s part of the View, but not supported by any stock Zend Framework code, so we should create a custom View Helper to generate it. This ensures we only one standard means of assembling this URL which is reusable across the whole application.

We’ll start by adding a new class at /application/views/helpers/EntryUrl.php:

The new helper is simple enough. We can pass it any object which has “id” and “title” properties, or optionally an array containing “id” and “title” keys – so it’s quite flexible. Based on these values, the class will then attempt to construct a valid URL combining that information.

Before we can use the View Helper, we must, as is usual, let the Zend Framework know where to find it. In this case, we’ll setup some options for Zend_View in our Bootstrap class at /application/Bootstrap.php. Note the new Helper path added in setupView():

Final step! Let’s rework our _entry.phtml Partial template to include relevant entry URLs.

Yes, there’s almost enough PHP to completely drown out the HTML so feel free to add formatting to split it up a bit better. Take charge of your browser and give the new Index page a trial run. The new link however won’t go anywhere…yet.

Step 5: Single Entry Display

To show just one specific entry, using the URL form we created earlier, we need to create a new Controller and View. The only complex item on our remaining agenda is mapping our entry URL to the right Controller and Action. The usual course of action for this is to use Apache’s mod_rewrite to rewrite a matching URL onto a new request for processing. This would translate the pretty URL into something more functional.

Unfortunately, this won’t work with the Zend Framework since the framework will still use the original request data, which is obviously not the rewritten functional version we want to use. The solution is to push rewriting into the application itself which is the domain of the Zend_Controller_Router classes. The Router in Zend_Controller is what maps any given URL to a Module, Controller and Action combination.

We’ll add a new route to detect using one the Router types available, in our case the Regex Router, which will detect the pretty URL we want to use and route it to the correct Module, Controller and Action. I’m adding it here directly to the Bootstrap class at /application/Bootstrap.php but as with a lot of setup tasks you can create a configuration file for the details and pass them to the Router using a Zend_Config instance.

See the revised setupFrontController() method. We retrieve the current Router and append any new Routes the application may need. The new Regex route requires a name, the regex string, an array of the Module/Controller/Action values this route will tell the Front Controller to dispatch, and optionally any parameters. For example, this route uses the regular expression to locate the entry id (as the content of the first subpattern), and assigns it to the “id” parameter.

With our new route in tow, let’s add three additional files. The first is the Entry Controller which captures the specific entry data based on its id, and passes it to the View. We’ll store it as /application/controllers/EntryController.php.

The second is the actual matching View, which like the index page is almost horrifically simple! This time, since it’s only one entry, we’ll call the Partial View Helper and not it’s Partial Loop cousin. This file will be located at /application/views/scripts/entry/view.phtml.

By calling the HeadTitle View Helper, we can set the title of this particular view which is already taken care of in our Layout template at /application/views/layouts/common.phtml where the name of the blog is also appended. As you can see, having Placeholders makes it easy to add a degree of customisation to our default Layout.

Here’s the revised portion of that Layout now:

You’ll note the entry/view template simply delegates to another partial template. We could put everything from the partial into the main template, but we might be able to reuse the same partial elsewhere so we’ll keep it isolated in one place for now at /application/views/scripts/partials/_entryextended.phtml.

And presto – we can now view any single entry by itself. It’s pretty bare right now, but this single entry view is where more exciting stuff like comments will also be presented.

Go ahead and give it a go in a browser! We may need to revisit styling so it looks great, but it’s entirely functional.

Conclusion

It’s shocking, but after 9 parts we have a functional minimalistic blog application. A little sprinkling of CSS and it would be perfectly presentable. In this Part we’ve covered Zend_View to offer a taste of its potential. If you haven’t previously considered the use of the new features such as Placeholders, then there is no time like the present!

In Part 10, we’ll polish the display of entries by adding some features such as PHP syntax highlighting, caching, and some guidance on how to manipulate the headers sent by the application on entry views. These are often topics a blog benefits from, and ones not often associated with off the shelf blog applications. The key question on my mind is whether this new blog would be capable of weathering a high traffic storm such as the one that recently took out this Serendipity based blog!

All Contents Copyrighted to Pádraic Brady.

About the author: insic

Subscribe in my RSS Feed for more updates on Web Design and Development related articles. Follow me on twitter or drop a message to my inbox.

  • Michel

    Great reading again, but i have one little question about the routing.

    I want a url like:
    http://www.example.com/news/5/the-title

    But i can’t get the routing working, this is what i got.

    $routes['news'] = new Zend_Controller_Router_Route_Regex(‘news/(\d+)/[0-9a-z\._!;,\+\-%]‘,
    array(
    ‘module’ => ‘default’,
    ‘controller’ => ‘news’,
    ‘action’ => ‘view’
    ),
    array(
    ‘id’ => 1
    )
    );

    I think i do something wrong in de regex, maybe you can help me?
    Thank you in advance!

    Michel

  • http://www.landofthewood.co.uk Jack

    Hey Insic, I have just found that the original AstrumFuture blog is back up at:

    http://blog.astrumfutura.com/archives/367-Example-Zend-Framework-Blog-Application-Tutorial-Parts-1-8-Revisited.html

    Thanks for putting it up, I would have been pulling my hair out by now with the lack of decent Zend tutorials if it weren’t for you.

  • http://blog.insicdesigns.com insic2.0

    @Padraic, I have drop you and email in your inbox.

    @Jack, Yes I have notice it also. And im really glad for it.

  • strrev

    Hi insic2.0,

    Thanks for tutorials. Btw, when will be done the last part ?

    Greetings from Romania,

  • Rocky

    Are u looking for writters, also i have questions related to zend certifcation.

  • Mark

    Hi.

    Great that you’ve posted the 9th part of the series – I’ve just finished 8 but where the hell are they on Padraic’s website? I’ve been going crazy trying to find parts after 8 with absolutely no success bar this post.

    Thanks.

  • Mark

    Hi.

    No reply to my other post???

    I’m getting the following error after completing step 5. Can anyone help?

    Fatal error: Uncaught exception ‘Zend_Controller_Response_Exception’ with message ‘Cannot send headers; headers already sent in C:\xampp\htdocs\zfblog\application\Bootstrap.php, line 1′ in C:\xampp\htdocs\zfblog\library\Zend\Controller\Response\Abstract.php:281 Stack trace: #0 C:\xampp\htdocs\zfblog\library\Zend\Controller\Response\Abstract.php(114): Zend_Controller_Response_Abstract->canSendHeaders(true) #1 C:\xampp\htdocs\zfblog\application\Bootstrap.php(58): Zend_Controller_Response_Abstract->setHeader(‘Content-Type’, ‘text/html; char…’, true) #2 C:\xampp\htdocs\zfblog\application\Bootstrap.php(36): Bootstrap::setupFrontController() #3 C:\xampp\htdocs\zfblog\application\Bootstrap.php(16): Bootstrap::prepare() #4 C:\xampp\htdocs\zfblog\public\index.php(16): Bootstrap::run() #5 {main} thrown in C:\xampp\htdocs\zfblog\library\Zend\Controller\Response\Abstract.php on line 281

    Thanks.

  • http://blog.insicdesigns.com insic2.0

    @Mark, Kindly check this line in your bootstrap,

    self::$root = dirname(dirname(<u>_FILE_</u>));

    This is wrong, It must be.

    self::$root = dirname(dirname(__FILE__));

    I check the Part 5 of the post then I notice this encoding error. So maybe this cause the error in your end.

  • Mark

    Thanks for the reply but that’s not it. It’s something to do with when the $response object is sent – I can see these lines in the bootstrap class relating to the response:

    $response = self::$frontController->dispatch(); 
    self::sendResponse($response);

    $response = new Zend_Controller_Response_Http; 
    $response->setHeader(‘Content-Type’, ‘text/html; charset=UTF-8′, true); 
    self::$frontController->setResponse($response);

    public static function sendResponse(Zend_Controller_Response_Http $response) 

    $response->sendResponse(); 
    }

    Can’t figure out why it says it’s already sent the headers! I’m pretty new to this and it’s for a project so it’s not even my main skill – Ineed to have a good read through the tutorial again but I don’t know whether I’ll get it sorted!

    Thanks.

  • http://gion.ro iongion

    Nice,

    What did you use as inspiration for this Bootstrap class :D

  • http://beaumontenterprise.activeboard.com/forum.spark?forumID=90886&p=3&topicID=27898211 Emesiamma

    emm. strange ))

  • CG

    @Mark I had a similar problem with headers already sent. The reason was that my php-file was UTF8 which resulted in the BOM (Byte Order Mark) to be sent first. Thus there is output before the header you want to set.
    Maybe this could be the reason for your problem as well.
    Check http://bugs.php.net/bug.php?id=22108&edit=2 for this bug.

  • http://kaizentech.co.uk kaizentech

    Nice Post about ZEND Framework and Blog. I really appreciate it.

  • Pingback: Zend Framework Tutorials « Silly Bits (and Bytes)

  • http://www.e-profitbooster.com eprofitbooster

    Overall good tutorial and important to the Website professional.

  • http://www.lowesceilingfans.net/blog/ Lowes Ceiling Fans

    Magnificent items from you, man. I have take into accout your stuff previous to and you’re just extremely wonderful. I actually like what you’ve got here, certainly like what you’re saying and the way in which during which you are saying it. You make it entertaining and you continue to care for to stay it smart. I can not wait to read far more from you. This is really a tremendous website.