Yii 1.1: Understanding the view rendering flow

56 followers

Introduction

There is a lot of confusion surrounding what is happening behind the scenes when you render a view.

If we take a look at the blog demo we have 3 major parts of our view rendering. They are:

1) The layouts/column1 and column2 (column1.php and column2.php):

<?php $this->beginContent('/layouts/main'); ?>
<div class="container">
    <div id="content">
        <?php echo $content; ?>
    </div><!-- content -->
</div>
<?php $this->endContent(); ?>

2) We have layouts/main

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="language" content="en" />
 
    <!-- blueprint CSS framework -->
    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/screen.css" media="screen, projection" />
 
       <!-- some more headers... -->
    <title><?php echo CHtml::encode($this->pageTitle); ?></title>
</head>
 
<body>
 
<!-- ... -->
 
    <?php $this->widget('zii.widgets.CBreadcrumbs', array(
        'links'=>$this->breadcrumbs,
    )); ?><!-- breadcrumbs -->
 
    <?php echo $content; ?>
 
</body>
</html>

3) and the view that we render, for example site/contact

<?php
$this->pageTitle=Yii::app()->name . ' - Contact Us';
$this->breadcrumbs=array(
    'Contact',
);
?>
 
<h1>Contact Us</h1>
<!-- ... -->

The call to the view rendering in the contact looks like the following:

/**
     * Displays the contact page
     */
    public function actionContact()
    {
        $model=new ContactForm;
        //some form code was here...
        $this->render('contact',array('model'=>$model));
    }

The common questions and causes of confusion we will address:

1) Why, if I pass contact.php some $test variable like $this->render('contact',array('model'=>$model,'test' => 123)); and I echo it in the layout with echo $test, do I get error 500 undefind variable error?

2) I didn't pass any $content variable so what is

<?php echo $content; ?>

doing inside the beginContent tags of column1.php ?

3) What is $content in layouts/main.php?

<?php echo $content; ?>

I didn't set this variable...

4) Why do both column1.php and main.php echo $content?

5) Why does setting the title inside contact.php change the value of $this->title in main.php?

<title><?php echo CHtml::encode($this->pageTitle); ?></title>
/* in contact.php $this->pageTitle=Yii::app()->name . ' - Contact Us'; */

And the main confusion:

6) What is called before what? What the heck is going on there? ;-)

Diving in the source code

You have two options:
1) You can accept the rules and follow them without understanding
2) You can understand and follow the rules...

If you choose number 1, tutorial is over for you ;-) But if you really want to learn and understand, let's dive together...

Every good IDE has the ability to go directly to the function by pressing ctrl and the function name...

So lets go to SiteController.php, to the actionContact and press the render word at this line:

$this->render('contact',array('model'=>$model));

After pressing you will find yourself inside yii/web/CController.php on line 775

public function render($view,$data=null,$return=false)
    {
        if($this->beforeRender($view))
        {
            $output=$this->renderPartial($view,$data,true);
            if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
                $output=$this->renderFile($layoutFile,array('content'=>$output),true);
 
            $this->afterRender($view,$output);
 
            $output=$this->processOutput($output);
 
            if($return)
                return $output;
            else
                echo $output;
        }
    }

The rendering is starting with a call to beforeRender, This is a preprocessing, if we are talking about the blog demo, no preprocessing there, so it just returns true...

protected function beforeRender($view)
    {
        return true;
    }

After we got true, we can continue ...

We have,

$output=$this->renderPartial($view,$data,true);

So our 'contact' view is rendered partially, with third parameter as true, this means that the result is not echoed to the page, it is captured and passed to the $output variable...

At this stage the contact page is rendered, all the $data it got (the variable array) is passed to the view was processed

After this, if a layout is defined

if(($layoutFile=$this->getLayoutFile($this->layout))!==false)               $output=$this->renderFile($layoutFile,array('content'=>$output),true);

We pass the $output we captured from the contact.php file, and pass it as $content to our layout file column1.php

Also this time the third parameter is true, meaning we done echo the output, we capture it and pass to $output variable (at this point the view output is overwritten, we don't need it any more cause we passed it to the view and it processed).

After this the afterRender called, and the processOutput, it's role is to insert all the javascript css etc. we registered as client scripts etc.

Time for answers

After we followed all the life cycle of the render method, we can answer to all the questions...

1) why if I pass to the contact some $test variable like $this->render('contact',array('model'=>$model,'test' => 123)); And I echo it in the layout with echo $test, I get error 500 undefind variable error?

Answer: Because the parameters we pass to the $this->render method, are processed in the view file , in this example contact.php, and then we pass to the layout the generated view, and the variable $test is out of the scope for hte layout. What to do if you need it? you can use the Yii registry(params), or you can define some class field, or consider creating widget ... (depends on the task)

2) I dont passed any $content variable so what is

<?php echo $content; ?>

Inside the beginContent tags inside column1.php ?

Answer: the captured view is passed to the layout as content parameter

3) Why the layouts/main.php has

<?php echo $content; ?>

Don't passed such variable...

Answer:

<?php
$this->beginContent('//layouts/main');
//...
$this->endContent();

All what between beginContent and endContent, is captured and passed to the layouts/main as content variable...

4) Why both column1.php and main.php echo the $content ?

Answer: If you understood answers to 1-3, you know the answer for this one... they are not the same, and don't have the same value

5) Why, if I make a change to the title inside contact.php, does it changes the value of $this->title in main.php?

<title><?php echo CHtml::encode($this->pageTitle); ?></title>
/* in contact.php $this->pageTitle=Yii::app()->name . ' - Contact Us'; */

Answer:

As we saw, the contact.php is rendered before the layout, so every field (aka class parameters), you change from the view file (contact.php) will effect the layout because it's rendered afterwards.

And the main confusion:

6) What is called before what? What the heck is going on there ? ;-)

contact.php (passed as content)-> column1.php (passed as content)-> main.php
6c07cda81e332dba19bd59a812abc121.png
That's it, you are now pro at layouts ;-)

Total 16 comments

#16364 report it
mem at 2014/02/14 12:33pm
Suggestion :)

Please pass @kenburcham info-graph, to the main article. :) It help me a lot on the clarification.

#16017 report it
Akshat Sharma at 2014/01/10 02:05am
Great

Thanks

#15107 report it
Balu at 2013/10/08 04:46am
Good

Give clear idea about the structure,..superb..

#15058 report it
rackycz at 2013/10/03 02:41am
Awesome :-)

This is what a documentation should look like. (Not only) Yii developers can learn here ...

Thanks.

#14114 report it
Nisanth thulasi at 2013/07/20 02:33am
Theme creation

Explained in great detail!

#13820 report it
kishore kittu at 2013/06/28 05:56am
Great

Its really a good wiki for beginners like me... Thank you very much.

#12572 report it
Gerhard Liebenberg at 2013/03/29 07:17am
immediate misunderstanding due to lack of indentation

Thanx for an excellent wiki.

Something that immediately steered my understanding in the wrong direction (before I read this document), was a simple lack of indentation in the layouts/column1 file:

<?php $this->beginContent('/layouts/main'); ?>
<div class="container">
    <div id="content">
        <?php echo $content; ?>
    </div><!-- content -->
</div>
<?php $this->endContent(); ?>

All three sections (beginContent, container and endContent) are placed right beneath each other. So my first thoughts were:

  1. These must be 3 separate areas on the page.

  2. Oh yes, I can see that beginContent is importing /layouts/main, so that would probably be the top section of your page.

  3. Views are displayed in container - cool.

  4. endContent must be the bottom section of your page and just like beginContent - it can probably also import some file that you can use as footer.

  5. But the "echo $content" in /layouts/main does not make any sense.

However, If layouts/column1 was indented like this:

<?php $this->beginContent('/layouts/main'); ?>
    <div class="container">
        <div id="content">
            <?php echo $content; ?>
        </div><!-- content -->
    </div>
<?php $this->endContent(); ?>

then I would immediately have noticed that they all work together to produce one section/row. And that would also make sense when I read that this section/row is then displayed in layouts/main's $content.

Just my 2 cents:)

#11182 report it
vrushank at 2012/12/24 04:22am
Helped A Lot

Thanks a lot for making such a nice tutorial. It cleared my doubts about view rendering in Yii, as i am new to yii and hunting for such tutorials, one of the best i got on view rendering.

#8467 report it
ps_sach at 2012/06/05 11:59pm
Great Explaination

Explained in great detail!

#8259 report it
Rajith R at 2012/05/22 02:30am
Great tutorial! Thanks

Great tutorial! Thanks

#8038 report it
kenburcham at 2012/05/04 05:42pm
Helpful diagram

Your blog post helped me understand, but as they say, a picture is worth a thousand words, so here's a diagram.

yii layout flow

#7879 report it
rix.rix. at 2012/04/24 12:13pm
Great tutorial! Thanks

Feel it's helpful to add a explanation of column1, column2 and main.php.

Essentially the columns are 'extended' layouts which your controller can use.

You can specify the layout per controller like this:

public $layout='//layouts/main;

#6348 report it
Rajcsányi Zoltán at 2012/01/01 12:29pm
Good work

I learned a lot from it too. I read this article some times so maybe it is a little help:

  • open blogdemo's some source files: (in this order)
    1. /protected/controllers/SiteController.php
    2. /protected/views/site/error.php
    3. /protected/views/layouts/column1.php
    4. /protected/views/layouts/main.php

After I read this article open this four files and rarely understand the knowledge, but my first question who work is "/protected/views/layouts/column2.php"? So open it: 5. /protected/controller/PostController.php

So if you don't understand first or maybe fifth time, it is little help ...

#6069 report it
Peniel at 2011/12/09 04:26pm
Interesting

Interesting images also the tutorial

#5459 report it
nettrinity at 2011/10/13 10:41pm
very good

I learned a lot from it. Very good exercise!

#5373 report it
Roman Solomatin at 2011/10/07 12:10pm
Nice

Lol, nice images and graphics. :)

Leave a comment

Please to leave your comment.

Write new article