Yii2:ActiveForm:在一个字段上组合规则/多重验证

Yii2: ActiveForm: combine rules / multiple validation on one field

登录表单:

public function rules()
{
    return [
        // username and password are both required
        [['username', 'password'], 'required'],
        // username should be a number and of 8 digits
        [['username'], 'number', 'message'=>'{attribute} must be a number'],
        [['username'], 'string', 'length' => 8],
        // password is validated by validatePassword()
        ['password', 'validatePassword'],
    ];
}

/**
 * Validates the password.
 * This method serves as the inline validation for password.
 *
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
public function validatePassword($attribute, $params)
{
    if (!$this->hasErrors()) {
        $user = $this->getUser();
        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError($attribute, 'Incorrect username or password.');
        }
    }
}

如上所示,我为同一字段设置了 2 条规则:

[['username'], 'number', 'message'=>'{attribute} must be a number'],
[['username'], 'string', 'length' => 8],

我希望表单针对以下 3 场景 情况显示不同的错误消息:

  1. 提供的值既不是数字,也不是 8 个字符(数字)。
  2. 提供的值是一个数字,但不是 8 个字符(数字)。
  3. 提供的值不是数字,而是 8 个字符(数字)。

我的问题是2折:

一个。有没有办法以任何标准 Yii2 方式组合这些规则。
B. 我试图设置一个自定义验证器(解决这个问题的明显方法),但它被忽略了。使它生效的唯一方法是将 username 字段添加到场景中。但是,一旦我也添加 password ,它又被忽略了。您能想到任何原因吗? 编辑skipOnError = false对此行为没有任何改变。

所以,请您在回答时确保最好在yii2/advanced中进行测试;我几乎没有接触过默认设置,所以应该很容易测试。

编辑:为清楚起见,我只想允许 8 个字符(数字)的数字,因此它们可能有前导 0,例如. 00000001,或 00000000。这就是为什么它必须是数字字符串。

Yii2 忽略您的验证规则可能是因为您不仅复制了属性,还复制了类型。 通过数字验证,我认为您应该使用 min/max 选项来验证数字长度。

对于这种情况:

'min'=>10000000,'max'=>99999999

你终于需要这个了:

  • 该值是必需的
  • 该值必须是 8 个字符的字符串
  • 该值只能包含数字

所以你应该试试 :

['username', 'required'],
['username', 'string', 'min' => 8, 'max' => 8],
['username', 'match', 'pattern' => '/^[0-9]{8}$/', 'message'=>'{attribute} must be a number'],

针对不同情况组合规则和显示自定义错误消息的最佳方法是创建自定义验证器。现在,如果你希望它也能在客户端工作(这是我在上面的问题 B 中详述的问题之一,感谢 @Beowulfenator 在这方面的领导),你必须创建从 yii2 本机验证器 class.

扩展而来的实际自定义验证器 class

这是一个例子:

CustomValidator.php

<?php

namespace app\components\validators;

use Yii;
use yii\validators\Validator;

class CustomValidator extends Validator
{
    public function init() {
        parent::init();
    }

    public function validateAttribute($model, $attribute) {
        $model->addError($attribute, $attribute.' message');
    }

    public function clientValidateAttribute($model, $attribute, $view)
    {
return <<<JS
messages.push('$attribute message');
JS;
    }
}

LoginForm.php

<?php
namespace common\models;

use Yii;
use yii\base\Model;
use app\components\validators\CustomValidator;

/**
 * Login form
 */
class LoginForm extends Model
{
    public $username;
    public $password;
    public $custom;

    private $_user;


    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // username should be a number and of 8 digits
            [['username'], 'number', 'message'=>'{attribute} must be a number'],
            [['username'], 'string', 'length' => 8],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
            ['custom', CustomValidator::className()],
        ];
    }

    // ...

login.php

<?php

/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \common\models\LoginForm */

use yii\helpers\Html;
use yii\bootstrap\ActiveForm;

$this->title = 'Login';
?>
<div class="site-login text-center">
    <h1><?php echo Yii::$app->name; ?></h1>

    <?php $form = ActiveForm::begin([
        'id' => 'login-form',
        'fieldConfig' => ['template' => "{label}\n{input}"],
        'enableClientValidation' => true,
        'validateOnSubmit' => true,
    ]); ?>

    <?= $form->errorSummary($model, ['header'=>'']) ?>

    <div class="row">
        <div class="col-lg-4 col-lg-offset-4">
            <div class="col-lg-10 col-lg-offset-1">

                    <div style="margin-top:40px">
                        <?= $form->field($model, 'username') ?>
                    </div>

                    <div>
                        <?= $form->field($model, 'password')->passwordInput() ?>
                    </div>

                    <div>
                        <?= $form->field($model, 'custom') ?>
                    </div>

                    <div class="form-group" style="margin-top:40px">
                        <?= Html::submitButton('Login', ['class' => 'btn btn-default', 'name' => 'login-button']) ?>
                    </div>

            </div>
        </div>
    </div>

    <?php ActiveForm::end(); ?>

</div>