Working with Layouts in Yii

May 16, 2012 — 41 Comments
The Yii Book If you like my writing on the Yii framework, you'll love "The Yii Book"!

Using the Model-View-Controller (MVC) design pattern, the look of a Yii-based site is naturally controlled by the View files. These files are a combination of HTML and PHP that help to create the desired output. Specific pages in a site will use specific View files. In fact, the View files are designed to be broken down quite atomically, such that, for example, the form used to both create and edit an employee record is its own file, and that file can be included by both create.php and update.php. As with most things in OOP, implementing atomic, decoupled functionality goes a long way towards improving reusability. But the individual View files are only part of the equation for rendering a Web page. Individual view files get rendered within a layout file. And although I’ve mentioned layouts a time or two in my writings on Yii, it’s a subject that deserves its own post.

To be clear, layouts are a type of View file. Specifically, whereas other View files get placed within a directory for the corresponding Controller (i.e., the SiteController pulls from views/site), layout files go within views/layouts. But while the other View files are associated with individual Controllers (and therefore, individual pages), layouts are communal, shared by all the pages. Simply put, a layout file is the parent wrapper for the entire site’s templating system. I’ll explain…

The Premise of Templates

When you begin creating dynamic Web sites using PHP, you’ll quickly recognize that many parts of an HTML page will be repeated throughout the site. For example, the opening and closing HTML and BODY tags. Even the site you’re looking at now has repeating elements: the header, the navigation, the footer, etc. To create a template system, you would pull all of those common elements out and put them into one (or more) separate files. Then each specific page can include these files around the page-specific content:

include('header.html');
// Add page-specific content.
include('footer.html'); 

This is the approach I would use on non-framework-based sites. It’s easy to generate and maintain. If you need to change the header for the entire site, you only need to edit the one file.

Templates in Yii

When using a framework, you’ll probably end up with a “bootstrap” approach, where all of the pages are accessed through one file. In the case of Yii, that bootstrap file is index.php. Part of its job is to manufacture the necessary HTML for the requested resource. Whereas the non-framework approach pulls the template files into the page-specific file, Yii pulls all the files from includes and assembles them together. Of these files, the layout files constitute all the common elements; everything that’s not page-specific. If you look at a Yii-generated site, you’ll see that views/layouts/main.php begins with the DOCTYPE and opening HTML tag, then has the HTML HEAD and all its jazz, then starts the BODY, and finally has the footer material and the closing tags. In the middle of the body of the code, you’ll see this line:

<?php echo $content; ?>

This is a magic line as it pulls in the page-specific content. If the site you’re looking at now used Yii, the value of $content would be all the HTML that makes up this post you’re reading. For a Yii example, when the user is looking at site/login, the SiteController‘s actionLogin() method will be called. That method will render the views/site/login.php View page, pulling that file’s contents into the main layout file at that echo $content location. That’s what’s going on behind the scenes.

So here, then, is the first key concept: if you want to change the general look of your Web site, edit the layout file (views/layouts/main.php). If you were to take your HTML mockup for your site, drop in the echo $content; line at the right place, and save it as views/layout/main.php, you will have created a custom look for your Web app. That is the basic principle and it’s essentially that simple.

To take layouts a step further, many sites will use variations on a theme. For example, the first Yii-based site I created used one header for the home page and another header for other pages, with all the remaining common elements being consistent. Or, I worked on an e-commerce project where one category of products used layout A and another category used layout B. If you have this need, there are a couple of ways of going about it.

Creating a Second Layout

The first and most obvious route is to create a second layout file. In the example where the site’s home page used a variation on the template, I created views/layouts/home.php. It was based upon main.php, with the necessary edits. To dynamically switch layouts, I changed the value assigned to the layout property within the proper Controller method:

// protected/controllers/SiteController.php
public function actionIndex() {
    $this->layout = 'home';

And that’s all there is to it. When views/site/index.php would be rendered, it’d use the views/layouts/home.php template.

Although this approach is easy to understand and implement, it has a downside: a LOT of HTML is being repeated between the two layout files. If I needed to change the navigation, I had to edit both files in the same way. This leads us to option B.

Hijacking 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 simply create variations on how the page-specific content gets rendered. These files do so by hijacking the layout process. For example, here is the entirety of column1.php:

<?php $this->beginContent('//layouts/main'); ?>
<div id="content"><?php echo $content; ?></div>
<?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'); ?>
<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>
<?php $this->endContent(); ?>

The trick here is the call to beginContent(). Remember how the layout file echoes the page-specific content in the correct place? Well, this call to beginContent() says that we’re about to start rendering the content. The method is provided with the primary layout file to use as the parent (i.e., the layout that encapsulates this content). All that’s happened here is that the content has been hijacked and replaced with slightly modified content. So the content in views/site/login.php gets pulled into column.php, where it’s wrapped within other content, and the combination of that content gets passed to main.php.

There are two tricks to this: first, beginContent() must point to //layouts/main.php. (The // just says to start in the Views directory.) Second, the layout that the Controller thinks it’s using is column1.php, not main.php. I personally think this approach is a bit complicated, particularly for the Yii newbie (yiibie?), but it is useful in situations where the content around the page-specific content needs to be adjusted dynamically.

If you were to open components/Controller.php, you would see this line:

public $layout='//layouts/column1';

That one line says that all Controllers (which inherit from Controller) will use column1.php as its layout. If you were to change this one line to column2, then all Controllers would use that as the default layout. If you were to change this one line to main, then all Controllers would use main.php as the default layout, without the intermediary column layouts.

Again, this is a bit more convoluted than is healthy for newbies, but layouts are just wrappers to page-specific content, whether you use a single layout or intermediary layouts such as column1 and column2. If you’d like a visual representation, there’s a useful image provided among the comments in this article.

Quick Guide

To summarize what’s been covered here…

To change the entire look for the entire site, edit layouts/main.php, but be sure to use the echo $content line where appropriate.

To change the default layout for every Controller, edit this line in components/Controller.php:

public $layout='//layouts/column1';

To change the default layout for every View in an individual Controller, add this line to that Controller’s definition:

class SiteController extends Controller {
public $layout = 'column2';

To change the layout used for a single action, add this line to the corresponding action:

// protected/controllers/SiteController.php
public function actionIndex() {
    $this->layout = 'home';

And remember that column1.php and column2.php just hijack the page-specific content before it gets passed on to the main.php layout file.

I hope this post has been helpful and let me know if you have any questions or problems with layouts in Yii. (And by the way, if you like this post, I’m going to be writing a book on Yii later this summer. Subscribe to my newsletter or follow me on Twitter to stay tuned!)

If you enjoyed this post, then please consider following me using your favorite social media, the RSS feed, and/or by subscribing to my newsletter. Or go crazy, and buy one or more of my books . Thanks!

41 responses to Working with Layouts in Yii

  1. Thanks for sharing knowledge Larry.. Every line is clean and clear, nothing more nothing less. Honestly it is a pleasure to read you. Greetings from Greece.

  2. Really enjoy your yii posts. Looking forward to the book

  3. Just though I might add a suggestion for another topic that’s not covered very thoroughly in Yii’s documentation. This might be a good topic to bring up in your upcoming book as well, which I’m very much looking forward to. It might even be a newbie question coming from my end, but then again there are a lot of fellow newbies in the Yii sphere too. The topic is the proper use of “Modules” and how to configure them, how the hierarchy of modules works when you are embedding modules within modules and what to look out for when creating an application using modules. Thanks!

    • Hello, Navid. Thanks for the suggestion and for the interest in the book. I haven’t fleshed out my table of contents yet, but will cover modules well. The hierarchy is a good topic, and one that I might not have thought of on my own. Thanks again!

  4. Thanks a lot Larry, for the clear and nicely laid out guide.

  5. Hi Larry,
    If I have several css files & javascript files that I’d like to use on different pages, How is the best practice?
    Should I just simply define all of them at once in the main.php of layout? or is there a way that Yii can intelligently load the required css files & js files for each page?

    Thanks

    • I would start by rethinking how complicated your site is. There may not be another way around it, but different CSS and JS on different pages is somewhat rare, in my experience. If it’s still a need, you could use different layout files for different pages.

  6. You helped me learn php/mysql and helped me get up to speed on javascript after ignoring it for too long. I’m looking forward to the YII framework book. Thank you for sharing your knowledge!

  7. I’m confused in this article where it says “For example, here is the entirety of column1.php:” and then after showing the example you say “Again, you have the magic echo $content line there” however in the code listing for column1.php I’m not seeing the echo $content statement. When I look at column1.php in my own installation of yii, I do see echo $content. Is this a typo or am I missing something?

  8. In fact the whole code listing for column1.php looks suspect. I’m using Firefox 14.0 and I’m seeing “beginContent(‘//layouts/main’); ?>” maybe there is a pre tag missing? Also the same issue with the column2.php code listing.

  9. bah comment form ate my html. Let’s see if this works:

    beginContent(‘//layouts/main’); ?>

  10. Clear article, thank you very very much. It helped a lot.

  11. Hi, Larry..
    Can You give me example how to create dropdownlist in layout? and the datas are taken from database. Thanks before

  12. thanks larry, I just want learn more about yii

  13. I’m Creating one View in components and adding widget in column 2. below the port-lets of blog application. I’ve passed my model object to the view by render(‘filename’,array(‘model’=>$model) so view have the model object in components/view. its working fine now and i’m able to see the form below the portlet in the actual form.

    now problem is when i save the portlet form its not actually saves in the database. I’ve written action method to save the data in file’s controller but i think it;s not getting called can you help me what to save the portlet form

    thanks and regards,
    Pratik Shah

  14. Larry, nice post indeed, I was working on a project of yii. I’m facing a problem, like in a controller I gave a public $layout = ‘new'; that is I assigned all the actions to that particular layout. But I don’t know why the actions I have created manually is not coming up with the layout. All the contents are coming just as a raw html. Even I put a die(var_dump()) on the the 1st line of the layout/new.php it’s giving all the content from other actions, but not from that action, that clearly mean it’s not even going to that layout. Do you have any solution for that?

  15. actually no other actions except index is sending contents to the layout

  16. hi
    when i am changes the themes in yii after changes the automatically links like create and manage user is not display so what was the reason behind that

  17. Hi Larry,
    I want to make a page without any layout. Can you please help me. I want to remove the layout.

  18. nice post……….

  19. Nice and clearly written. Larry, you have been a great impact to my Yii development. Got me up to speed in no time. Thanx, from Nigeria.

  20. Thank you so much. It’s a nice tutorial of Yii. Easy to follow. You just resolved my problem related to Yii’s layout. I will follow the other series such as JQuery.

  21. Hi Larry could you please tell me how to set timezone in main.php and then how to use it in view file

  22. Can i know how to insert image in layouts/main.php. I’m using Bootstrap extension.

  23. Nice article!

    I have a question about the layout fallback mechanism.

    Lets suppose I have module called admin and active theme called classic, so main.php exists in three places as shown below

    protected/views/layout/main.php
    themes/classic/views/layout/main.php
    protected/modules/admin/views/layout/main.php

    Now I want to know which layout file will take precedence and what is the order?

  24. I have confused those things for two days. Now only i get a clear view. Thank you larry..

  25. Awesome. really, really helpful. Thanks sooo much – easy to follow, worked like a charm.

  26. Nothing to say than u help me in understanding yii

  27. Thanks you Larry

Trackbacks and Pingbacks:

  1. What is Larry Thinking? #59 => Frameworks and Yii | Larry Ullman - July 23, 2013

    […] in May, I posted Working with Layouts in Yii, which explains how the layout system works in the Yii framework. If you’re using the framework, […]

Comments are great, but I'd strongly prefer any requests for assistance get made in the support forums. Thanks!