default layout in CWebModule

Hi all,

I’ve just started working with Yii nad I’ve found something that doesn’t work like I expected after reading the manual.

I’ve got an AdminModule, extending CWebModule in modules/admin/AdminModule.php

I’ve got a layout: modules/admin/views/layout/main.php

I want all controllers in this module to use this layout. So I’ve added the following property in the class AdminModule (and didn’t add anything to the DefaultController):

public $layout = "main";

When I open index.php?r=admin, the default layout from Yii is used.

However, if I set the property mentioned above in the DefaultController in modules/admin/controllers/DefaultController.php, the correct layout is used.

The documentation about CWebModel->layout is:

the layout that is shared by the controllers inside this module. If a controller has explicitly declared its own layout, this property will be ignored. If this is null (default), the application’s layout or the parent module’s layout (if available) will be used. If this is false, then no layout will be used.

PS: adding $this->layout = “main” in the public function init() from the AdminModule doesn’t work either.

Have I misinterpreted the usage of the layout property in the CWebModel?

You need to set the layout path by using

Then set the layout

Please note that

By using

// in the layout will points to protected/views/layouts

using / to use the modules/views/layouts

while using one layout in another layout

Thank you very much, setting the layoutPath in the Module works!

I don’t understand why it works without setting the layoutPath if I define the layout in the Controller, but I’m happy it now works as intended.

Another solution is to edit the $layout declaration in your "protected/components/Controller.php" file to read as follows:


$layout = "column1";

instead of…


$layout = "//layout/column1";

Then your $this->layout declaration in init() in the AdminModule.php file will work.

Like PeRoChAk mentioned in his post, the double forward slash (//) points to protected/views/layouts because you are extending the protected/components/Controller.php class file and in that case it takes protected/views/layouts as the default path to your views instead of the module’s view directory.

I guess you can set $this->layoutPath to the module’s view path but when I tried it for some reason it could not find the path. If setting $this->layoutPath works for you then PeRoChAk’s suggestion is better and cleaner in my opinion. In that case I would recommend going with his advice.

Anyway, here’s where I found the solution: code.google.com/p/yii/issues/detail?id=1043

Hope this helps a little.

Gii does not generate default layouts for modules, so I just copy them over from the main application and remove the extra slash:




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



to:




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



The ONLY solution that I have found that works with the latest Yii is to put the following in your module DefaultController.php:




    public $layout = "column2";



I have looked at a lot of posts and alternative solutions, but this is the only one that works.

I believe its the correct solution, because class DefaultController extends Controller :)

Thanks, this really works.

Hey,

I have tried to solutions above, but it didn’t work for me (yii framework v1.1.8.)

After removing the extra slash, the main layout was not found anymore.

So, the solution was changing the path in the layout:


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

[font="Arial"]to[/font]


<?php $this->beginContent('admin.views.layouts.main'); ?>

[font="Arial"]

where admin is the name of my module.

In the controller file, I also needed to add:[/font]


  public $layout='/layouts/column1'; 

This solution worked Yii 1.1.9

in column2.php




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

and in DefaultController of module


 

public $layout='/layouts/column2';

I still can’t get it to work. In my case it looks like this. I’ve only one controller in main app “protected/controllers/SiteController” and one module called “admin” with “protected/modules/admin/controllers/DefaultController”. I’ve set “errorAction” for “errorHandler” in protected/config/main.php to “site/error” and this view is being rendered for every error in my app (SiteController and admin module controllers errors) but I want to set diffrent errorHandler for module errors.

I use diffrent layout for "protected/controllers/SiteController":

  • SiteController $layout is set to:

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

and "protected/modules/admin/controllers/DefaultController":

  • DefaultController $layout is set to:

public $layout = '/layouts/column1';

  • "protected/modules/admin/views/layouts/column1" beginContent arg is: "/layouts/main".

  • admin module init() looks like this:


public function init()

	{

		$this->setImport(array(

			'admin.models.*',

			'admin.components.*',

		));

		

		$this->setComponents(array(			

			'errorHandler'=>array(

				'errorAction'=>'admin/default/error',

			),

			

			'user' => array(          

                'class' => 'CWebUser',

				'allowAutoLogin' => true,

				'loginUrl' => Yii::app()->createUrl('admin/default/login'),

            )

        ));

}

The problem is:

  • if I access www.domain.pl/site/xyz - error view is rendered in "protected/views/layouts/main"

  • if I access www.domain.pl/admin/xyz - is also rendered in "protected/views/layouts/main" rather than "protected/modules/admin/views/layouts/column1".

Am I doing something wrong?

UDPATE:

The problem was in the init() function it seems that setting components the way that I’ve done it isn’t working. New init looks like this:


Yii::app()->setComponents(array(

			'errorHandler'=>array(

				'errorAction'=>'admin/default/error',

			)

        ));

setComponents() should be called for Yii::app() not module. Hope it helps somebody else with similar problem.

I think this is suitable answer for future readers. However is late.

In CController as default public $layout property is commented. and when we extends a Controller from CController, the Controller have not a property named $layout.

Hence when we declare $layout in a class extended from CWebModule for defining default layout for all Controllers, this property have not effect on controllers.

So for defining default layout for all module controllers we need declare the $layout property in the controller. With doing this we can simply define default layout for all controllers. I hope that is helpful.

I found a cleaner solution (well, at least for me).

Firstly, I used Gii to create my Admin module and my module’s controllers. It will result a set of controller extending from Controller class (protected/components/Controller.php) -> The problem is here.

As mentioned in the documentation, CWebModule::layout will be used for all controllers within the module, UNLESS the controller wants to use its own layout (by declaring $this->layout).

So, all my controllers of the module inherits $layout property from Controller class, that’s why CWebModule::layout is not used.

I was thinking of 2 solutions:

SOLUTION 1

[list=1]

[*]Create an AdminController (modules/admin/components/AdminController.php), extending from CController, with $layout = <your admin layout>;

[*]All of your module’s controllers should extend from this class.

[*]Remove all $layout property in your module controller (as Gii automatically created it);

[/list]

This makes things quite clear and structural: All your front-end controller is using another controller base (Controller), and your module is using another one (AdminController).

SOLUTION 2

[list=1]

[*]In your AdminModule::init(), specify your admin layout path.


$this->setLayoutPath(Yii::getPathOfAlias('admin.views.layouts'));

[*]In your Controller class, change $layout to ‘main’:


public $layout = 'main';

This will tell the controller to look for the main.php file in the specified layoutPath

[*]Now you can use 2 layouts separately.

  • Your front-end layout: views/layouts/main.php

  • Your admin module layout: modules/admin/views/layouts/main.php

Remember that the filename should be the same.

[*]Remove all $layout property in your module controller (as Gii automatically created it);

[*]If you want to use column1/2, you need to change the parameter of beginContent method.

[/list]

There might be limitations to these solutions, but it fits my need, and in any chance it could fit someone’s :)