A Zend Framework tutorial, part four
by Naneau
In the first three parts of this tutorial I talked about designing your models and controllers. In this fourth part I’m going to take a look at views. Views are the final part of a MVC application. They contain code that will create the output of the application.
For most of you this will mean that your views will contain HTML. There are a few ways to do this. First, you could just use Zend_View. While this probably is the most ‘pure’ way to go from a Zend Framework point of view, it is not my first choice. I feel that mixing raw php and html leads to complicated templates, that may be hard to understand for template designers, who often aren’t really adept at coding.
Before the Zend Framework was thought up, there was Smarty. It’s my template engine of choice. I have written a little tutorial, that tells you how to use Smarty with the Zend Framework, if you’re interested.
Either way, in your controller actions you will have to initiate a view and assign variables to it. I have a controller base class, which all controllers extend from. The base itself extends Zend_Controller_Action. In that class I have a method called “createView” that does exactly what it describes. It builds a view and assigns some variables to it that all templates use. For a standard Zend_View based application this would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 | protected function createView() { Zend_Loader::loadClass('Zend_View'); //load it $view = new Zend_View(); //create the view $view->somevariable = 'some value'; $view->anothervariable = 'another value'; //assign variables return $view; } |
Like I said, I tend to use Smarty for my templates, but you should realize that Smarty is a php4 library. There may be a few things you can’t do with Smarty in a php5 environment, although I have never really had any problems with it. For our example application of a blog, you will want to create a template for a single post. You can then assign a single Zend_Db_Table_Row to it and use it’s values. See part two of this tutorial for information about the controller methods readAction and getPost.
1 2 3 4 5 6 7 8 9 10 | public function readAction() { if ($post = $this->getPost()) { $view = $this->createView(); $view->post = $post; $this->getResponse()->setBody($view->render('post.tpl')); } else { //post not found, do something meaningful } } |
You’ll have to write a matching template file called post.tpl, which could look a little like this, though probably a lot more complex. Note that this file uses Smarty syntax, not Zend_View-like php.
1 2 3 4 5 6 7 8 9 |
You will probably find that most of your templates contain the same code over and over again. For instance the start of the html, with the head, title, javascript and css, may not change for all your pages. You can get around this by using Smarty’s {include} tag in your templates and create a “head.tpl”, but there is another way. Just create a base template, called “index.tpl”, that gets an “include” variable from your controller action. It then includes the file you put in that variable, so you only have to write templates for the bits that really change from page to page.
That method has another benefit as well. If you write a __toString method for your view class, you don’t have to tell it to render index.tpl each and every time you set the body for your response.
1 2 3 4 | public function __toString() { return $this->render($this->_defaultTemplate); //_defaultTemplate = 'index.tpl' } |
You can just assign the view as the body in your controllers
1 2 3 | $view->post = $post; $view->include = 'post.tpl'; $this->getResponse()->setBody($view); |
That is pretty much it. With minimal amounts of code you can create a very functional application. Of course there still are some things left, like working with input from your users to create and modify posts, but I leave that to you. Using Zend_Db_Table models gives you the power to do that with ease.
Comments
Thank you for the great tutorial.
I like your point of view.
I`m interested in using zend_acl for letting users update their own posts and even letting users assign access rights to over users on their own posts.
I tried to follow a tutorial about this but it`s a bit messy.
http://devzone.zend.com/node/view/id/1665
A tutorial in this area will be really appreciated, at least by me
Yeah, I’ve tried to follow that tutorial as well. When I did that the missing code wasn’t even in the comments, so I had to do a whole lot of guessing as to what Simon meant. But the solution he displays is an elegant and powerful one.
I’m not sure whether I’m the right person to write about Zend_Acl though. But it is something I’d like to study, so who knows. I’ll try to see what I can do.
I heard there were a few issues with rendering HTML using ZF controllers. Have you faced any rendering issues?
I am really curious to know if clean URLs is really worth all this effort with a Controller and its overhead.
$this->getResponse()->setBody($view->render(‘post.tpl’));
Like tracing the objects in the controller to figure out what exactly does this line do is time consuming.
It’s not just clean URLs why you would use a MVC approach. In the controllers of the Zend Framework you can work with a request and a response object in a very clean object oriented way. I thought getResponse() was pretty descriptive. It returns the response object, to which you can then assign headers and a body.
Rendering HTML can be done in a variety of ways. You could of course just echo whatever HTML you produce, but that gets messy very quickly. If you use views (be that Zend_View or my Smarty approach) you can echo the result of that, or just assign it as the response body.
If you do not want all that, there is of course no benefit to using the controllers. You don’t HAVE to by the way, you can use all the components of the framework without using controllers. I just feel that using them leads to a very structured approach to creating a web application.
Thanks Naneau. Maybe we just need to look into the controller in depth.
I generally don’t like to be tied into a structure. Reasons being:
1) There could be other scripting languages like Ruby that we might choose to migrate to. Migrating a simple object structure is easier than migrating the entire ZF.
2) Some hosting environments might not support eh ZF yet.
But as you mentioned, we can use ZF without using the controller. The good thing about a structured approach is maintenance. But then again, we can’t find 5 people in Singapore that understand the ZF, so its going to be even tougher to maintain ZF code.
Thanks for all these tutorials. It’s really handy. Cheers.
Hi Naneau, I’ve got an interesting issue. Probably you could solve it.
I have managed to get the controllers working, and they are rendering views.
I have a view that displays content from a model class.
And there is an image dispalyed called “/img/Logo.gif”.
when the URL is http://site.com/ everything is fine.
But when the URL is http://site.com/index/ OR http://site.com/index/index which is the same page really, the image reference is “/index/img/Logo.gif”
Any thoughts here? Interested in getting the files?
Ah.. yes. That issue predates the Zend Framework
. What you should do is either hardcode the root of your site (http://site.com) in front of your image source (/img/Logo.gif), so it looks like src=”http://site.com/img/Logo.gif”. Or, even better, create a small view helper that can do that for you. (Which is what I would do, because if your site url were to change, you would only have to change the view helper for all the images to be ported).
Have a look at the zend manual to learn more about view helpers: http://framework.zend.com/manual/en/zend.view.helpers.html
Prior to ZF, the images were all referenced to the initiating php script. In the http://harro.com case, it was /index.php, so everything works nicely.
However, the ZF causes the calling script to change now. Will look at helpers.
Yes, I try to avoid relative links (be it images or just plain links) at all cost. They always cause problems later on, in my experience, and it’s just so easy to create a view helper or some other way of adding a url root variable to your views/templates. For Smarty I always have {$url_root} available.
At least for recent version of ZF each body parts are being typcasted to strings before they are stored in the body array (using the $var = (string) $var; syntax).
Such a typecast does not trigger __toString() so i’m a bit curious how you got that to work?
As i understood it you put the __toString method in a subclass of Naneau_View_Smarty?
Oh, i found the answer myself just after psoting this comment, they have changed it in PHP version 5.2.0, so that __toString is called even for a typecast.
Well, maybe someone else with PHP
You are, of course, completely right. That behavior was changed in 5.2. As the ZF runs on anything higher than 5.1.4 I should have mentioned it would only work in 5.2, even though it’s just a little nicety.
Thanks for clearing this up
. I’m just trying to grasp the concepts of ZF and i find this blog very useful, keep up the good work
.
That’s just the road I went before “playing” with ZF. I love and hate these little tricky lines which let my wishes become true. The last two samples of code in your article were the solution to rebuild the smarty-game that I am used to.
Big thanks!
I’m trying to do a Zend framework based news management system. While rendering an article, the paragraphs dont show, and the entire article is one block of words. If I add html tags, they are rendered in text. How do I render bold, italics, breaks, paras etc?
Maurice, you know what? I think rendering views using ZF is a real pain. Unfortunately, its a pain that I have to deal with. Ok, maybe you can help me out on this. If need be, I’ll be happy to send you the files.
I’m using ZF-RC1.
My frontcontroller dispatches nicely. My indexAction in my indexcontroller works find when I have:
public function indexAction() {
$id = (int) $this->getRequest()->getParam(‘id’);
$this->view->date = date(‘Y-m-d H:i:s’);
$this->view->id = “ID: ” . $id;
$this->render();
}
I take this as Zend’s default, fully automated view rendering. It pulls my index.phtml view-script and works. THIS WORKS.
Say I want to do a setBody within indexAction, and now my script looks like this:
public function indexAction() {
$id = (int) $this->getRequest()->getParam(‘id’);
$response = $this->getResponse();
$response->setBody(‘test’);
}
Outputs ‘test’. THIS WORKS too. Question : Why does it still auto renders my index.phtml appended after ‘test’?
Ok, now I want to get more fancy and render some content.
public function indexAction() {
$id = (int) $this->getRequest()->getParam(‘id’);
$response = $this->getResponse();
Zend_Loader::loadClass(‘Zend_View’);
$view = new Zend_View();
$view->somevar = ’somevalue’;
$response->setBody($view->render(‘testoutput.phtml’));
// echo $view->render(‘testoutput.phtml’);
// $this->render();
}
THIS FAILS. testoutput.phtml is pure html file. (ie. no embedded php). Can you figure out why nothing is returned to the browser? I did try to echo the $view, but to no avail.
I think the challenge is to see some feedback from the controller. Debugging is tough short of either implementing a log. Any suggestions on debugging?
Thanks.
What you’re probably running into is the viewRenderer action helper. It automatically renders a view script controller/action.phtml style. You manage to get it working. But if you want more control over what gets rendered where, you may want to disable the viewRenderer. This can be done (in action controlller context) with:
$this->_helper->viewRenderer->setNoRender();
If you want to disable it application wide, do the following in your bootstrap:
$front->setParam(‘noViewRenderer’, true);
where $front is your front controller.
I hope this helps you on your way. If it doesn’t, why not visit #zftalk on freenode? I hang out there a lot, and there’s plenty of others who can help you. If you’re new to IRC, check http://www.zftalk.com for an applett that will get you online.
Wow! wow! wow! I lack words!
Please can someone post a downloadable file with the whole files and structure in it?
Thanks a lot!
Currently I will have my action method call the index view for that controller. In the action I will set my various properties, then in the index view I will render various standard bits (header, footer, nav, ads, etc.) while calling the page specific properties from the action.
For example: potatoAction of SaladController will set $this->view->first_graph to some html content. It will then render the index view for SaladController. The index view will have a number of calls to render OTHER views (echo $this->render(‘header.phtml’) and echo $this->render(’salad/content.pthml’) for example). content.phtml will render the first_graph property set in potatoAction. This seems clunky to me. Do you have any insight into how to better handle something like this?
p.s. I asked something like this on #zftalk but… erm, no answers at all.
That sometimes happens in #zftalk. Usually the channel is quite busy though and you should be able to get an answer.
Anyway, to answer your question. You may want to have a look at Xend_Layout. It will be included as Zend_Layout in 1.1. It’s a great layout solution written by Ralph Schindler. You can find more about it here:
http://www.spotsec.com/blogs/archive/the-basics-of-zend_layout-ahem-xend_layout.html
download it from Ralph’s SVN:
http://svn.ralphschindler.com/repo/Xend/
Yes, I did finally find that. I also found Zend_View Enhanced which seems to offer its own similar sort of solution. I’ll read about them both but perhaps you are familiar with the differences between these two already?
At the moment Xend_Layout is the way to go. ZF 1.1 will include a Layout solution that will in part be based on Xend_Layout. In the meantime it’s a solid implementation that will get you where you want.
thanks it helps me a lot
Thanks so much, this is the most useful Zend Framework MVC tutorial I could find by googling around. it’s helped me quickly understand the basics. Strangely, most other tutorials didn’t mention the models part at all. Many thanks for taking the time to write and publish this.
i am a newbie and downloaded ZF 1.5
Please help me if you have a few to spare on this. I am trying to figure it out two days..
I dont know why i am getting this error:
[code]
exception 'Zend_Exception' with message 'At least one error occurred including "User.php"; see includeErrors property' in C:wampwwwtutoriallibZendLoader.php:300
Stack trace:
#0 C:wampwwwtutoriallibZendLoader.php(93): Zend_Loader::_throwIncludeErrors('User.php')
#1 C:wampwwwtutorialapplicationcontrollersBlogController.php(17): Zend_Loader::loadClass('User')
#2 C:wampwwwtutoriallibZendControllerAction.php(499): BlogController->indexAction()
#3 C:wampwwwtutoriallibZendControllerDispatcherStandard.php(242): Zend_Controller_Action->dispatch('indexAction')
#4 C:wampwwwtutoriallibZendControllerFront.php(927): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#5 C:wampwwwtutorialindex.php(48): Zend_Controller_Front->dispatch()
#6 {main}
[/code]
hrgof vbtm ucxthkv tqekubnhx etxjd cqubf icflongu
xygbv smrywopc toyx tkhgmudx tfmz hlmvsn phwejr
One thing i find when using zend framework VMC structure is a problem for designers.
Attaching CSS and IMAGES to your layouts is very easy using baseUrl();?> after you created a helper, BUT, when a designer opens the .phtml layout into his DREAMWEAVER for example, his page finds neither CSS files or IMAGE files. This of course takes away all advantages of a wysiwyg editor!
Therefor i would still suggest to hard-code the absolute path to those files, it doesn’t look pretty or clean, but at least the designer still has his wysiwyg functionality and can design the way he was always used to…
Anyone know of a better solution?