Friday, June 3, 2011

Referencing views from alternate locations and themeability

This is something very simple, but it's very easy to do it 'wrong' and end up creating more work for yourself in the long run.

I ran into this again today when I was trying to create a theme for a site, and I knew that I want to be able to deploy the site in multiple locations (I will make another post later regarding distributed Yii applications) which employ different themes to present the same forms.



Typically, when you're going to render a view, you would simply

...
$this->render('myview');
...

But occasionally, you may need to reference a view which is stored in something OTHER than the default folder for the controller -- lets say, for example, that you have a lot of partial views that you want to categorize into folders, something like
/views/site/
/views/site/rightBarOptions/
/views/site/rightBarOptions/_layout1.php
/views/site/rightBarOptions/_layout2.php

If you want to explicitly ensure that the EXACT file in your primary view structure, you could (and many people do) reference it as:
$this->renderPartial('application.views.site.rightBarOptions._layout1');

However, if you do this, it will NOT be overwritten by any theme based file you may later wish to use. If you want the file to be recognizable by the theme, you need to reference it via the view path 'operator', like so:
$this->renderPartial('//site/rightBarOptions/_layout1');

The magic is in the first two slashes '//' which indicate that the path to search is the 'current view path'. This will search your default application views directory, unless you have a theme applied in which case it will search the theme views directory first, then if not finding a match, it will fall back to the application views directory.

This same method works if you want to reference a file from a controller or widget that is stored under another controller. For example, if I have a a '_search' partial that is used in my admin view, and I also want to use the SAME search form within another page from another controller, I can reference it as such:

// In the views/posts/index.php view
// Note, this will automatically translate into //posts/_search
$this->renderPartial('_search', array('model'=>$searchModel));
...

// In the views/authors/view.php view
// Say I want to be able to search the posts this author has made
// My searchModel would have pre-set the author_id
$this->renderPartial('//posts/_search', array('model'=>$searchModel));


To learn more about themes, read the Yii Guide here:
http://www.yiiframework.com/doc/guide/1.1/en/topics.theming


Themes are very powerful features and can be applied to widgets and modules simply by placing the module name or widget name under the theme's view path and then placing your themed version of the module/widget file in that structure.