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.

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

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

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
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant 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
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
Rector logo

Rector

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

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Asymmetric Property Visibility in PHP 8.4 image

Asymmetric Property Visibility in PHP 8.4

Read article
Access Laravel Pulse Data as a JSON API image

Access Laravel Pulse Data as a JSON API

Read article
Laravel Forge adds Statamic Integration image

Laravel Forge adds Statamic Integration

Read article
Transform Data into Type-safe DTOs with this PHP Package image

Transform Data into Type-safe DTOs with this PHP Package

Read article
PHPxWorld - The resurgence of PHP meet-ups with Chris Morrell image

PHPxWorld - The resurgence of PHP meet-ups with Chris Morrell

Read article
Herd Executable Support and Pest 3 Mutation Testing in PhpStorm 2024.3 image

Herd Executable Support and Pest 3 Mutation Testing in PhpStorm 2024.3

Read article