I know there are different ways to choose layout file. It can be done
Yii::$app->layout = \'...\'
It could be set in an application event:
$config = [
'id' => 'my-app',
'basePath' => dirname(__DIR__),
'params' => ...
'bootstrap' => ['log'],
'components' => [
...
'errorHandler' => [
'errorAction' => 'site/error',
],
...
],
'on beforeAction' => function (yii\base\ActionEvent $e) {
if ($e->action->id === 'error' && $e->action->controller->id === 'site') {
// this is the second step
if (isset(Yii::$app->params['requested-layout']))
$e->action->controller->layout = Yii::$app->params['requested-layout'];
} else {
// this happens first
Yii::$app->params['requested-layout'] = $e->action->controller->layout;
}
},
];
What happens: Note that beforeAction gets called twice if an exception gets thrown in an action. The second call is caused by the error handler. Now, on each reqular request the current layout gets stored somewhere (I used the globally available Yii params for this). If then for some reason the error handler needs to be called the layout file for the containing error action controller gets adopted to the stored layout file.
Open issues:
Yii::$app->params['requested-layout']
will be set only for existing actions and controllers (and modules) and exceptions that gets thrown within action methods.site/error
without executing it before. So the layout file does not gets adopted (could be solved with an implementation for 'on beforeRequest'
) and the default layout will be used (can be really default or could be set in different ways (e.g. $layout property of the SiteController). I think the module could be considered as well easily with extending this line (not tested):
Yii::$app->params['requested-layout'] = $e->action->controller->layout ?:
$this->action->controller->module->layout;
Let me know if this makes sense or is wrong.
One way could be to set the the layout file for the application in beforeAction
in each controller:
public function beforeAction($action) {
Yii::$app->layout = $this->layout;
return parent::beforeAction($action);
}
It could be optimized by putting this in an extra controller from which all other controllers inherit.
But I don't really like this solution. Seems to me like a workaround. Also this does not work if the SiteController defines a layout by itself (as $layout
property or within a method).