Fun with the ViewRenderer
by Naneau
The ViewRenderer
Really, there is fun to be had! Since it’s introduction, the ViewRenderer had caused headaches and frustration. It is the one thing that glues the Controller and View parts of Zend’s MVC implementation together. As such, it ties in strongly with your code and is likely to cause confusion if you don’t know what it does.
Retrieving the helper
The ViewRenderer is an action helper. This means that it is stored in the helper broker. You can retrieve it anywhere in your code with:
1 | $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); |
There is one small catch. If you retrieve it before dispatch, the view object it contains has not been initialized. You can do that yourself by calling initView() on the object:
1 2 3 4 5 6 7 | $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer'); $viewRenderer->initView(); //retrieve the helper and initialize the view $viewRenderer->view->someVar = 'someValue'; //set a variable in the view //this will be available in all your view scripts |
The view object contained in the helper is the only view object your application will work with. Any variable you assign to it will be available in all the view scripts you render. If you want to modify the view script, for instance if you want to pass extra configuration options, you can do it like this:
1 2 3 4 | $view = new Zend_View(array('encoding' => 'utf-8')); //a view object with the encoding option set to utf-8 $viewRenderer->setView($view); //set the object in the viewRenderer |
In this example, all I do is set up a configuration setting. As is made clear in the comments for this post, this could also be done with:
1 | $viewRenderer->view->setEncoding(’utf-8′); |
If you want to use a custom view object, like one with Smarty rendering, you do need to set up the property yourself.
Setting up your view scripts
Unless you disable it, the ViewRenderer action helper will try to render a view for every action that gets called. I am sure that by now you know that for a controller FruitController, with an action appleAction() you need to create a view file “/views/scripts/fruit/apple.phtml”. View scripts are stored in the “views” directory of your module. If you use a directory layout without modules (i.e. you only have a single non-namespaced module “default”) you should have it in the same directory as you “controllers” directory.
You can immediately see the problem here. If you don’t follow the convention for naming your view scripts you get an exception like:
1 | script 'index/index.phtml' not found in path (/your/path/to/view/scripts/) |
The assumption that every action will have a view may be false for you. Luckily, the helper is smart enough to not try to render a view when you do _forward() and _redirect(). If you render a view manually (using $this->render()) it won’t try to find the default view either. If that is not enough for you, you can disable the view rendering for a specific action with:
1 2 | $this->_helper->viewRenderer->setNoRender(); //disable the view rendering for an action |
Advanced ViewRenderer magic
When you start writing view helpers you’ll probably put them in the “helpers” subdirectory of your “views” directory. This is fine, but it will start causing problems as soon as you want to use the same helper in other modules. To get around this you could put the helpers in your library and add the containing directory like this:
1 2 3 4 5 6 | $viewRenderer->view->addHelperPath( 'Naneau/View/Helper/', //the path to the helpers 'Naneau_View_Helper' //the class prefix ); |
As you can see, the addHelperPath function of Zend_View expects two arguments, one is the path to where your helpers are located. The second is the prefix for the helpers. I use “Naneau” as my namespace, so my helpers start with Naneau_View_Helper.
Another thing that people regularly ask is how to put the content the helper renders into a sidewide layout file. There are several solutions for this. The most interesting at the moment is called Xend_Layout. It will, in modified form, be included in the 1.1 release of the framework as Zend_Layout. SpotSec has written a nice article about it, which I suggest you read
. Just remember that even advanced solutions like Xend_Layout use the ViewRenderer. Also, please note that Xend_Layout is currently a proposal, and is likely to undergo significant changes before it moves to Zend_Layout.
Comments
Naneau — nice summary! A few notes:
* Don’t call $viewRenderer->init(); this should only be called by the helper broker, as it is with this method that the ViewRenderer injects the view object into the action controller. Instead, call $viewRenderer->initView(), which will initialize the view object only.
* setView(), not setview() (case matters)
* You can also configure the view object after the fact; e.g., $viewRenderer->view->setEncoding(‘utf-8′)
* One cool feature about the ViewRenderer is that it also adds a helper path relative to the module’s views directory (‘module/views/helpers’), and namespaces them with the module name. As an example, if you were in the blog module, your local helper classes (those in blog/views/helpers/) should be prefixed with ‘Blog_View_Helper_’: blog/views/helpers/Comment.php has the class Blog_View_Helper_Comment, which you then reference as $this->comment() in your view script.
Finally, Xend_Layout will not be Zend_Layout; the current proposal on the framework development wiki is quite different than Ralph’s initial Xend_Layout. Make sure to check out the API it exposes.
Also remember that the default module namespace for view helpers is Zend_View not Default_View which is another gotcha
Right, let me comment on these comments
You are, of course, absolutely right about the init() method. I did just steal it from the helper broker. I should have taken more care. As I should have for the little case typo
I included the method for setting up your own view object as an addition to my Naneau_View_Smarty post. In case you would want to use a custom view object, instead of just setting a few options, you would need to use the setView() method. I have made this more clear now.
I will modify the post to make it more clear that Xend_Layout will undergo (significant) changes before it changes into Zend_Layout.
Isn’t this http://svn.ralphschindler.com/repo/ZendFramework/Zend_Layout/library/
closer to final Zend_Layout?
Btw. Thanks for great articles. Saved me a lot of time.
Having read this I used it to push auth details to the view from a front controller plugin which is cool because I can now do that site wide in a few lines of code.
If the initView() method were to return the view as well, then one might be able to write
$view = Zend_Controller_Action_HelperBroker::getStaticHelper(‘viewRenderer’)->initView();
Is that a reasonable suggestion?
Uhm, are you still alive?
Hey man, u alive? Haven’t seen any updates for a while now
convention directory…
Many blogs have stopped using trackbacks because dealing with spam became too burdensome. (Blogger now has backlinks – very similar…
Моя мысль значительно отличается от изложенной автором, если кому-то интересно, могу поделиться своим опытом. Мой емаил:yura1992.92@mail.ru, Cергей.
Я больше 3х лет регулярно работаю над этим направлением и думаю ваши доводы слишком поверхностными
Очень сильно хочется поделиться опытом с кем-нибудь по этому вопросу. У кого есть возможность, катайте в асю 14532553
ddt-club.ru…
Более 3х лет стабильно работаю над этим направлением и считаю ваши доводы достаточно поверхностными…