pass variables or content block from view file to layout file

You are viewing revision #3 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.

« previous (#2)next (#4) »

if you use yiic to generate webApplication there will exists a layouts dir under the protected/views/, and the Controller class under protected/components which will default use these layouts files .

all your controllers may extends the Controller base class directly or indirectly . please look at this class you may find this lines :

/**
     * @var string the default layout for the controller view. Defaults to '//layouts/column1',
     * meaning using a single column layout. See 'protected/views/layouts/column1.php'.
     */
    public $layout = '//layouts/column1';
    /**
     * @var array context menu items. This property will be assigned to {@link CMenu::items}.
     */
    public $menu = array();
    /**
     * @var array the breadcrumbs of the current page. The value of this property will
     * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
     * for more details on how to specify this property.
     */
    public $breadcrumbs = array();

these variables will be shared by all its subClasses (may be you whole XXXController of your project ) . except the $layout variable the other two usually used to pass variables to layout files :

$this->breadcrumbs=array(
	'Albums'=>array('index'),
	'Manage',
);

$this->menu=array(
	array('label'=>'List Album','url'=>array('index')),
	array('label'=>'Create Album','url'=>array('create')),
);

if you use gii to generate crud functionality you may see such code in the top section of file (admin.php,index.php,create.php,update.php,view....). "$this" represent the current controller , so "breadcrumbs" and "menu" are the member variables of controller and it defined in "Controller" class . now lets take look at layout file "column2":

<?php $this->beginContent('//layouts/main'); ?>
<div class="container">
	<div class="span-19">
		<div id="content">
			<?php echo $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>
</div>
<?php $this->endContent(); ?>
//  and layout main.php:
..
<?php if(isset($this->breadcrumbs)):?>
		<?php $this->widget('zii.widgets.CBreadcrumbs', array(
			'links'=>$this->breadcrumbs,
		)); ?><!-- breadcrumbs -->
	<?php endif?>
..

so you can see the "$this->menu " and "$this->breadcrumbs" are assigned in view files (create.php, index.php, admin.php ....) ;

in yii when some route be executed , lets say (default manner) : "user/create" , UserController::actionCreate function will be called , and in the actionCreate function it will render the create.php view file and using some specified layout file . so the execution order will be : create.php(view file) ----> column2.php(layout file)----> main.php(layout file) . all these file can refer to "$this" variable , so you can pass variable by defining some public var in the "Controller" class . and give it some value in view file then fetch it in layouts file . all above method may be the normal way :) ;

you may notice the file render order : view-->column2--->main ; these just the view files if look it further : webapp->userModule->..UserController--->actionCreate-->view--->column2...main.php . any point before the layout files can assign variable and fetch if from layout file . these execution points are in the same php thread and in same request scope . so you can use any global variable to pass value to the following point ($_GET, $_POST ,$_REQUEST, $_COOKIE; Yii::app()->params['xx']='value to be passed to following point '). in my opinion , do not use $_GET|$_POST|$_COOKIE to pass value , $_GET may affect url creation (Contorller::createUrl() or CWebApplication::createUrl()) , using $_POST|$_COOKIE are strange so i prefer use $_REQUEST (because they are in same "request" scope ) , you can freely use anther method to do that , such as a singleton Registry class :

class Registry /*extends ArrayObject*/
{
    /**
     * @var Registry
     */
    private static  $_instance;

    protected function __construct(){
        //parent::__construt(array(),ArrayObject::...);
    }

    protected function __clone(){
        parent::__clone();
    }

    /**
     * @var array
     *
     */
    private  $_store = array();

    /**
     * @static
     * @return Registry
     */
    public static function instance(){
       if(isset(self::$_instance)){
           return self::$_instance;
       }else{
           return self::$_instance =  new self();
       }
    }

    /**
     * @param $key
     * @param $value
     */
    public function set($key, $value){

        $this->_store[$key] =   $value;
    }

    /**
     * @param $key
     * @param null $default
     * @return null
     */
    public function get($key,$default = null){
      if(isset($this->_store[$key])){
          return $this->_store[$key];
      }else{
          return $default;

      }
    }

    /**
     * @param $key
     * @return bool
     */
    function isValid($key) {
        return  isset($this->_store[$key]) || array_key_exists($key,$this->_store) ;
    }
}

i have create a extension which will be used by some lazy people who don't want to define variables in "Controller" root class (include me :) ):

class ExtraAttribute extends CBehavior{
  private $_attributes;

 public function __get($name){
     try{
         return parent::__get($name);
     }catch(CException $e){
         if(isset($this->_attributes[$name])){
             return $this->_attributes[$name];
         }else{
             throw $e;
         }
     }
 }

    public function __set($name,$value){
        try{
           // echo __METHOD__;
            parent::_set($name,$value);
        }catch(CException $e){
            $this->_attributes[$name] = $value;
           // YiiUtil::dumpObject($this);
        }
    }

    public function canSetProperty($name){
        //echo __METHOD__;
        return true;
    }
    public function canGetProperty($name){
       // echo __METHOD__;
        if(parent::canGetProperty($name)){
            return true;
        }elseif(isset($this->_attributes[$name])){
            return true;
        }else{
           return false;
        }
    }

}

usage
in you base controller :

class Controller extends CController
{
    public function behaviors(){
        return array(
            'extraAttribute'=>array(
                'class'=>'application.components.ExtraAttribute',
            )
        );
    }

then you can pass any unPreDefined variable in you view file or you actionXXX method :

// actionXXX()  or someView file:
$this->anyVariableName = "someValue";

// in your layout file :
if(isset($this->anyVariableName)){
  // do by $this->anyVariableName value here ...
}

above is about how pass variables to layout file , sometimes the content box in layout file will be different for different views , and we don't want to define variable in "Controller" class , the CClipWidget class come to us just need you define some placeholders in you layout file :

// in your  layout file :
 if(isset($this->clips['someSpecMenu'])){

        echo $this->clips['someSpecMenu'];
      }


// and pass clips in some view file  or actionXXX method :

 $this->beginClip('someSpecMenu');
   // echo  any content block here ;
$this->endClip();

that's all ! hope help some body ;

here are some topic about it:

Yii - On what circumstances should we use clips?

Render Cgridview Pager Separately