Content decorators are a less heralded but interesting feature of the Yii framework. Content decorators allow you to hijack the view rendering process and add some additional stuff around the view file being rendered. In this excerpt from Chapter 7, “Working with Controllers,” of “The Yii Book“, I’ll explain what content decorators are and how you use them. (This does assume you have some familiarity with Yii and how it renders the complete layout.)
Content decorators are created by invoking the controller’s beginContent()
and endContent()
methods. The beginContent()
method takes as an argument the view file into which this content should be inserted (i.e., treat this output as the value of $content
in that view):
<!--?php $this--->beginContent('//directory/file.php'); ?> // Content. <!--?php $this--->endContent(); ?>
This feature is best used for handling more complex embedded or nested layouts, although it can be used in other ways, too (there’s an interesting example in Alexander Makarov’s book).
For example, say you wanted some pages in your site to use the full page width for the content:
More recent versions of Yii will automatically create three layout files when you create a new Web app:
- views/layouts/column1.php
- views/layouts/column2.php
- views/layouts/main.php
Although all three are layout files, you don’t have three different template files. The main.php file still creates the DOCTYPE and HTML and HEAD and so forth. The column1.php and column2.php files are decorators that create variations on how the page-specific content gets rendered. Here is the entirety of column1.php:
<!--?php $this--->beginContent('//layouts/main'); ?></pre> <div id="content"> </div> <pre> <!--?php $this--->endContent(); ?>
Again, you have the magic echo $content
line there, but all column1.php does is wrap the page-specific content in a DIV.
The column2.php file starts off the same, but adds another DIV (which includes some widgets) before $this->endContent()
:
<!--?php $this--->beginContent('//layouts/main'); ?></pre> <div class="span-19"> <div id="content"> </div> <!-- content --></div> <div class="span-5 last"> <div id="sidebar"><!--?php $this--->beginWidget('zii.widgets.CPortlet', array('title'=>'Operations')); $this->widget('zii.widgets.CMenu', array( 'items'=>$this->menu, 'htmlOptions'=>array('class'=>'operations'), )); $this->endWidget(); ?></div> <!-- sidebar --></div> <pre> <!--?php $this--->endContent(); ?>
To be absolutely clear on what’s happening, the protected/components/Controller.php class sets column1.php as the default layout file. When, say, the actionIndex()
method of the SiteController
class is invoked, it renders the protected/views/site/index.php file. The rendered result from index.php will be passed to the layout file, column1.php. This means the $content
variable in column1.php represents the rendered result from index.php.
Then, because column1.php uses beginContent()
the rendered result from column1.php will be passed to the main.php layout file (because it’s provided as an argument to beginContent()
). This means the $content
variable in main.php represents the rendered result from *column1.php.
Personally I think this default layout approach is a bit complicated for the Yii newbie (yiibie?), but it is invaluable in situations where the content around the page-specific content needs to be adjusted dynamically.