Advanced/backend: ActiveForm: custom validator ignored

Suppose I have the following set up for my LoginForm rules:




    public function rules()

    {

        return [

            // username and password are both required

            [['username', 'password'], 'required'],

            // username should be a number and of 8 digits

            ['username', 'is8NumbersOnly'],

            // password is validated by validatePassword()

            ['password', 'validatePassword'],

        ];

    }


    public function is8NumbersOnly($attribute)

    {

        if (!preg_match('/^[0-9]{8}$/', $this->$attribute)) {

            $this->addError($attribute, 'Must contain exactly 8 digits.');

        }

    }


    // The following is default stuff in Yii2

    public function validatePassword($attribute, $params)

    {

        if (!$this->hasErrors()) {

            $user = $this->getUser();

            if (!$user || !$user->validatePassword($this->password)) {

                $this->addError($attribute, 'Incorrect username or password.');

            }

        }

    }



Note that pretty much the only thing I changed compared to the original advanced/backend template is adding the new custom validator [font="Courier New"]is8NumbersOnly[/font].

My problem is that when I try to submit the login form, it simply ignores whatever I put into the [font="Courier New"]username[/font] field, except of course that it has to have a value (controlled by [font="Courier New"]required[/font]).

I tried using [font=“Courier New”]skipOnEmpty = false[/font], but that didn’t change anything. Implementing a scenario however made it work, but only if the only field included in the scenario was the [font=“Courier New”]username[/font] field. Once I added the [font=“Courier New”]password[/font] field too, the new custom validator [font=“Courier New”]is8NumbersOnly[/font] stopped working (no validation has been made against it anymore, ie. anything passed).

Any specific reason for this?

I now understand that this is because customValidators set up this way will not trigger on client-side unless a separate custom Validator class is created.

Sample code:

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>



Hope this helps someone ;)