Howard Panton
Howard Panton

Reputation: 267

Yii2 framework - Email Validation in Ajax Form

I'm having an issue validation whether a submitted Email Address is Unique in the database. When the User registers I need to validate whether the email address exists all of the other validation is working fine.

Is there a step missing when you are validating a using an Ajax form in Yii 2.

A User clicks on CTA to register on site/index

use yii\bootstrap\Modal;
use frontend\models\Register;
use yii\helpers\Html;
use yii\helpers\Url;

...

Modal::begin([
    'id' => 'modal',
    'size'=>'modal-lg',
    'clientOptions' => ['backdrop' => 'static', 'keyboard' => FALSE],
]);

echo "<div id='modalContent'></div>";

Modal::end();
?>
 <?= Html::button('Register', ['value' => Url::to(['register/create']), 'title' => 'Register', 'class' => 'btn btn-success','id'=>'modalButton']); ?>

This opens up a modal (register/create)

Model Register

class Register extends User
{

...

    public function rules()
{
    return [

        ['Email', 'filter', 'filter' => 'trim'],
        ['Email', 'required'],
        ['Email', 'email'],
        ['Email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],

    ];
}

    public function signup()
{
    $user = new User();
    if ($this->validate()) {
        $user->Email            = $this->Email;
        if ($user->save()) {
            return $user;
        }
    } else {
        return;
    }
}

Register Controller

    public function actionCreate()
{
    $model = new Register(['scenario' => 'signup']);
    if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
        Yii::$app->response->format = Response::FORMAT_JSON;
        Yii::error($model);
        return $model->validate();
    }
    if ($model->load(Yii::$app->request->post())) {
        if ($user = $model->signup()) {
            if (Yii::$app->getUser()->login($user)) {
                return $this->goHome();
            }
        }
    }
    return $this->renderAjax('create', [
        'model' => $model,
    ]);
}

The View file

<?php $form = ActiveForm::begin(['id'=> 'register', 'enableClientValidation'=>true, 'enableAjaxValidation'=>true, 'validateOnChange'=> true, 'validationUrl' => Url::to(['register/create'])]  ); ?>
 <div class="form-group">
            <?= $form->field($model, 'Email') ?>
 </div>

Javascript file

$script = <<< JS

$('body').on('beforeSubmit', 'form#register', function (event, jqXHR, settings) {
     var form = $(this);
     // return false if form still have some validation errors
     if (form.find('.has-error').length) {
          return false;
     }
     // submit form
     $.ajax({
          url: form.attr('action'),
          type: 'GET',
          data: form.serialize(),
          success: function (response) {
               // do something with response

               $(document).find('#modal').modal('hide');
          }
     });
     return false;
});


JS;
$this->registerJs($script);

Upvotes: 1

Views: 2888

Answers (2)

CICSolutions
CICSolutions

Reputation: 1053

I was facing a similar issue where my email field was not triggering the "unique" rule in the javascript validation, but the "unique" rule was getting triggered on the form submit.

It's awesome that you came up with a solution for your question that gets the job done, but I think what I just learned could also shed some insight on this question for others.

The key for me was learning/realizing that the "unique" validation rule must use a database lookup to verify if the user input is unique, thus it must use ajax. Other validation rules, such as "required" or "email" don't need ajax to validate. They just use javascript in the client to validate, so they are client validation, whereas the "unique" validator is actually ajax validation.

Note: my code below is not really addressing your code directly, but is intended to communicate the overall understanding and code structure that is needed to answer your question. Some of these steps you already have, but some are missing :)

First, you need a model with a field that requires a 'unique' rule, such as an email address for a user account.

In your Model

public function rules()
{
    $rules = [
        [['email'], 'unique'],

        // ... and all your other rules ...
    ];
}

When using \yii\widgets\ActiveForm, client validation is enabled by default, but ajax validation is not.

So, next you need to directly turn on ajax validation in the view. This can be done either for the entire form, or just for a single field. The Yii2 Validation docs explain this best, but in my case, I chose to just enable ajax validation on my email field.

In your View

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

<?= $form->field($user, 'email', ['enableAjaxValidation' => true])->textInput();

//... other form fields and such here ...

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

Next, you also need to handle the ajax validation in your controller, so your controller method could look something like this:

public function actionRegister()
{
    $user = new User();

    $post = Yii::$app->request->post();
    $userLoaded = $user->load($post);

    // validate for ajax request
    if (Yii::$app->request->isAjax) {
        Yii::$app->response->format = Response::FORMAT_JSON;
        return ActiveForm::validate($user);
    }

    // vaidate for normal request
    if ($userLoaded && $user->validate()) {
        $user->save();
        return $this->redirect(['view', 'id' => $user->id]);
    }

    // render
    return $this->render('create', ['user' => $user]);
}

And then here's the catch ... everything above is what you would need when working with a normal (non-ajax) form. In your question, you are working on a form in a modal window that is being submit via ajax, so the above controller method will not work. With ajax forms, it becomes pretty tricky to handle the ajax form validation and the ajax form submit in the same controller method.

As usual, Yii has this all figured out for us, and the validationUrl form parameter will save the day. All you have to do is create a new method in your controller that is specifically for ajax validation, and reference the controller/action URL in your form. Something like this should do the trick:

In your View

<?php $form = ActiveForm::begin([
    'id' => 'form-id', // always good to set a form id, especially when working with ajax/pjax forms
    'validationUrl' => ['user/validate-email'], //['controller/action'],
]); ?>

In your Controller

public function actionRegister()
{
    $user = new User();

    $post = Yii::$app->request->post();

    // vaidate for normal request
    if ($user->load($post) && $user->validate()) {
        $user->save();
        return $this->redirect(['view', 'id' => $user->id]);
    }

    // render
    return $this->render('create', ['user' => $user]);
}

public function actionValidateEmail()
{
    // validate for ajax request
    if (Yii::$app->request->isAjax) {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $user = new User();
        $post = Yii::$app->request->post();
        $user->load($post);

        return ActiveForm::validate($user);
    }       
}

Cheers!

Upvotes: 1

Howard Panton
Howard Panton

Reputation: 267

I managed to solve this myself by using Javascript to make an Ajax request and PHP to receive the quest and check whether the Email already exists in the database.

    function checkEmail(){
    var email_check;
    // Get the value of the email input field
    var input_value = document.getElementById('register-email').value;
    // Send the value to a PHP page to check
    $.ajax({
        url: 'checkemail.php/', 
        type: 'POST',
        data: {
        email_check: input_value
    },
        success: function(response) {
            // If we have a repsonse we need to check whether it is True or False
            email_check = response;
            if (email_check == 1) {
                // If True add error class
                $('.field-register-email').addClass('has-error');
                $('.field-register-email .help-block').text('The email supplied has already been used');
            } else {
                $('.field-register-email').removeClass('has-error');
                $('.field-register-email .help-block').text(' ');
            }
        }
    });
};

This will send a POST request to the checkemail.php which will check whether the email address is in the database

Upvotes: 0

Related Questions