Form Validation in Laravel with Real Examples
When you build a web application, forms are everywhere. Login forms, registration forms, contact forms, checkout forms, profile forms, admin forms, search forms, comment forms — almost every useful application depends on user input. And because users are human, that input is rarely perfect the first time. Someone forgets to type their email. Someone uploads the wrong file type. Someone writes a name that is too short. Someone leaves a required field empty. Someone enters a password that is too weak.
That is exactly why validation matters so much.
In Laravel, form validation is one of the nicest parts of the framework. It is expressive, readable, and powerful enough for small projects and large production systems. You can start with a few simple rules and gradually move into advanced scenarios like nested array validation, conditional rules, custom messages, and reusable Form Request classes. The beauty of Laravel validation is that it lets you keep your application clean while protecting your data from invalid or harmful input.
In this article, we will go deep into form validation in Laravel with real examples. We will not just talk about theory. We will build practical examples step by step so you can understand how validation works in real projects. By the end, you will know how to validate common forms, customize error messages, handle file uploads, validate arrays, use Form Request classes, and write validation logic that feels professional and maintainable.
Why Validation Matters So Much
Validation is not just about showing red error messages under a form field. It is about protecting your application, your database, and your users.
Imagine a registration form that accepts any kind of value. A user might enter text into a phone number field, or submit an empty email, or upload a huge unsupported file. Without validation, your application could save broken data, crash in unexpected ways, or create a bad user experience that feels sloppy and untrustworthy.
Good validation helps in several ways. It keeps your database clean. It improves user experience by showing clear guidance. It reduces bugs caused by bad input. It helps protect your application from common mistakes and input-related issues. And in a well-designed app, validation also acts like a conversation with the user. Instead of silently failing, the form tells them exactly what needs to be corrected.
Laravel understands this very well, which is why its validation system is designed to be developer-friendly and easy to read.
A Simple Example: Validating a Contact Form
Let us begin with one of the most common examples: a contact form.
Suppose you have a form with fields for name, email, subject, and message. You want to make sure that the name and email are required, the email is valid, the subject is not too short, and the message is long enough to be meaningful.
Blade Form Example
<form action="{{ route('contact.submit') }}" method="POST">
@csrf
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" value="{{ old('name') }}">
@error('name')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label for="email">Email</label>
<input type="email" name="email" id="email" value="{{ old('email') }}">
@error('email')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label for="subject">Subject</label>
<input type="text" name="subject" id="subject" value="{{ old('subject') }}">
@error('subject')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label for="message">Message</label>
<textarea name="message" id="message">{{ old('message') }}</textarea>
@error('message')
<p>{{ $message }}</p>
@enderror
</div>
<button type="submit">Send Message</button>
</form>
This Blade form uses old() to preserve input if validation fails, which makes the experience much better for the user. Nobody likes retyping everything because of one missing field. The @error directive displays the validation message for each field.
Controller Example
use Illuminate\Http\Request;
public function submit(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:100',
'email' => 'required|email|max:255',
'subject' => 'required|string|min:5|max:150',
'message' => 'required|string|min:20',
]);
// Save the data, send an email, or perform another action
// ...
return back()->with('success', 'Your message has been sent successfully.');
}
This is one of the simplest ways to validate data in Laravel. If validation fails, Laravel automatically redirects back to the form and sends the errors to the session. If validation passes, the $validated array contains only valid data.
That is already useful, but this is only the beginning.
Understanding Laravel Validation Rules
Laravel validation rules are the heart of the system. You can combine multiple rules on one field using a pipe | or an array.
Here are some of the most common rules you will use often:
requiredensures the field must not be emptystringensures the value is a stringemailensures the value is a valid email addressminandmaxcontrol length for strings or size for filesconfirmedchecks that a field has a matching confirmation fielduniquechecks that the value does not already exist in the databaseexistschecks that the value exists in the databasenumericensures the value is a numberdateensures the value is a valid datebooleanensures the value is true or falseimagevalidates image uploadsmimesandmimetypesvalidate file types
A rule set may look simple, but these rules give you a lot of control.
A Real Registration Form Example
Registration forms are perfect for learning validation because they usually involve several important fields and at least a few business rules.
Suppose we want to register a user with these fields:
name
email
password
password confirmation
terms acceptance
Controller Example
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|min:3|max:100',
'email' => 'required|email|unique:users,email|max:255',
'password' => 'required|string|min:8|confirmed',
'terms' => 'accepted',
]);
User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
return redirect()->route('login')->with('success', 'Account created successfully.');
}
This example shows a few very important ideas.
First, the unique:users,email rule prevents duplicate email addresses in the users table. That is essential in most applications. Second, the confirmed rule requires a matching password_confirmation field. Third, the password is hashed before saving. Validation and security go hand in hand. A form may validate successfully, but that does not mean every value should be saved directly as-is.
Blade Form Example
<form action="{{ route('register.submit') }}" method="POST">
@csrf
<div>
<label>Name</label>
<input type="text" name="name" value="{{ old('name') }}">
@error('name')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label>Email</label>
<input type="email" name="email" value="{{ old('email') }}">
@error('email')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label>Password</label>
<input type="password" name="password">
@error('password')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label>Confirm Password</label>
<input type="password" name="password_confirmation">
</div>
<div>
<label>
<input type="checkbox" name="terms" value="1">
I agree to the terms
</label>
@error('terms')
<p>{{ $message }}</p>
@enderror
</div>
<button type="submit">Register</button>
</form>
This is a common pattern, and it already covers a large percentage of real-world form validation cases.
Using Custom Error Messages
Laravel gives you default validation messages, but in real projects, custom messages often feel much better. They sound more natural, match your brand tone, and can be written in the language of your audience.
Here is an example of custom messages inside the controller.
public function register(Request $request)
{
$validated = $request->validate(
[
'name' => 'required|string|min:3|max:100',
'email' => 'required|email|unique:users,email|max:255',
'password' => 'required|string|min:8|confirmed',
'terms' => 'accepted',
],
[
'name.required' => 'Please enter your full name.',
'name.min' => 'Your name must be at least 3 characters long.',
'email.required' => 'Email address is required.',
'email.email' => 'Please enter a valid email address.',
'email.unique' => 'This email is already registered.',
'password.required' => 'Please create a password.',
'password.min' => 'Your password must be at least 8 characters.',
'password.confirmed' => 'The password confirmation does not match.',
'terms.accepted' => 'You must accept the terms before continuing.',
]
);
// ...
}
This approach is helpful, but once your form grows larger, putting everything inside the controller can make the code feel crowded. That is where Form Requests become incredibly useful.
Form Request Validation: Cleaner and More Professional
Form Request classes are one of the nicest features in Laravel validation. They allow you to move validation logic out of your controller and into a dedicated class. This keeps controllers cleaner and makes your validation easier to manage.
To create a Form Request, you can use:
php artisan make:request RegisterUserRequest
Form Request Example
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => 'required|string|min:3|max:100',
'email' => 'required|email|unique:users,email|max:255',
'password' => 'required|string|min:8|confirmed',
'terms' => 'accepted',
];
}
public function messages(): array
{
return [
'name.required' => 'Please enter your full name.',
'email.unique' => 'This email is already registered.',
'password.confirmed' => 'The password confirmation does not match.',
'terms.accepted' => 'You must accept the terms before continuing.',
];
}
}
Controller Using Form Request
use App\Http\Requests\RegisterUserRequest;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
public function register(RegisterUserRequest $request)
{
$validated = $request->validated();
User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
return redirect()->route('login')->with('success', 'Account created successfully.');
}
This version looks much cleaner. The controller focuses on what happens after validation, while the Form Request focuses on the rules themselves. In larger applications, this separation is extremely valuable.
Real Example: Validating a Blog Post Form
Let us move to a more realistic content management example. Imagine you are building a blog admin panel. You want to create a form for posts with the following fields:
title
slug
excerpt
body
category_id
published
cover_image
The validation should make sure the title and slug are unique, the body is long enough, the category exists, and the uploaded image has the right format.
Form Request Example
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => 'required|string|min:5|max:200',
'slug' => 'required|string|min:5|max:200|unique:posts,slug',
'excerpt' => 'nullable|string|max:500',
'body' => 'required|string|min:100',
'category_id' => 'required|exists:categories,id',
'published' => 'nullable|boolean',
'cover_image' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
];
}
public function messages(): array
{
return [
'title.required' => 'Post title is required.',
'slug.unique' => 'This slug is already in use.',
'body.min' => 'The post body must contain at least 100 characters.',
'category_id.exists' => 'Please select a valid category.',
'cover_image.image' => 'The cover image must be a valid image file.',
'cover_image.mimes' => 'Only JPG, JPEG, PNG, and WEBP files are allowed.',
'cover_image.max' => 'The cover image must not exceed 2MB.',
];
}
}
Controller Example
use App\Http\Requests\StorePostRequest;
use App\Models\Post;
use Illuminate\Support\Str;
public function store(StorePostRequest $request)
{
$validated = $request->validated();
$data = [
'title' => $validated['title'],
'slug' => $validated['slug'],
'excerpt' => $validated['excerpt'] ?? null,
'body' => $validated['body'],
'category_id' => $validated['category_id'],
'published' => $request->boolean('published'),
];
if ($request->hasFile('cover_image')) {
$data['cover_image'] = $request->file('cover_image')->store('posts', 'public');
}
Post::create($data);
return redirect()->route('posts.index')->with('success', 'Post created successfully.');
}
This is a very practical example because it combines text validation, relational validation, boolean handling, and file upload validation in one form.
Validating File Uploads Properly
File validation is one area where beginners often make mistakes. A file input may look simple, but you need to think about format, size, and sometimes dimensions.
Suppose you are allowing profile photo uploads. You probably want to accept only images, with a limited file size, and maybe with a specific width and height.
Example
public function updateProfile(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|min:3|max:100',
'avatar' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
]);
if ($request->hasFile('avatar')) {
$path = $request->file('avatar')->store('avatars', 'public');
}
// Save profile data
}
If you want to validate image dimensions, Laravel provides a dimensions rule.
public function updateProfile(Request $request)
{
$validated = $request->validate([
'avatar' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048|dimensions:min_width=300,min_height=300',
]);
}
This is useful when your UI requires a square or large enough image. It prevents users from uploading tiny images that look blurry or break your design.
Validating Arrays and Nested Inputs
Modern forms often include arrays. Think of product variations, shopping cart options, multiple images, tags, team members, or repeated form groups. Laravel handles array validation very well.
Imagine a product form with multiple tags:
<input type="text" name="tags[]" />
<input type="text" name="tags[]" />
<input type="text" name="tags[]" />
You can validate them like this:
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|min:3|max:255',
'tags' => 'nullable|array',
'tags.*' => 'nullable|string|min:2|max:50',
]);
}
The tags.* syntax means “validate each item in the tags array.” This is very powerful and very common in real forms.
Example with Nested Data
Suppose you are collecting multiple order items:
<input type="text" name="items[0][name]" />
<input type="number" name="items[0][quantity]" />
<input type="text" name="items[1][name]" />
<input type="number" name="items[1][quantity]" />
Validation:
public function storeOrder(Request $request)
{
$validated = $request->validate([
'customer_name' => 'required|string|max:100',
'items' => 'required|array|min:1',
'items.*.name' => 'required|string|min:2|max:255',
'items.*.quantity' => 'required|integer|min:1',
]);
}
This kind of validation is very useful in admin panels, inventory systems, quotation forms, and checkout systems.
Validating a Login Form
Login validation often looks simple, but it is still important. A login form typically needs an email and password.
Example
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
if (auth()->attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('/dashboard');
}
return back()->withErrors([
'email' => 'The provided credentials are incorrect.',
])->onlyInput('email');
}
Notice that we do not give away too much information. A generic error message is better for security than saying exactly which field is wrong in a way that helps attackers.
Conditional Validation Rules
Some fields should only be required if another field has a certain value. This is very common in real projects.
For example, imagine a form where users can choose whether they want delivery or pickup. If they select delivery, then an address must be provided.
Example
public function checkout(Request $request)
{
$validated = $request->validate([
'order_type' => 'required|in:delivery,pickup',
'address' => 'required_if:order_type,delivery|string|max:255',
'phone' => 'required|string|max:20',
]);
}
Here, address is only required when order_type is delivery.
Another useful rule is nullable, which means the field can be empty, but if it has a value, that value must still pass validation.
You can also use sometimes for more dynamic validation logic.
$request->validate([
'discount_code' => 'sometimes|string|min:3|max:20',
]);
Validating Unique Data During Updates
One of the most common mistakes in Laravel validation happens when updating records. Suppose you want to edit a user profile. The email should remain unique, but the current user’s email should not trigger a duplicate error if they did not change it.
This is where the ignore() method becomes useful.
Example
use Illuminate\Validation\Rule;
public function update(Request $request, User $user)
{
$validated = $request->validate([
'name' => 'required|string|min:3|max:100',
'email' => [
'required',
'email',
Rule::unique('users', 'email')->ignore($user->id),
],
]);
$user->update($validated);
return back()->with('success', 'Profile updated successfully.');
}
This is much safer and cleaner than trying to write custom logic yourself. It tells Laravel to check uniqueness while ignoring the current record.
Customizing Attribute Names for Friendlier Messages
Sometimes validation messages sound too technical. Instead of saying The first_name field is required, you may want to display First name is required.
Laravel lets you customize attribute names.
Example in Form Request
public function attributes(): array
{
return [
'first_name' => 'first name',
'last_name' => 'last name',
'phone_number' => 'phone number',
];
}
Now your validation messages become more natural and user-friendly.
This small detail makes a big difference. A form that feels human and clear is often much more pleasant to use than one that feels robotic.
Real Example: Validating a Product Form for an E-Commerce Site
E-commerce forms are excellent validation examples because they contain many field types. Let us say you are adding a product with:
name
sku
price
stock
description
category_id
images
status
Form Request Example
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreProductRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => 'required|string|min:3|max:255',
'sku' => ['required', 'string', 'max:100', Rule::unique('products', 'sku')],
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'description' => 'nullable|string|max:2000',
'category_id' => 'required|exists:categories,id',
'status' => 'required|in:active,inactive,draft',
'images' => 'nullable|array',
'images.*' => 'image|mimes:jpg,jpeg,png,webp|max:2048',
];
}
public function messages(): array
{
return [
'name.required' => 'Product name is required.',
'sku.unique' => 'This SKU already exists.',
'price.numeric' => 'The price must be a valid number.',
'stock.integer' => 'Stock must be an integer value.',
'category_id.exists' => 'Please choose a valid category.',
'status.in' => 'Please choose a valid product status.',
'images.*.image' => 'Each uploaded file must be an image.',
];
}
}
This example is close to what you might use in a real business application.
Controller Example
use App\Http\Requests\StoreProductRequest;
use App\Models\Product;
public function store(StoreProductRequest $request)
{
$validated = $request->validated();
$product = Product::create([
'name' => $validated['name'],
'sku' => $validated['sku'],
'price' => $validated['price'],
'stock' => $validated['stock'],
'description' => $validated['description'] ?? null,
'category_id' => $validated['category_id'],
'status' => $validated['status'],
]);
if ($request->hasFile('images')) {
foreach ($request->file('images') as $image) {
$path = $image->store('products', 'public');
// Save image relation if needed
}
}
return redirect()->route('products.index')->with('success', 'Product created successfully.');
}
Validation is not only about preventing bad data. It also helps define what “good data” means in your application.
Validating Dates and Time
Date validation is another area where you need precision. Consider an event booking form.
Fields might include:
event_date
start_time
end_time
booking_type
Example
public function storeEvent(Request $request)
{
$validated = $request->validate([
'event_name' => 'required|string|min:3|max:255',
'event_date' => 'required|date|after:today',
'start_time' => 'required',
'end_time' => 'required',
]);
}
You can also compare dates using Laravel’s date rules.
$request->validate([
'start_date' => 'required|date',
'end_date' => 'required|date|after_or_equal:start_date',
]);
This is especially useful in booking systems, hotel reservations, appointments, and scheduling apps.
Real Example: Validating a Profile Update Form
Profile forms are common, but they often include optional values. The user might want to change their name, phone number, bio, or avatar.
Example
public function updateProfile(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|min:3|max:100',
'phone' => 'nullable|string|min:8|max:20',
'bio' => 'nullable|string|max:500',
'avatar' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
]);
$user = auth()->user();
$user->name = $validated['name'];
$user->phone = $validated['phone'] ?? null;
$user->bio = $validated['bio'] ?? null;
if ($request->hasFile('avatar')) {
$user->avatar = $request->file('avatar')->store('avatars', 'public');
}
$user->save();
return back()->with('success', 'Profile updated successfully.');
}
This type of form is extremely common, and validation keeps it safe and consistent.
Handling Validation in AJAX Requests
In modern applications, forms are often submitted through AJAX instead of a full page reload. Laravel still handles validation nicely here.
When validation fails in an AJAX request, Laravel usually returns a JSON response with the validation errors.
Example
public function storeComment(Request $request)
{
$validated = $request->validate([
'post_id' => 'required|exists:posts,id',
'content' => 'required|string|min:5|max:1000',
]);
Comment::create($validated);
return response()->json([
'message' => 'Comment added successfully.',
]);
}
If the request expects JSON, Laravel will return validation errors in JSON format automatically. This is very convenient for frontend applications built with Vue, React, Alpine, or even plain JavaScript.
Using validateWithBag for Separate Error Bags
Sometimes a page has more than one form on it. For example, a profile page might have one form for updating personal info and another for changing password. In that case, error messages can become confusing if they all appear together.
Laravel allows you to use separate error bags.
Example
public function updatePassword(Request $request)
{
$request->validateWithBag('passwordUpdate', [
'current_password' => 'required',
'password' => 'required|string|min:8|confirmed',
]);
}
Then, in Blade, you can reference that error bag specifically.
@error('password', 'passwordUpdate')
<p>{{ $message }}</p>
@enderror
This keeps forms organized and prevents validation messages from leaking into the wrong place.
Advanced Validation with Rule Objects
Sometimes string-based rules are not enough. Laravel also allows rule objects, which can make complex validation more readable.
For example, the unique rule with ignore logic is often better written using Rule::unique().
use Illuminate\Validation\Rule;
$request->validate([
'email' => [
'required',
'email',
Rule::unique('users')->ignore($user->id),
],
]);
Rule objects are especially useful when validation conditions start becoming more dynamic or when you want to write code that is easier to understand later.
Real Example: Validating a Payment or Checkout Form
Checkout forms are sensitive because they often contain a mix of address, shipping, contact, and payment-related fields.
A simplified example might look like this:
public function checkout(Request $request)
{
$validated = $request->validate([
'full_name' => 'required|string|min:3|max:100',
'email' => 'required|email|max:255',
'phone' => 'required|string|max:20',
'address' => 'required|string|max:255',
'city' => 'required|string|max:100',
'postal_code' => 'required|string|max:20',
'country' => 'required|string|max:100',
'payment_method' => 'required|in:card,cash_on_delivery,paypal',
]);
}
If the order depends on country-specific rules or shipping methods, you can extend validation further with conditional logic.
The important thing is to think about the business process, not just the form field. Validation should match the real-world rules of your application.
Using sometimes for Flexible Validation
There are cases where a field should only be validated under certain conditions. Laravel’s sometimes method is helpful in that case.
Example
$validator = Validator::make($request->all(), [
'coupon_code' => 'nullable|string|max:50',
]);
$validator->sometimes('coupon_code', 'required|string|min:5', function ($input) {
return $input->has_discount === true;
});
$validated = $validator->validate();
This allows the rules to change based on the submitted data. Flexible validation like this is useful in conditional workflows.
Validating Strong Passwords
Passwords deserve extra attention. A good password rule should enforce length and potentially complexity.
Example
$request->validate([
'password' => 'required|string|min:8|confirmed',
]);
For more advanced password validation, Laravel also supports password rule helpers depending on your version and configuration. In modern apps, you may want stronger policies that require letters, numbers, symbols, or mixed-case characters.
A password is not just a form field. It is part of your application’s security boundary, so validation here matters a great deal.
Making Error Messages Feel Human
This part is often underestimated. Validation messages should not only be correct; they should sound like they were written by a thoughtful person.
Compare these two styles:
“validation.required”
versus
“Please enter your name so we can continue.”
The second one feels warmer and more useful. It does not sound like a machine yelling at the user. It sounds like a guide.
For example:
public function messages(): array
{
return [
'name.required' => 'Please tell us your name.',
'email.required' => 'We need your email address so we can contact you.',
'password.min' => 'Your password should be at least 8 characters long.',
];
}
That small touch can make a form feel far more polished.
A Full Example: Article Submission Form
Let us put everything together in a realistic example. Suppose a content platform has an article submission form.
Fields:
title
slug
category_id
summary
content
cover_image
tags
published_at
Form Request
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreArticleRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => 'required|string|min:10|max:255',
'slug' => ['required', 'string', 'min:10', 'max:255', Rule::unique('articles', 'slug')],
'category_id' => 'required|exists:categories,id',
'summary' => 'required|string|min:50|max:500',
'content' => 'required|string|min:500',
'cover_image' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:3072',
'tags' => 'nullable|array',
'tags.*' => 'string|min:2|max:50',
'published_at' => 'nullable|date|after_or_equal:today',
];
}
public function messages(): array
{
return [
'title.min' => 'Your article title should be descriptive.',
'slug.unique' => 'This slug is already taken.',
'summary.min' => 'The summary should give readers a clear idea of the article.',
'content.min' => 'The article content should be detailed and helpful.',
'published_at.after_or_equal' => 'The publish date cannot be in the past.',
];
}
public function attributes(): array
{
return [
'category_id' => 'category',
'cover_image' => 'cover image',
'published_at' => 'publish date',
];
}
}
Controller
use App\Http\Requests\StoreArticleRequest;
use App\Models\Article;
public function store(StoreArticleRequest $request)
{
$validated = $request->validated();
$article = Article::create([
'title' => $validated['title'],
'slug' => $validated['slug'],
'category_id' => $validated['category_id'],
'summary' => $validated['summary'],
'content' => $validated['content'],
'published_at' => $validated['published_at'] ?? null,
]);
if ($request->hasFile('cover_image')) {
$article->cover_image = $request->file('cover_image')->store('articles', 'public');
$article->save();
}
return redirect()->route('articles.index')->with('success', 'Article submitted successfully.');
}
This is the kind of validation you would be proud to keep in a real codebase.
Common Mistakes to Avoid
Even though Laravel makes validation easy, there are still a few mistakes people often make.
One common mistake is forgetting to validate on the server side and relying only on frontend validation. Frontend validation is useful, but it is never enough by itself because users can bypass it.
Another mistake is overusing controller validation in very large applications. It works, but eventually your controllers become crowded and difficult to maintain. Form Requests usually solve that problem.
A third mistake is not validating files properly. Accepting raw uploads without restrictions can lead to security and storage issues.
A fourth mistake is writing error messages that are too technical or unfriendly. Good validation should help people, not confuse them.
A fifth mistake is forgetting to handle update scenarios differently from create scenarios, especially with unique rules.
These may seem like small details, but together they make a big difference in the quality of your application.
Best Practices for Laravel Form Validation
A good validation strategy is simple, consistent, and easy to maintain.
Use Form Request classes for medium and large applications. Keep your controllers focused on business logic, not validation clutter. Write custom messages when user experience matters. Validate file uploads carefully. Use old() in Blade to preserve user input after validation errors. Customize attribute names when default field names feel too technical. Validate arrays with * rules when using repeated fields. Handle update forms properly with ignore(). And always remember that validation is both a usability feature and a security feature.
A clean validation layer often reflects a mature application overall. It shows that the developer has thought about how users actually behave, not just how the code should look.
A Friendly Final Thought
Laravel validation is one of those features that feels simple at first, but becomes more valuable the more you use it. At the beginning, it may just be about checking required fields. Later, it becomes your way of enforcing business rules, protecting your data, and guiding users gently through your forms.
That is what makes it so useful in real projects. Good validation is not just a technical detail. It is part of the experience. It is the quiet layer that helps your application feel reliable, polished, and respectful of the person using it.
When your validation is done well, users do not always notice it directly. They simply feel that the form makes sense, that mistakes are easy to fix, and that the application understands them. That is a sign of good software.