Laravel User Authentication with Ajax Validation

User Validation Controller

Create a new Validation folder in Controllers

mkdir app/Http/Controllers/Validation

Create a new controller: app/Http/Controllers/Validation/UserController.php in the new Validation folder. It has a single method that applies the rules from the User model for validation.

UserController.php
<?php

namespace App\Http\Controllers\Validation;

use App\Models\User;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

use App\Http\Requests;

class UserController extends Controller
{
    public function user(Request $request)
    {
        $this->validate($request, (new User)->rules());
    }
}

Routes

Edit the app/Http/routes.php file to register a route group using api middleware. Inside this group add a validate/user route that accepts a POST method. The request is then sent to the new Validation\UserController user method for validation.

routes.php
...

Route::get('/', function () {
    return view('welcome');
});

Route::group(['middleware' => ['api']], function () {
    Route::post('/validate/user', [
        'uses' => 'Validation\UserController@user',
    ]);
});

Route::auth();

Route::get('/home', 'HomeController@index');

Register Form Template

Edit the user registration form template, resources/views/auth/register.blade.php. Remove all the blade if statements that surround the span help blocks. Each of these blocks need to be rendered by the server and made available to the javascript to display any errors. For example:

REPLACE
@if ($errors->has('name'))
    <span class="help-block">
        <strong>{{ $errors->first('name') }}</strong>
    </span>
@endif
WITH
<span class="help-block">
    <strong>{{ $errors->first('name') }}</strong>
</span>

The form should look like this after the if statements have been removed from around the span help blocks.

register.blade.php
...

<form class="form-horizontal" role="form" method="POST" action="{{ url('/register') }}">
    {{ csrf_field() }}

    <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
        <label class="col-md-4 control-label">Name</label>

        <div class="col-md-6">
            <input type="text" class="form-control" name="name" value="{{ old('name') }}">
            <span class="help-block">
                <strong>{{ $errors->first('name') }}</strong>
            </span>
        </div>
    </div>

    <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
        <label class="col-md-4 control-label">E-Mail Address</label>

        <div class="col-md-6">
            <input type="email" class="form-control" name="email" value="{{ old('email') }}">
            <span class="help-block">
                <strong>{{ $errors->first('email') }}</strong>
            </span>
        </div>
    </div>

    <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
        <label class="col-md-4 control-label">Password</label>

        <div class="col-md-6">
            <input type="password" class="form-control" name="password">
            <span class="help-block">
                <strong>{{ $errors->first('password') }}</strong>
            </span>
        </div>
    </div>

    <div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
        <label class="col-md-4 control-label">Confirm Password</label>

        <div class="col-md-6">
            <input type="password" class="form-control" name="password_confirmation">
            <span class="help-block">
                <strong>{{ $errors->first('password_confirmation') }}</strong>
            </span>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-6 col-md-offset-4">
            <button type="submit" class="btn btn-primary">
                <i class="fa fa-btn fa-user"></i>Register
            </button>
        </div>
    </div>
</form>

...

JavaScript

For this tutorial, just going to inline the JavaScript to handle the Ajax at the bottom of the base layout view. Creating .js modules in the resources/assets folder and using elixer to deploy them to a public folder is a topic for another tutorial.

Edit the base layout template resources/views/layouts/app.blade.php. Near the top of the file add a meta tag to make the csrf-token available for the JavaScript ajax function.

app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    ...

  • Instead of using a meta tag to store the token for the ajax request header, retrieve it from the hidden _token input value inside the form. This input is added using the {{ csrf_field() }} blade template helper.

At the bottom of the app.blade base layout file, before the closing body tag, add the JavaScript.

app.blade.php
    ...

    <script>
    $(function() {

        var app = {
            DOM: {},
            init: function () {

                // only applies to register form
                if (window.location.pathname == '/register') {

                    this.DOM.form = $('form');
                    this.DOM.form.name  = this.DOM.form.find('input[name="name"]');
                    this.DOM.form.email = this.DOM.form.find('input[name="email"]');
                    this.DOM.form.pwd   = this.DOM.form.find('input[name="password"]');
                    this.DOM.form.pwdc  = this.DOM.form.find('input[name="password_confirmation"]');

                    this.DOM.form.name.group = this.DOM.form.name.closest('.form-group');
                    this.DOM.form.email.group = this.DOM.form.email.closest('.form-group');
                    this.DOM.form.pwd.group = this.DOM.form.pwd.closest('.form-group');

                    this.DOM.form.submit( function(e) {
                        e.preventDefault();

                        var self = this; // native form object

                        error = {};

                        app.DOM.form.name.group.find('strong').text('');
                        app.DOM.form.email.group.find('strong').text('');
                        app.DOM.form.pwd.group.find('strong').text('');

                        app.DOM.form.name.group.removeClass('has-error');
                        app.DOM.form.email.group.removeClass('has-error');
                        app.DOM.form.pwd.group.removeClass('has-error');

                        var user = {};
                        user.name = app.DOM.form.name.val();
                        user.email = app.DOM.form.email.val();
                        user.password = app.DOM.form.pwd.val();
                        user.password_confirmation = app.DOM.form.pwdc.val();

                        var request = $.ajax({
                            headers: {
                                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                            },
                            url: '/validate/user',
                            type: 'POST',
                            contentType: 'application/json',
                            data: JSON.stringify(user)
                        });
                        request.done( function(data)
                        {
                            // native form submit
                            self.submit();
                        });
                        request.fail( function(jqXHR)
                        {
                            error = jqXHR.responseJSON;
                            if (error.name) {
                                app.DOM.form.name.group.find('strong').text(error.name[0]);
                                app.DOM.form.name.group.addClass('has-error');
                            }
                            if (error.email) {
                                app.DOM.form.email.group.find('strong').text(error.email[0]);
                                app.DOM.form.email.group.addClass('has-error');
                            }
                            if (error.password) {
                                app.DOM.form.pwd.group.find('strong').text(error.password[0]);
                                app.DOM.form.pwd.group.addClass('has-error');
                            }

                        });

                    });
                }
            }
        }

        app.init();

    });
    </script>
</body>
</html>

In the script above, the form is assigned to the this.DOM.form jQuery object. The event.preventDefault() method is used to prevent the jQuery form from submitting or posting normally so the ajax post can take place instead. On the next line, var self = this; the native JavaScript form object this is assigned to the self variable. When the ajax request.done callback function is ready to submit the form normally after all of the validation is completed, call the native form.submit() method with self.submit(). Since this is not a jQuery form event, the event.PreventDefault handler isn’t triggered, and the form is submitted.

After registering a new user, check the users table in database to verify that the new user was added successfully.

  • Even though XAMPP comes with phpMyAdmin which you can use in the web browser, I prefer to use the MySQL Workbench when working with MySQL.

Source Code

Published by

Jim Frenette

Web Developer - views here are my own except those taken from people more clever than me.