This post is more than a year old. There is a chance the content is outdated.

Laravel Validation 101, Controllers, Form Requests, & Rules

Laravel Validation 101, Controllers, Form Requests, & Rules


A core part of any project is understanding how to validate the incoming request from your users.

Working with Controllers

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

The ValidatesRequests trait give 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:

1public function store(Request $request) {
2 $this->validate($request, [
3 'name' => 'required|string',
4 'body' => 'required|string',
5 'publish_at' => 'required|date_format:Y-m-d H:i:s'
6 ]);
7 
8 // The request validated and this code will get run
9 ...
10}
11 
12public function store(Request $request) {
13 // Note: the array syntax is also available
14 $request->validate([
15 'name' => ['required', 'string'],
16 'body' => ['required', 'string'],
17 'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
18 ]);
19 
20 // The request validated and this code will get run
21 ...
22}
23 
24public function store() {
25 request()->validate([
26 // Validation Rules...
27 ]);
28 
29 // The request validated and this code will get run
30 ...
31}
32 
33public function store() {
34 $this->validate(request(), [
35 // Validation Rules...
36 ]);
37 
38 // The request validated and this code will get run
39 ...
40}

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

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

Alternatively, you can do this too:

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

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:

1php artisan make:request StoreArticleRequest

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

1class StoreArticleRequest extends FormRequest
2{
3 
4 public function authorize()
5 {
6 return true;
7 }
8 
9 public function rules()
10 {
11 return [
12 //
13 ];
14 }
15}

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:

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

Rules

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

1public function rules()
2{
3 return [
4 'name' => ['required', 'string'],
5 'body' => ['required', 'string'],
6 'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
7 ];
8}

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:

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

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:

1public function store(StoreArticleRequest $request) {
2 $validatedData = $request->validated();
3 
4 // Insert into database
5 ...
6}

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:

1php artisan make:rule IsValidStateInUSA

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

1class IsValidStateInUSA implements Rule
2{
3 
4 public function passes($attribute, $value)
5 {
6 //
7 }
8 
9 public function message()
10 {
11 return 'The validation error message.';
12 }
13}

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:

1class IsValidStateInUSA implements Rule
2{
3 
4 public function passes($attribute, $value)
5 {
6 return in_array(strtoupper($value), [
7 'AL',
8 'AK',
9 'AZ',
10 ...
11 ]);
12 }
13 
14 public function message()
15 {
16 return 'This is not a valid state code in the USA.';
17 }
18}

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

1public function store(Request $request) {
2 $request->validate([
3 'state' => ['required', new IsValidStateInUSA],
4 ]);
5 
6 ...
7}

Handling Validation Errors

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

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

1@if($errors->any())
2 <div class="alert alert-danger" role="alert">
3 <button type="button" class="close" data-dismiss="alert" aria-label="Close">
4 <span aria-hidden="true">&times;</span>
5 </button>
6 
7 @foreach($errors->all() as $error)
8 {{ $error }}<br/>
9 @endforeach
10 </div>
11@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:

1@error('title')
2 <div class="alert alert-danger">{{ $message }}</div>
3@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:

1public function store(Request $request) {
2 $this->validate($request, [
3 'name' => 'required|string',
4 'body' => 'required|string',
5 'publish_at' => 'required|date_format:Y-m-d H:i:s'
6 ], [
7 'name.required' => 'A article name is required',
8 'body.required' => 'A article body is required',
9 'publish_at.date_format' => 'The publish at date is not in the correct format.'
10 ]);
11 
12 // The request validated and this code will get run
13 ...
14}

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

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

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

Anthony Rappa

By Anthony Rappa

Hello! I'm a full stack developer from Long Island, New York. Working mainly with Laravel, Tailwind, Livewire, and Alpine.js (TALL Stack). I share everything I know about these tools and more, as well as any useful resources I find from the community. You can find me on GitHub and LinkedIn.