pass variables or content block from view file to layout file

You are viewing revision #2 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.

next (#3) »

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(<>) ----> column2.php(<>)----> main.php(<>) . 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;
        }
    }

}

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