laravel-form-validation
PHP

Laravel Form Validation

Today we’ll be handling form validation in Laravel. There are many things that need to happen to make sure that everything works the way a user would expect it to so let’s get started.

What We’ll Be Building

Screenshot 2014-07-07 07.52.09

Our application will do many things. These include:

  • Validate required, unique in the database, and matching form fields
  • Use Laravel to show errors
  • Use Laravel to show old inputs so a user doesn’t have to retype inputs
  • Create custom error messages

That’s a lot to do so let’s get started. We’ll start by setting up our database.

Database and Models

Once we’ve got Laravel all set up, let’s go to our command line and create our migration so that our database table will be created.

We will need this so we can do validation to make sure that emails entered are unique in the database. We wouldn’t want multiple users with the same email right?

Migration

Go into the command line and type

$ php artisan migrate:make create_ducks_table --create=ducks

Now let’s edit that newly generated migration file.


<?php
// app/database/migrations/####_##_##_######_create_ducks_table.php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateDucksTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('ducks', function(Blueprint $table)
		{
			$table->increments('id');

			$table->string('name');
			$table->string('email');
			$table->string('password');
			
			$table->timestamps();
		});
	}

}

Now make sure your database settings are good in app/config/local/database.php or app/config/database.php and then let’s run our migration:

$ php artisan migrate

Model

We are going to create our Eloquent model so that we can save our ducks to the database. This is also how we will be able to test to make sure that our new duck/user has a unique email in the database.

Create the model at app/models/Duck.php.

<?php
// app/models/Duck.php

class Duck extends Eloquent {

	protected $fillable = array('name', 'email', 'password');

}

With our database and model ready to go, now we can get to our actual validation.

Further Reading: A Guide to Using Eloquent ORM in Laravel

Setting Up Our Routes

We will be handling the routing for our application in the app/routes.php file that Laravel provides. We’ll be handling the GET for showing the form and the POST for processing the form here.

<?php
// app/routes.php

// route to show the duck form
Route::get('ducks', function() 
{
	return View::make('duck-form');
});

// route to process the ducks form
Route::post('ducks', function()
{

	// process the form here

});

This will be accessible at http://example.com/ducks and then we’ll also be POSTing to the same URL. With our routes ready, let’s create the duck-form that we are displaying to our user.

Further Reading: Simple and Easy Laravel Routing

Creating Our View

The view file will be at app/views/duck-form.blade.php and we’ll use Laravel’s Blade to handle our views. For more information on Blade, here’s a starter Blade article.

Here’s that view file. We’ll use Bootstrap to make our views look good and then we’ll start our validation.

<!-- app/views/duck-form.blade.php -->
<!doctype html>
<html>
<head>
	<title>Laravel Form Validation!</title>

	<!-- load bootstrap -->
	<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
	<style>
		body 	{ padding-bottom:40px; padding-top:40px; }
	</style>
</head>
<body class="container">

<div class="row">
	<div class="col-sm-8 col-sm-offset-2">
		
		<div class="page-header">
			<h1><span class="glyphicon glyphicon-flash"></span> Ducks Fly!</h1>
		</div>	

		<!-- FORM STARTS HERE -->
		<form method="POST" action="/ducks" novalidate>

			<div class="form-group">
				<label for="name">Name</label>
				<input type="text" id="name" class="form-control" name="name" placeholder="Somebody Important">
			</div>

			<div class="form-group">
				<label for="email">Email</label>
				<input type="email" id="email" class="form-control" name="email" placeholder="super@cool.com">
			</div>

			<div class="form-group">
				<label for="password">Password</label>
				<input type="password" id="password" class="form-control" name="password">
			</div>

			<div class="form-group">
				<label for="password_confirm">Confirm Password</label>
				<input type="password" id="password_confirm" class="form-control" name="password_confirm">
			</div>

			<button type="submit" class="btn btn-success">Go Ducks Go!</button>

		</form>

	</div>
</div>

</body>
</html>

Now our beautiful form has taken… well… form.

laravel-form-validation-form

Validating Our Form

Now that we have our view, we’ll be going through all the validations needed. Since we already have our form’s action="/ducks" ready to go, our form will send a POST request to http://example.com/ducks and then we handle that in the route we made earlier in app/routes.php.

Basic Form Validation

Let’s start off our validation now in that routes.php file. We’re going to create our rules, run the validation on the form inputs, and handle the error messages from there.

Creating Rules

Let’s create those rules:

<?php 
// app/routes.php

...
// route to process the ducks form
Route::post('ducks', function()
{

	// process the form here

	// create the validation rules ------------------------
	$rules = array(
		'name'             => 'required', 						// just a normal required validation
		'email'            => 'required|email|unique:ducks', 	// required and must be unique in the ducks table
		'password'         => 'required',
		'password_confirm' => 'required|same:password' 			// required and has to match the password field
	);

	// do the validation ----------------------------------
	// validate against the inputs from our form
	$validator = Validator::make(Input::all(), $rules);

	// check if the validator failed -----------------------
	if ($validator->fails()) {

		// get the error messages from the validator
		$messages = $validator->messages();

		// redirect our user back to the form with the errors from the validator
		return Redirect::to('ducks')
			->withErrors($validator);

	} else {
		// validation successful ---------------------------

		// our duck has passed all tests!
		// let him enter the database

		// create the data for our duck
		$duck = new Duck;
		$duck->name     = Input::get('name');
		$duck->email    = Input::get('email');
		$duck->password = Hash::make(Input::get('password'));

		// save our duck
		$duck->save();

		// redirect ----------------------------------------
		// redirect our user back to the form so they can do it all over again
		return Redirect::to('ducks');

	}

});

...

With this setup, we have:

  • A required name
  • A required email that has to be in email form
  • An email that has to be unique in the database
  • A required password
  • A password_confirm field that needs to match password

So we have set the rules for our inputs. We have run the validation and checked if that worked. If it didn’t work, we are sending our user back to our form with the errors.

If the validation succeeded, we are going to create the duck in our database and redirect the user back to the form.

For a full list of the validation rules available to you, go look at the available validation rules.

Showing Errors In the View

Let’s say that our user did not pass the validation. We want to show off all the messages in our view. All we have to do is go into our view and add that in.

Dumping Out All Error Messages

<!-- app/views/duck-form.blade.php -->
...
<div class="row">
	<div class="col-sm-8 col-sm-offset-2">
		
		<div class="page-header">
			<h1><span class="glyphicon glyphicon-flash"></span> Ducks Fly!</h1>
		</div>	

		@if ($errors->has())
		
@foreach ($errors->all() as $error) {{ $error }}
@endforeach
@endif <!-- FORM STARTS HERE --> <form method="POST" action="/ducks" novalidate> ...
laravel-form-validation-show-all-errors

Just like that we know can show off our errors. This may not always be ideal though. Sometimes our user will expect the error to be placed next to the input it corresponds to.

Hand-Picking Error Messages

We have the ability to do this in Laravel and we can pick out single errors using:


// select the first error that corresponds to the name field
{{ $errors->first('name') }}

We can add this to each of our inputs and also we will use and if statement to add a Bootstrap error class (has-error) so that Bootstrap will turn our inputs red.

<!-- app/views/duck-form.blade.php -->
...
<!-- FORM STARTS HERE -->
<form method="POST" action="/ducks" novalidate>

	<div class="form-group @if ($errors->has('name')) has-error @endif">
		<label for="name">Name</label>
		<input type="text" id="name" class="form-control" name="name" placeholder="Somebody Awesome">
		@if ($errors->has('name')) <p class="help-block">{{ $errors->first('name') }}</p> @endif
	</div>

	<div class="form-group @if ($errors->has('email')) has-error @endif">
		<label for="email">Email</label>
		<input type="text" id="email" class="form-control" name="email" placeholder="super&64;cool.com">
		@if ($errors->has('email')) <p class="help-block">{{ $errors->first('email') }}</p> @endif
	</div>

	<div class="form-group @if ($errors->has('password')) has-error @endif">
		<label for="password">Password</label>
		<input type="password" id="password" class="form-control" name="password">
		@if ($errors->has('password')) <p class="help-block">{{ $errors->first('password') }}</p> @endif
	</div>

	<div class="form-group @if ($errors->has('password_confirm')) has-error @endif">
		<label for="password_confirm">Confirm Password</label>
		<input type="password" id="password_confirm" class="form-control" name="password_confirm">
		@if ($errors->has('password_confirm')) <p class="help-block">{{ $errors->first('password_confirm') }}</p> @endif
	</div>

	<button type="submit" class="btn btn-success">Go Ducks Go!</button>

</form>
...

Now we are using if statements that will show errors if Laravel has shown there is an error for that field.

Each input will have the correct errors next to it now.

laravel-form-validation-single-errors

Showing Old Inputs In the View

Now that our errors are good to go, our user will find those messages helpful. One other thing that they would appreciate also is that they would probably want to have the inputs they provided to still populate the form. We wouldn’t want our users to enter their name and email all over again just because their two password fields didn’t match up.

We have to add something to our form validation on the backend side of things and then we’ll add the information to our view file.

// app/routes.php
...
// check if the validator failed -----------------------
	if ($validator->fails()) {
		// redirect our user back with error messages		
		$messages = $validator->messages();

		// also redirect them back with old inputs so they dont have to fill out the form again
		// but we wont redirect them with the password they entered

		return Redirect::to('ducks')
			->withErrors($validator)
			->withInput(Input::except('password', 'password_confirm'));

	} else {
...

We are redirecting users back to the form with all inputs except the password and password_confirm fields. This way they won’t have to enter their name and email again.

Now, we have to populate our form with the data that comes back. We’ll use our input field’s value attribute and the way we get old input data from Laravel is to use:

{{ Input::old('name') }}

We’ll add that to our form now.

<!-- app/views/duck-form.blade.php -->
...
<!-- FORM STARTS HERE -->
<form method="POST" action="/ducks" novalidate>

	<div class="form-group @if ($errors->has('name')) has-error @endif">
		<label for="name">Name</label>
		<input type="text" id="name" class="form-control" name="name" placeholder="Somebody Awesome" value="{{ Input::old('name') }}">
		@if ($errors->has('name')) <p class="help-block">{{ $errors->first('name') }}</p> @endif
	</div>

	<div class="form-group @if ($errors->has('email')) has-error @endif">
		<label for="email">Email</label>
		<input type="text" id="email" class="form-control" name="email" placeholder="super&64;cool.com" value="{{ Input::old('email') }}">
		@if ($errors->has('email')) <p class="help-block">{{ $errors->first('email') }}</p> @endif
	</div>

...

Just like that, we are now populating our form with data that the user submitted. Our users will thank us for not having to enter in information again, especially on larger forms!

laravel-form-validation-old-input

Custom Error Messages

The last thing we’ll handle in form validation for Laravel today is to create custom error messages. Sometimes the default ones just aren’t fun enough for our flare and pizazz.

All we have to do for this is to create our custom messages and pass it into the $validator in our routes.php file. Let’s change the messages for our required and same fields.

// app/routes.php

// route to process the ducks form
Route::post('ducks', function()
{

	// create the validation rules ------------------------
	$rules = array(
		'name'             => 'required', 						// just a normal required validation
		'email'            => 'required|email|unique:ducks', 	// required and must be unique in the ducks table
		'password'         => 'required',
		'password_confirm' => 'required|same:password' 			// required and has to match the password field
	);

	// create custom validation messages ------------------
	$messages = array(
		'required' => 'The :attribute is really really really important.',
		'same' 	=> 'The :others must match.'
	);

	// do the validation ----------------------------------
	// validate against the inputs from our form
	$validator = Validator::make(Input::all(), $rules, $messages);

	// check if the validator failed -----------------------
	if ($validator->fails()) {

By just adding that, Laravel will automatically send back those error messages to the view. We don’t have to change anything on the view side of our code.

laravel-form-validation-custom-error-messages

You can create your own messages by using the :attribute and :other fillers to fill that space with the field name.

Model Level Rules

Another way to set rules for attributes is to do it in your models. This is a great way to do this since all the rules are attached to the model. This way, no matter where you have to validate, you can always reference back to the model. Let’s look at how we can do this.

In your model, let’s create the same rules we created ealier:

<?php
// app/models/Duck.php

class Duck extends Eloquent {

	protected $fillable = array('name', 'email', 'password');

	public static $rules = array(
		'name'             => 'required', 						// just a normal required validation
		'email'            => 'required|email|unique:ducks', 	// required and must be unique in the ducks table
		'password'         => 'required',
		'password_confirm' => 'required|same:password' 			// required and has to match the password field
	);

}

That’s all we have to do in the model. Now to access those rules when we validate, we just pass in those rules using ModelName::$rules.

$validator = Validator::make(Input::all(), ModelName::$rules);

You can also use this method to set rules based on user levels, like for an admin vs a normal user with differently named rule sets. So you would be able to create $adminRules and $userRules and access them directly.

Thanks to longshot for pointing out this great Jeffrey Way tip.

Conclusion

Hopefully this was a good example of the flexibility and features that Laravel provides when validating forms. There is still much more to dive into like creating custom validation rules and conditionally adding rules.

I would definitely suggest checking out the official docs for more information about the great things you can do. Let us know if you have any questions or comments on any validation tactics and thanks for reading.

Chris Sevilleja

Design, development, and anything in between that I find interesting.

View My Articles

Stay Connected With Us
hover these for magic

Follow

Get valuable tips, articles, and resources straight to your inbox. Every Tuesday.