Laravel Validation 101, Controllers, Form Requests, and Rules

Published on by

Laravel Validation 101, Controllers, Form Requests, and Rules image

A core part of any project is understanding how to validate the incoming request from your users and in this tutorial let’s look at how we can setup validation with our controllers, form requests, and rules.

Working with Controllers

By default, all Laravel controllers that extend from the Base Controller inherit the ValidatesRequests trait.

The ValidatesRequests trait gives you access to the validate method in your controller methods.

For example, say the user is creating an Article:

They may be entering a name, and some content for this article, along with a date to publish. Your controller validation may look like one of these, which are all valid:

public function store(Request $request) {
$this->validate($request, [
'name' => 'required|string',
'body' => 'required|string',
'publish_at' => 'required|date_format:Y-m-d H:i:s'
]);
 
// The request validated and this code will get run
...
}
 
public function store(Request $request) {
// Note: the array syntax is also available
$request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
 
// The request validated and this code will get run
...
}
 
public function store() {
request()->validate([
// Validation Rules...
]);
 
// The request validated and this code will get run
...
}
 
public function store() {
$this->validate(request(), [
// Validation Rules...
]);
 
// The request validated and this code will get run
...
}

It should also be noted, that the response to the validate method will be the data that was validated:

public function store(Request $request) {
$validatedData = $request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
 
// I know that the only keys in validated data are the ones that were successfully validated, i.e.: name, body, published_at
$this->articleService->store($validatedData);
}

Alternatively, you can do this too:

public function store(Request $request) {
$request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
 
// I know that the only keys in validated data are the ones that were successfully validated, i.e.: name, body, published_at
$this->articleService->store($request->only('name', 'body', 'published_at'));
}

A list of the available validation rules can be found here.

Working with Form Requests

If you would like to extract your validation logic away from your controllers, or you would like to do authorization and validation at the same time, Laravel makes the Form Request class available to you.

You can make a form request class using one of the many built-in Laravel generators:

php artisan make:request StoreArticleRequest

This will make you the following class in app/Http/Requests by default:

class StoreArticleRequest extends FormRequest
{
 
public function authorize()
{
return true;
}
 
public function rules()
{
return [
//
];
}
}

Form request classes comes with 2 methods, authorize and rules.

Authorize

Authorize lets you prevent the controller code from running if this method returns false. Laravel will throw a 401 unauthorized exception.

An example usage might be:

public function authorize()
{
return $this->user()->can('create articles');
}

Rules

The rules method will return our array of rules that was in our controller validate method;

public function rules()
{
return [
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
];
}

Note: You can type-hint any dependencies needed into the rules method.

So, how do we use this class? We type-hint it right into our controller method:

public function store(StoreArticleRequest $request) {
// Validation and Rules passed
}

If the validation fails, the user will be redirected back with the errors flashed to the session. If it is an AJAX call, it will be a 422 Unprocessable Entity response.

You may get the validated data just like before:

public function store(StoreArticleRequest $request) {
$validatedData = $request->validated();
 
// Insert into database
...
}

Working with Custom Rule Classes

If there is a validation rule that does not exist as part of the available built-in validation rules, you can create a custom one:

Again, using one of Laravel’s make commands:

php artisan make:rule IsValidStateInUSA

This will make you the following class in app/Rules by default:

class IsValidStateInUSA implements Rule
{
 
public function passes($attribute, $value)
{
//
}
 
public function message()
{
return 'The validation error message.';
}
}

Your passes method holds the logic for allowing this validation method through. The message method is the message that gets returned if it fails.

Using the example of Is a valid State in the USA, we would need the item passed in to be one of 50 different state codes. It could look like this:

class IsValidStateInUSA implements Rule
{
 
public function passes($attribute, $value)
{
return in_array(strtoupper($value), [
'AL',
'AK',
'AZ',
...
]);
}
 
public function message()
{
return 'This is not a valid state code in the USA.';
}
}

Then, to use this in your controller or form request, you just add it to the list of validation rules for that key:

public function store(Request $request) {
$request->validate([
'state' => ['required', new IsValidStateInUSA],
]);
 
...
}

Handling Validation Errors

When validation fails, Laravel will automatically redirect back, with an $errors variables flashed in the session.

You can easily create a file called messages.blade.php and include in your master layout file like this:

@if($errors->any())
<div class="alert alert-danger" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
 
@foreach($errors->all() as $error)
{{ $error }}<br/>
@endforeach
</div>
@endif

Which will display all errors flashed to the session, or if you prefer to handle them one by one on a per blade file basis, you can use the blade @error helper:

@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror

The $message variable will be provided to you inside the @error tag.

Customizing Error Messages

When working with the validate method, you can override the messages with the third parameter:

public function store(Request $request) {
$this->validate($request, [
'name' => 'required|string',
'body' => 'required|string',
'publish_at' => 'required|date_format:Y-m-d H:i:s'
], [
'name.required' => 'A article name is required',
'body.required' => 'A article body is required',
'publish_at.date_format' => 'The publish at date is not in the correct format.'
]);
 
// The request validated and this code will get run
...
}

When working with a form request class, a messages function can also be implemented to customize the error message for each specific rule:

public function messages()
{
return [
'name.required' => 'A article name is required',
'body.required' => 'A article body is required',
'publish_at.date_format' => 'The publish at date is not in the correct format.'
];
}

For the most up to date documentation and a much more in-depth look at validation, visit the Laravel Documentation.

Anthony Rappa photo

Full Stack/Certified Laravel Developer from Long Island, New York.

Filed in:
Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Tinkerwell

Version 4 of Tinkerwell is available now. Get the most popular PHP scratchpad with all its new features and simplify your development workflow today.

Visit Tinkerwell
Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
Larafast: Laravel SaaS Starter Kit logo

Larafast: Laravel SaaS Starter Kit

Larafast is a Laravel SaaS Starter Kit with ready-to-go features for Payments, Auth, Admin, Blog, SEO, and beautiful themes. Available with VILT and TALL stacks.

Larafast: Laravel SaaS Starter Kit
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector

The latest

View all →
Sort Elements with the Alpine.js Sort Plugin image

Sort Elements with the Alpine.js Sort Plugin

Read article
Anonymous Event Broadcasting in Laravel 11.5 image

Anonymous Event Broadcasting in Laravel 11.5

Read article
Microsoft Clarity Integration for Laravel image

Microsoft Clarity Integration for Laravel

Read article
Apply Dynamic Filters to Eloquent Models with the Filterable Package image

Apply Dynamic Filters to Eloquent Models with the Filterable Package

Read article
Property Hooks Get Closer to Becoming a Reality in PHP 8.4 image

Property Hooks Get Closer to Becoming a Reality in PHP 8.4

Read article
Asserting Exceptions in Laravel Tests image

Asserting Exceptions in Laravel Tests

Read article