在Yii2中运行行为代码之前获取控制器操作


Getting the controller action before behaviour code runs in Yii2

我正在尝试在Yii2控制器内执行一些代码,因为我需要模型中的一些代码在behaviors部分中可以访问,这样我就可以将模型作为参数传递,避免运行重复查询;然而,我也需要能够找出action的名称,但我运气不太好。

我尝试过使用beforeAction,但似乎在运行behaviours代码后运行,所以这对我没有帮助。

然后我尝试使用init,但此时action似乎无法通过$this->action->id使用。

一些示例代码:

class MyController extends Controller { 
    public $defaultAction = 'view';
    public function init() {
        // $this->action not available in here
    }
    public function beforeAction() {
        // This is of no use as this runs *after* the 'behaviors' method
    }
    public function behaviors() {
        return [
            'access' => [
                'class' => NewAccessControl::className(),
                'only' => ['view','example1','example2'],
                'rules' => [
                    [
                        'allow' => false,
                        'authManager' => [
                            'model' => $this->model,
                            'other_param' => $foo,
                            'other_param' => $bar,
                        ],
                        'actions' => ['view'],
                    ],
                    // everything else is denied
                ],
            ],
        ];
    }
    public function viewAction() {
        // This is how it is currently instantiated, but we want to instantiate *before* the behavior code is run so we don't need to instantiate it twice
        // but to be able to do that we need to know the action so we can pass in the correct scenario
        $model = new exampleModel(['scenario' => 'view']);
    }
}

authManager只是对AccessRule类的扩展中的member variable的引用。

有没有我能做到的?

好吧,如果我说得对,你正在寻找这样的东西:

public function behaviors()
{
    $model = MyModel::find()->someQuery();
    $action = Yii::$app->controller->action->id;
    return [
         'someBehavior' => [
             'class' => 'behavior/namespace/class',
             'callback' => function() use ($model, $action) {
                 //some logic here
             }
         ]
    ];
}

因为behaviors()只是一个方法,您可以声明任何变量并在其中添加任何所需的逻辑,所以您必须遵循的唯一约定是返回类型必须是数组

如果使用自定义行为,则可以使用events()方法,在该方法中可以将行为的方法绑定到某些事件。例如

class MyBehavior extends Behavior
{
    public function events()
    {
        return [
            'yii'web'User::EVENT_AFTER_LOGIN => 'myAfterLoginEvent',
        ];
    }
    public function myAfterLoginEvent($event)
    {
        //dealing with event
    }
}

在本例中,myAfterLoginEvent将在用户成功登录应用程序后执行。$event变量将由框架传递,并且根据事件类型,它将包含不同的数据。阅读事件对象

更新:

正如我现在所看到的,我的答案是关于事件和行为的更一般的。现在,当您添加代码时,我可以建议您用以下代码覆盖行为的beforeAction($action)方法:

public function beforeAction($action)
{
    $actionID = $action->id;
    /* @var $rule AccessRule */
    foreach ($this->rules as &$rule) {
        $model = &$rule->authManager['model'];
        //now set model scenario maybe like this
        $model->scenario = $actionID;
    }
    //now call parent implementation 
    parent::beforeAction($action);
}

还要看一下beforeAction方法的AccessControl实现,它为每个规则调用allows方法,并将当前操作作为参数传递给它。因此,如果您有扩展AccessRule的类,您可以覆盖allows($action,$user,$request)方法或matchCustom($action)方法来设置适当的模型场景。希望这会有所帮助。

还有一种选择:

超驰控制器的CCD_ 20方法。这里$id是actionID-正是您所需要的。检查id,设置适当的模型场景并调用parent::runAction($id, $params);