Only until March 18th: coupon LARAVEL12 for 40% off Yearly/Lifetime membership!

Read more here
Courses

[FREE] Laravel 12 For Beginners: Your First Project

Form Validation and Error Messages

Summary of this lesson:
- Add backend validation to prevent invalid form submissions
- Use `validate()` in the Controller to enforce rules
- Display validation errors in Blade views
- Use Form Requests to simplify Controller logic

Let's work on validating the form.

Currently, we don't have any validation. If you don't fill out the Create Post form, you will receive an error from the database.


You could add required in the HTML form, and it would work. But this value can be easily removed using the browser developer tool, or the user could send the request not directly from the browser.

It could also be a mobile application with an API or someone maliciously trying to launch the request. So, we need the backend validation.

There are a few ways how to add backend validation. First, each field must have a set of validation rules. These rules can be placed in the Controller itself or a separate Form Request class.

First, let's see how we can do it in the Controller.


Validation Rules in Controller

We must validate the Request, so we need to call the validate() method and provide fields with rules in an array.

app/Http/Controllers/PostController.php:

class PostController extends Controller
{
// ...
 
public function store(Request $request)
{
$request->validate([
'title' => ['required'],
'text' => ['required'],
'category_id' => ['required'],
]);
 
Post::create([
'title' => $request->input('title'),
'text' => $request->input('text'),
'category_id' => $request->input('category_id'),
]);
 
return redirect()->route('posts.index');
}
 
// ...
}

Now, you will be redirected back to the form if you try to submit an empty form.

This is the default behavior: Laravel redirects back with error messages in the session if validation fails.


Showing Validation Messages

Let's show those validation errors. To show errors, we will use a snippet from the official documentation.

@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

The $errors variable is passed automatically to the Views. In the View where we have form, for example, on the create page, we can add this snippet before the form.

resources/views/posts/create.blade.php:

// ...
<div class="p-6 text-gray-900">
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
 
<form method="POST" action="{{ route('posts.store') }}">
// ...

After posting an empty form, we can see validation error messages.

Here's the GitHub commit for this change.


Validation in Form Requests

Next, I would like to show you how to make your Controllers shorter. For that, Laravel has a concept of Form Request class. Form Requests can also be created using an artisan command.

php artisan make:request StorePostRequest

Then, we replace the Request type in the Controllers method. And move all the fields into the StorePostRequest class rules() method.

app/Http/Controllers/PostController.php:

use App\Http\Requests\StorePostRequest;
 
class PostController extends Controller
{
// ...
 
public function store(Request $request)
public function store(StorePostRequest $request)
{
$request->validate([
'title' => ['required'],
'text' => ['required'],
'category_id' => ['required'],
]);
 
Post::create([
'title' => $request->input('title'),
'text' => $request->input('text'),
'category_id' => $request->input('category_id'),
]);
 
return redirect()->route('posts.index');
}
 
// ...
}

Inside the Form Request class, we see this structure:

app/Http/Requests/StorePostRequest.php:

class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return false;
}
 
public function rules(): array
{
return [
//
];
}
}

The authorize() method indicates whether the user is authorized to make the request. It can be set to true but could contain more checks, such as the user's role.

app/Http/Requests/StorePostRequest.php:

use Illuminate\Foundation\Http\FormRequest;
 
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return false;
return true;
}
 
public function rules(): array
{
return [
'title' => ['required'],
'text' => ['required'],
'category_id' => ['required'],
];
}
}

Also, when using Form Request, the returned result is validated data as an array. This array can be used when creating or updating records using the Request's validated() method.

app/Http/Controllers/PostController.php:

class PostController extends Controller
{
// ...
 
public function store(StorePostRequest $request)
{
$request->validate([
'title' => ['required'],
'text' => ['required'],
'category_id' => ['required'],
]);
 
Post::create([
'title' => $request->input('title'),
'text' => $request->input('text'),
'category_id' => $request->input('category_id'),
]);
 
Post::create($request->validated());
 
return redirect()->route('posts.index');
}
 
// ...
}

Now, the create form works the same way, but the Controller is much shorter. Validating data or creating the record is achieved with just two lines in the Controller.

Here's the GitHub commit for this change.


And... that's it for this practical Laravel course for beginners! Well done if you created a similar project while reading this!

In the next and final lesson, I will give advice on what to learn next.

No comments or questions yet...