Search code examples
yii2yii2-modelyii2-validation

Yii2 unique validator ignored


In the rules() of my RegisterForm model:

[ 'user_username', 'unique', 'targetClass' => 'app\models\User', 'message' => 'This username is already been taken.' ],

In my controller:

$model = new RegisterForm();
if ( $model->load( Yii::$app->request->post() ) ) {
    if ( $user = $model->register() ) {
        return $this->redirect( [ '/login' ] );
    }
}

In RegisterForm:

public function register() {  
    $user = new User();
    $user->user_firstname = $this->user_firstname;
    $user->user_lastname = $this->user_lastname;
    $user->user_username = $this->user_username;
    $user->user_email = $this->user_email;
    $user->setPassword( $this->user_password );

    if ( !$user->validate() ) {
        return null;
    }    

    if ( $user->save() ) {
        return $user;   
    }

    return null;
}

Form:

<?php $form = ActiveForm::begin(); ?>

<?= $form->field( $model, 'user_firstname' )->textInput( [ 'maxlength' => true ] ) ?>

<?= $form->field( $model, 'user_lastname' )->textInput( [ 'maxlength' => true ] ) ?>

<?= $form->field( $model, 'user_username' )->textInput( [ 'maxlength' => true ] ) ?>

<?= $form->field( $model, 'user_email' )->textInput( [ 'maxlength' => true ] ) ?>

<?= $form->field( $model, 'user_password' )->passwordInput() ?>

<?= $form->field( $model, 'user_password_repeat' )->passwordInput() ?>

<?= Html::submitButton( 'Register', [ 'class' => 'btn btn-primary', 'name' => 'register-button' ] ) ?>

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

Yet when I enter a username that I know already exists, the error never comes up and the record tries to save, though I get: Integrity constraint violation: 1062 Duplicate entry ...

EDIT: if I add the unique rule to the User model itself the form will not submit if I input a username that exists, the errors just don't show up


Solution

  • Like I have suspected, you are not checking for unique user_username attribute in client side. The reason why it's not working it's because you are not sending Ajax requests to check the results from database. Unlike other rules, unique rule requires additional Ajax requests to server since it would be pretty bad thing if Javascript would retrieve all currently registered username and store somewhere in Client side.

    To solve you problem, in form write something like this:

    $form = ActiveForm::begin([
        'enableAjaxValidation' => true,
        'validationUrl' => [<URL HERE>],
    ]);
    

    Now you have to create a method (action) in controller that returns validation (not just unique, all of them) back to ActiveForm. So it could be something like this:

    public function actionAjaxValidation()
    {
        $post = Yii::$app->request->post();
        $model = new YourClass();
    
        if (!$model->load($post)) {
            throw new HttpException(403, 'Cannot load model');
        }
    
        $array = ActiveForm::validate($model);
    
        return json_encode($array);
    }