Yii2 根据条件添加规则

Yii2 add rule based on condition

我正在使用 PHP Yii2 框架和 dektrium\yii2-user 用户身份验证扩展构建网站。

如果登录失败次数超过 3 次,我想向用户询问验证码,但默认情况下扩展不支持此功能。

现在,我已经覆盖了扩展程序的用户和登录表单模型,并添加了所需的字段和检查。但是,我不知道如何添加一条规则,使验证码仅在第四次尝试时才需要。

是否可以动态添加规则?我在下面显示了一个简化的代码视图以及我需要帮助的评论。我会写函数,只需要注释部分的帮助。

<?php

namespace app\models\dektrium\user;


class LoginForm extends \dektrium\user\models\LoginForm
{
    public $captcha;
    public $need_captcha;

    public function rules() {
        $rules = parent::rules();

        //This is how you'd normally add a rule, but this will require it for every login
        //The following rule should be added from the login()
        $rules[] = ['captcha', 'captcha', 'message' => 'Too many attempts. Captcha required.'];
        $rules[] = ['need_captcha', 'boolean'];

        return $rules;
    }

    public function login() {
        $success = false;
        $requireCaptcha = false;

        if ($this->validate() && $this->user) {
            if ($this->user->login_attempts > 3) {
                //add rule here to require captcha

                $requireCaptcha = true;
            }

            $success = !$requireCaptcha && $this->validateCaptcha() && $this->validateLogin();

            if ($success) {
                $this->user->updateAttributes(['last_login_at' => time()]);
            }
        }

        return $success;
    }
}

?>

编辑:

如果有一个'optional'参数与'required'相反,那就足够了。我可以在我的 login() 中检查验证码。

编辑 2:

我尝试使用如下场景,但在进行验证之前,模型没有在控制器操作中加载验证码值。

<?php

namespace app\models\dektrium\user;


class LoginForm extends \dektrium\user\models\LoginForm
{
    public $captcha;
    public $need_captcha;
    public $login_count;

    public function rules() {
        $rules = parent::rules();

        $rules[] = ['captcha', 'captcha', 'message' => 'Too many attempts. Captcha required.', 'on' => ['required_captcha']];
        $rules[] = ['need_captcha', 'boolean'];
        $rules[] = ['login_count', 'integer'];

        return $rules;
    }

    public function login() {
        $this->user = $this->finder->findUserByUsernameOrEmail(trim($this->login));

        if($this->user && $this->user->login_count > 3) {
            $this->scenario = 'required_captcha';
            $this->need_captcha = true;
        }

        $success = parent::login();

        if ($success) {
            $this->user->login_count = 0;
            $this->user->save();
        } else {
            $this->login_count++;

            if ($this->user) {
                $this->user->login_count++;
                $this->user->save();
            }
        }

        if ($this->login_count > 3) {
            $this->scenario = 'required_captcha';
            $this->need_captcha = true;
        }

        return $success;
    }
}

这就是我最终这样做的方式...

我使用了基于场景的规则,在控制器操作中,我在需要验证码的条件为真时设置了场景。实际上,控制器也是任意扩展的,所以我不得不做一些支持的控制器映射,并通过事件设置场景。

我第一次尝试这样做失败了,因为我在验证功能期间设置了场景,但应该在这样做之前设置它,它起作用了。

<?php

namespace app\models\dektrium\user;


class LoginForm extends \dektrium\user\models\LoginForm
{
    public $captcha;
    public $need_captcha;
    public $login_count;

    public function rules() {
        $rules = parent::rules();

        $rules[] = ['captcha', 'required', 'on' => ['use_captcha']];
        $rules[] = ['captcha', 'captcha', 'on' => ['use_captcha']];
        $rules[] = ['need_captcha', 'boolean'];
        $rules[] = ['login_count', 'integer'];

        return $rules;
    }

    public function login() {
        $success = true;
        $this->user = $this->finder->findUserByUsernameOrEmail(trim($this->login));

        if(!$this->need_captcha && $this->user && $this->user->login_count > 3) {
            $this->need_captcha = true;
            $success = false;
        }

        $success = $success && parent::login();

        if ($success) {
            $this->user->login_count = 0;
            $this->user->save();
        } else {
            $this->login_count++;

            if ($this->user) {
                $this->user->login_count++;
                $this->user->save();
            }

            if ($this->login_count > 2)
                $this->need_captcha = true;
        }

        return $success;
    }
}

使用 ['field', 'required', 'when' => function($model) { return your_true_condition; } 设置验证规则时。

条件可以是 session/cookie 中的某个值,比如每次递增的整数