Set the layout file for error action dynamically

后端 未结 2 1684
野性不改
野性不改 2021-01-24 20:09

I know there are different ways to choose layout file. It can be done

  • in the configuration
  • with Yii::$app->layout = \'...\'
  • with
相关标签:
2条回答
  • 2021-01-24 20:56

    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:

    • Modules are not taken into account. Would require some more logic.
    • 'on beforeAction' gets called and Yii::$app->params['requested-layout'] will be set only for existing actions and controllers (and modules) and exceptions that gets thrown within action methods.
    • If the route is invalid or another error occurres the beforeAction handler will be called directly for 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).
    • More logic could be required if the layout files are not in one single folder (defined in Application::$layoutFiles).

    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.

    0 讨论(0)
  • 2021-01-24 20:56

    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).

    0 讨论(0)
提交回复
热议问题