Laravel Form Validation is one of the most important features to understand when building reliable Laravel applications. Whether you are creating a registration form, login form, contact form, checkout page, product form, profile editor, or REST API, validating user input is essential.
Table of Contents
Laravel provides a powerful form validation system with hundreds of built-in rules, readable syntax, custom error messages, conditional validation, Form Request classes, file validation, nested array validation, database-aware rules, and custom form validation rules.
In this guide, we will learn Laravel form validation from the basics to more advanced techniques, with practical examples you can use in real-world applications.
Laravel version note: The examples in this article follow modern Laravel validation practices and are aligned with the current Laravel documentation. Laravel’s validation API continues to evolve, so always check the official documentation when working with a specific Laravel version.
What Is Laravel Form Validation?
Laravel Form validation is the process of checking whether data submitted by a user satisfies specific requirements before the application processes or stores it.
For example, suppose you have a registration form:
Name
Email
Password
Confirm Password
You may want to enforce rules such as:
- Name is required.
- Name must be a string.
- Name must be at least three characters.
- Email is required.
- Email must be valid.
- Email must be unique.
- Password is required.
- Password must meet your application’s password requirements.
- Password confirmation must match.
Instead of manually writing PHP if statements for every condition, Laravel provides a dedicated validation system.
A simple example looks like this:
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed'],
]);
If validation succeeds, Laravel continues processing the request.
If validation fails during a normal web request, Laravel automatically redirects the user back and makes validation errors available to the view. For XHR requests, Laravel returns validation errors in JSON with a 422 response.
Why Is Laravel Form Validation Important?
Never assume that data submitted by a browser is valid.
Client-side laravel form validation can improve user experience, but it should never be your only validation layer.
A malicious user can bypass JavaScript validation and send a request directly to your Laravel application.
Server-side Laravel validation helps protect your application from:
- Invalid data
- Missing fields
- Incorrect data types
- Unexpected values
- Duplicate records
- Invalid file uploads
- Incorrect relationships
- Business-rule violations
- Malformed API requests
For example, this JavaScript validation:
if (email === '') {
alert('Email is required');
}
is useful for the user interface but is not sufficient for application security.
The server should still validate the request:
$request->validate([
'email' => ['required', 'email'],
]);
A good rule is:
Client-side validation improves UX; server-side validation protects application integrity.
How Laravel Form Validation Works
The basic Laravel validation flow is:
User submits form
↓
Laravel receives HTTP request
↓
Validation rules are executed
↓
├── Validation passes → Continue application logic
│
└── Validation fails → Return validation errors
Laravel provides several ways to perform validation.
The most common approaches are:
$request->validate()- Form Request classes
Validatorfacade- Laravel Custom validation rules
For small controllers, $request->validate() is usually convenient.
For larger applications, Form Requests provide a cleaner structure.
Creating a Laravel Form
Let’s create a simple contact form.
Suppose we have the following routes:
use App\Http\Controllers\ContactController;
use Illuminate\Support\Facades\Route;
Route::get('/contact', [ContactController::class, 'create'])
->name('contact.create');
Route::post('/contact', [ContactController::class, 'store'])
->name('contact.store');
Create the controller:
php artisan make:controller ContactController
Then:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function create()
{
return view('contact');
}
public function store(Request $request)
{
// Validation will go here
}
}
Now create:
resources/views/contact.blade.php
Example form:
<form method="POST" action="{{ route('contact.store') }}">
@csrf
<div>
<label for="name">Name</label>
<input
type="text"
id="name"
name="name"
>
</div>
<div>
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
>
</div>
<div>
<label for="message">Message</label>
<textarea
id="message"
name="message"
></textarea>
</div>
<button type="submit">
Send Message
</button>
</form>
Basic Laravel Validation
Now let’s add validation.
public function store(Request $request)
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email'],
'message' => ['required', 'string', 'min:10'],
]);
// Process validated data
}
Here:
'name' => ['required', 'string', 'max:255']
means:
required: the field must be present and non-empty.string: the value must be a string.max:255: the maximum size is 255 characters.
The validate() method returns the validated data when validation succeeds.
For example:
$validated = $request->validate([
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]);
You can then use:
$validated['name'];
$validated['email'];
This is preferable to blindly processing every value in the request.
Laravel Validation Rules
Laravel validation rules can be written as strings:
'email' => 'required|email'
or as arrays:
'email' => ['required', 'email']
The array syntax is often easier to maintain when rules become more complex.
For example:
'username' => [
'required',
'string',
'min:3',
'max:50',
]
Laravel also provides fluent rule objects for more advanced scenarios:
use Illuminate\Validation\Rule;
For example:
'role' => [
'required',
Rule::in(['admin', 'editor', 'author']),
]
Common Laravel Validation Rules
Here are some of the most frequently used validation rules.
| Rule | Purpose |
|---|---|
| required | Field must exist and contain a value |
| nullable | Field can contain null |
| string | Value must be a string |
| integer | Value must be an integer |
| numeric | Value must be numeric |
| boolean | Value must represent a boolean |
| array | Value must be an array |
| Validates email format | |
| url | Validates URL |
| date | Validates date |
| regex | Validates against regular expression |
| min | Minimum size/value |
| max | Maximum size/value |
| between | Value must be within a range |
| in | Value must exist in a list |
| not_in | Value must not exist in a list |
| same | Must match another field |
| different | Must differ from another field |
| confirmed | Requires matching confirmation field |
| unique | Checks database uniqueness |
| exists | Checks database existence |
| file | Validates uploaded file |
| image | Validates image |
| mimes | Validates MIME type |
| required_if | Required when another field matches |
| required_with | Required when another field exists |
| required_without | Required when another field is missing |
Laravel’s current validation documentation contains a large collection of built-in rules, including rules for strings, arrays, files, dates, passwords, database values, conditional input, and more.
The required Rule
The required rule is probably the most commonly used Laravel validation rule.
'name' => ['required']
It means the field must be present and must not be empty.
Example:
$request->validate([
'name' => ['required'],
]);
If the user submits an empty name, laravel custom validation fails.
The string Rule
Use string when a value should be text.
'name' => ['required', 'string']
You can combine it with length rules:
'name' => [
'required',
'string',
'min:3',
'max:100',
]
This is useful for names, titles, descriptions, usernames, and similar fields.
The email Rule
For email addresses:
'email' => [
'required',
'email',
]
Laravel’s email validation uses the egulias/email-validator package. Laravel also supports additional email validation styles such as RFC and DNS validation.
For example:
'email' => [
'required',
'email:rfc,dns',
]
Be careful with DNS validation because it requires DNS-related checks and may not be appropriate for every application.
The integer and numeric Rules
Use integer when a value must be an integer:
'age' => [
'required',
'integer',
]
Use numeric when decimal values should also be accepted:
'price' => [
'required',
'numeric',
]
For a product price:
'price' => [
'required',
'numeric',
'min:0',
]
Modern Laravel also provides more specialized rules and fluent builders for numeric constraints.
Minimum and Maximum Laravel Validation
You can use min and max with different types of values.
For a string:
'title' => [
'required',
'string',
'min:5',
'max:100',
]
For a number:
'age' => [
'required',
'integer',
'min:18',
]
For a file:
'photo' => [
'required',
'file',
'max:2048',
]
The meaning of size-related rules depends on whether the validated value is a string, numeric value, array, or file.
Displaying Laravel Validation Errors in Blade
Laravel automatically makes validation errors available through the $errors variable.
You can display all errors:
@if ($errors->any())
<div>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
You can display the first error for a specific field:
@error('email')
<p>{{ $message }}</p>
@enderror
This is often cleaner.
For example:
<div>
<label for="email">Email</label>
<input
type="email"
name="email"
id="email"
>
@error('email')
<p>{{ $message }}</p>
@enderror
</div>
Laravel’s validation system provides a MessageBag for working with validation errors, including retrieving the first error for a specific attribute.
Adding Error Styling
A common Blade pattern is to add a class when a field has an error:
<input
type="email"
name="email"
class="@error('email') border-red-500 @enderror"
value="{{ old('email') }}"
>
You can also use:
@if ($errors->has('email'))
...
@endif
This makes it easy to visually identify invalid fields.
Repopulating Form Fields with old()
One frustrating user experience is losing all form data after a laravel form validation failure.
Laravel solves this using flashed input and the old() helper.
<input
type="text"
name="name"
value="{{ old('name') }}"
>
For email:
<input
type="email"
name="email"
value="{{ old('email') }}"
>
Laravel’s old() helper retrieves input from the previous request, allowing forms to be repopulated after validation failure.
A complete field might look like:
<div>
<label>Name</label>
<input
type="text"
name="name"
value="{{ old('name') }}"
>
@error('name')
<p>{{ $message }}</p>
@enderror
</div>
Never repopulate password fields:
<input
type="password"
name="password"
>
Custom Laravel Validation Error Messages
Laravel provides default validation messages, but you may want application-specific messages.
One approach is to pass custom messages to the validator.
$request->validate(
[
'email' => ['required', 'email'],
'name' => ['required'],
],
[
'email.required' => 'Please enter your email address.',
'email.email' => 'Please enter a valid email address.',
'name.required' => 'Please enter your name.',
]
);
This gives you complete control over the user-facing messages.
Laravel’s validation messages can also be customized through the application’s language files.
Custom Attribute Names
Sometimes Laravel’s default attribute name is not user-friendly.
Instead of:
The first_name field is required.
you may want:
The first name field is required.
You can customize attribute names.
$request->validate(
[
'first_name' => ['required'],
],
[],
[
'first_name' => 'first name',
]
);
This is particularly useful when field names contain underscores or internal naming conventions.
Using the bail Rule
Suppose you have:
'title' => [
'required',
'unique:posts',
'max:255',
]
Laravel evaluates rules in order.
You can use bail to stop validation for that field after the first failure:
'title' => [
'bail',
'required',
'unique:posts',
'max:255',
]
For example, if title is empty, Laravel doesn’t need to continue checking subsequent rules for that field.
Laravel explicitly documents bail as a way to stop further validation rules for an attribute after the first failure.
Using nullable for Optional Fields
Consider an optional publication date:
'publish_at' => [
'date',
]
If the field is submitted as null, validation can fail because date expects a valid date.
Use:
'publish_at' => [
'nullable',
'date',
]
Now the field can be either:
null- A valid date
Laravel’s default middleware also trims strings and converts empty strings to null, which makes nullable particularly important for optional fields.
Validating Checkboxes
Suppose you have:
<input type="checkbox" name="terms" value="1">
You can validate:
'terms' => [
'accepted',
]
This is useful for terms and conditions.
Example:
$request->validate([
'terms' => ['accepted'],
]);
Validating Passwords
For password validation, Laravel provides the Password rule.
use Illuminate\Validation\Rules\Password;
$request->validate([
'password' => [
'required',
'confirmed',
Password::min(8),
],
]);
You can create stronger requirements:
Password::min(8)
->mixedCase()
->numbers()
->symbols()
This is more expressive than manually creating complex regular expressions.
Confirming Passwords
Laravel’s confirmed rule is useful for password confirmation.
Validation:
'password' => [
'required',
'confirmed',
]
Your form should contain:
<input
type="password"
name="password"
>
<input
type="password"
name="password_confirmation"
>
Laravel checks whether the confirmation field matches the original password.
The confirmed rule expects a corresponding {field}_confirmation field by default.
Validating Dates
Basic date validation:
'date_of_birth' => [
'required',
'date',
]
You can also compare dates.
For example:
'end_date' => [
'required',
'date',
'after:start_date',
]
This ensures the end date occurs after the start date.
Modern Laravel also provides fluent date rule builders for expressing date constraints more clearly.
Conditional Validation
Sometimes a field is required only under certain conditions.
Suppose users can choose:
Contact Method:
Email
Phone
If they select phone, a phone number should be required.
You can use:
'phone' => [
'required_if:contact_method,phone',
]
Example:
$request->validate([
'contact_method' => ['required', 'in:email,phone'],
'email' => [
'required_if:contact_method,email',
'email',
],
'phone' => [
'required_if:contact_method,phone',
],
]);
Laravel supports several conditional rules, including required_if, required_unless, required_with, and required_without.
Using Rule::requiredIf
For more complicated conditions, use Rule::requiredIf.
use Illuminate\Validation\Rule;
$request->validate([
'role_id' => [
Rule::requiredIf($request->user()->is_admin),
],
]);
Closures can also be used:
'role_id' => [
Rule::requiredIf(fn () => $request->user()->is_admin),
]
This is useful when conditional validation depends on application logic rather than a simple field comparison.
Validating File Uploads
File uploads require special attention because users can submit files that don’t match the expected format.
Basic file validation:
$request->validate([
'document' => [
'required',
'file',
'max:5120',
],
]);
The value of max for files is measured in kilobytes.
You can also validate extensions:
'document' => [
'required',
'file',
'extensions:pdf,doc,docx',
]
However, don’t rely only on the filename extension. Laravel’s documentation specifically notes that extension validation should generally be combined with MIME-related validation when appropriate.
Validating Images
For an image upload:
'avatar' => [
'required',
'image',
]
You can add size restrictions:
'avatar' => [
'required',
'image',
'max:2048',
]
You can also use the dimensions rule:
use Illuminate\Validation\Rule;
'avatar' => [
'required',
'image',
Rule::dimensions()
->maxWidth(1000)
->maxHeight(1000),
]
Laravel provides fluent image-dimension validation options for width, height, and aspect ratio.
Validating Arrays
Suppose your form submits:
tags[]
tags[]
tags[]
You can validate:
'tags' => [
'required',
'array',
]
Then validate every element:
'tags.*' => [
'string',
'max:50',
]
Example:
$request->validate([
'tags' => ['required', 'array'],
'tags.*' => ['string', 'max:50'],
]);
Laravel supports wildcard validation for array elements.
Validating Nested Input
Suppose the request contains:
[
'author' => [
'name' => 'John',
'email' => 'john@example.com',
],
]
You can validate it using dot notation:
$request->validate([
'author.name' => ['required', 'string'],
'author.email' => ['required', 'email'],
]);
Laravel uses dot notation for nested request attributes.
Validating Dynamic Form Rows
This is useful for invoices, orders, shopping carts, and product variants.
Example request:
[
'products' => [
[
'product_id' => 1,
'quantity' => 2,
],
[
'product_id' => 5,
'quantity' => 4,
],
],
]
Validation:
$request->validate([
'products' => ['required', 'array'],
'products.*.product_id' => [
'required',
'integer',
],
'products.*.quantity' => [
'required',
'integer',
'min:1',
],
]);
This approach is extremely useful for dynamic forms.
Database Validation with exists
Suppose a product belongs to a category.
You can ensure the category exists:
'category_id' => [
'required',
'exists:categories,id',
]
This prevents invalid category IDs from being accepted.
A more expressive version uses Rule:
use Illuminate\Validation\Rule;
'category_id' => [
'required',
Rule::exists('categories', 'id'),
]
Database Validation with unique
Suppose user emails must be unique:
'email' => [
'required',
'email',
'unique:users,email',
]
Laravel checks whether the email already exists in the users table.
This is especially important for:
- Registration
- Usernames
- Product SKUs
- Slugs
- Coupon codes
- Order numbers
Unique Validation During Updates
One common mistake occurs when updating an existing record.
Suppose user ID 10 already has:
john@example.com
If you use:
'email' => [
'required',
'email',
'unique:users,email',
]
Laravel may reject the user’s existing email because it already exists.
You can ignore the current record.
use Illuminate\Validation\Rule;
'email' => [
'required',
'email',
Rule::unique('users', 'email')
->ignore($user->id),
]
This allows the current user to keep their existing email while still preventing another user from using it.
Form Request Validation
When validation becomes complex, putting everything inside a controller can make the controller difficult to maintain.
Laravel provides Form Request classes specifically for this purpose.
Create one:
php artisan make:request StorePostRequest
Laravel creates the request class under:
app/Http/Requests/StorePostRequest.php
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',
'max:255',
],
'body' => [
'required',
'string',
],
];
}
}
Then inject the request into your controller:
use App\Http\Requests\StorePostRequest;
public function store(StorePostRequest $request)
{
$validated = $request->validated();
// Store post
}
Laravel validates the Form Request before the controller method is executed.
This keeps controllers clean.
Authorizing Form Requests
A Form Request contains an authorize() method.
public function authorize(): bool
{
return true;
}
You can use this method for authorization logic.
For example:
public function authorize(): bool
{
return auth()->user()?->can('create', Post::class) ?? false;
}
This separates authorization from validation.
That is especially useful in applications with policies and permissions.
Custom Messages in Form Requests
Instead of defining custom messages inside the controller, you can define them in the Form Request:
public function messages(): array
{
return [
'title.required' => 'Please enter a post title.',
'title.max' => 'The post title cannot exceed 255 characters.',
'body.required' => 'Please enter the post content.',
];
}
This keeps validation-related configuration together.
Preparing Data Before Validation
Sometimes incoming input needs to be transformed before validation.
A Form Request can prepare the data before validation.
For example:
protected function prepareForValidation(): void
{
$this->merge([
'slug' => strtolower($this->slug),
]);
}
This can be useful for:
- Normalizing input
- Converting formats
- Cleaning values
- Preparing derived fields
Be careful not to use preprocessing as a replacement for proper validation.
Working with Validated Data
After validation, prefer validated data rather than the entire request.
For example:
$validated = $request->validated();
Or:
$validated = $request->safe()->only([
'title',
'body',
]);
You can also retrieve validated data as a collection:
$collection = $request->safe()->collect();
Laravel’s validation API provides safe(), only(), except(), merge(), and collect() methods for working with validated input.
Manual Validator
For more control, Laravel provides the Validator facade.
use Illuminate\Support\Facades\Validator;
$validator = Validator::make(
$request->all(),
[
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]
);
Then:
if ($validator->fails()) {
return back()->withErrors($validator);
}
You can also retrieve validated data:
$validated = $validator->validated();
Manual validation is useful when:
- Validation happens outside normal controller flow.
- You need custom validation behavior.
- Multiple validation stages are required.
- You are building reusable application services.
Laravel Validation for APIs
Laravel validation also works very well for REST APIs.
Example:
public function store(Request $request)
{
$validated = $request->validate([
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]);
return response()->json([
'message' => 'User created successfully.',
'data' => $validated,
]);
}
When validation fails for an XHR/API-style request, Laravel returns validation errors using a 422 Unprocessable Entity response.
A frontend can therefore receive something conceptually like:
{
"message": "The given data was invalid.",
"errors": {
"email": [
"The email field must be a valid email address."
]
}
}
This structure works well with React, Vue, mobile applications, and other API clients.
Custom Validation Rules
Built-in validation rules cover most common requirements.
Sometimes your application needs a business-specific rule.
Examples:
- Username must not contain reserved words.
- Coupon must be valid for a particular customer.
- Employee ID must follow a company-specific pattern.
- Product SKU must follow a custom format.
You can create a custom validation rule.
php artisan make:rule ValidUsername
Example rule:
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidUsername implements ValidationRule
{
public function validate(
string $attribute,
mixed $value,
Closure $fail
): void {
if (in_array(strtolower($value), ['admin', 'root', 'system'])) {
$fail('The :attribute cannot use a reserved username.');
}
}
}
Use it:
use App\Rules\ValidUsername;
$request->validate([
'username' => [
'required',
'string',
new ValidUsername,
],
]);
This is much cleaner than placing complex business logic directly inside a controller.
Real-World Registration Form Example
Let’s put several concepts together.
Controller
use Illuminate\Http\Request;
use Illuminate\Validation\Rules\Password;
public function register(Request $request)
{
$validated = $request->validate([
'name' => [
'required',
'string',
'min:3',
'max:100',
],
'email' => [
'required',
'email',
'unique:users,email',
],
'password' => [
'required',
'confirmed',
Password::min(8)
->mixedCase()
->numbers()
->symbols(),
],
'terms' => [
'accepted',
],
]);
// Create user...
}
Blade Form
<form method="POST" action="{{ route('register') }}">
@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 and conditions.
</label>
@error('terms')
<p>{{ $message }}</p>
@enderror
</div>
<button type="submit">
Register
</button>
</form>
This gives you server-side validation for the major registration requirements.
Real-World Product Form Example
Consider a product creation form:
$request->validate([
'name' => [
'required',
'string',
'max:255',
],
'sku' => [
'required',
'string',
'max:100',
'unique:products,sku',
],
'price' => [
'required',
'numeric',
'min:0',
],
'category_id' => [
'required',
'integer',
'exists:categories,id',
],
'description' => [
'nullable',
'string',
],
'image' => [
'nullable',
'image',
'max:2048',
],
]);
This example demonstrates several important validation concepts:
- Required fields
- String validation
- Unique database values
- Numeric validation
- Minimum values
- Foreign-key validation
- Optional fields
- File validation
Laravel Form Validation Best Practices
1. Always Validate Server-Side
Never rely solely on JavaScript validation.
Always validate incoming data in Laravel.
2. Validate Before Database Operations
Don’t insert data first and validate afterward.
Bad:
User::create($request->all());
$request->validate([
'email' => ['required', 'email'],
]);
Good:
$validated = $request->validate([
'email' => ['required', 'email'],
]);
User::create($validated);
3. Don’t Automatically Store $request->all()
Avoid:
User::create($request->all());
Prefer:
$validated = $request->validate([
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]);
User::create($validated);
This makes it clear which fields your application accepts.
4. Use Form Requests for Complex Forms
If a controller contains dozens of validation rules, move them into a Form Request.
Instead of:
public function store(Request $request)
{
// 50 lines of validation...
}
use:
public function store(StoreProductRequest $request)
{
$validated = $request->validated();
// Business logic
}
This improves readability and maintainability.
5. Use nullable for Optional Fields
Don’t assume optional means “no validation.”
For example:
'phone' => [
'nullable',
'string',
]
This means:
- It can be empty/null.
- If provided, it must be a string.
6. Use Database Rules
If an ID must exist:
'exists:categories,id'
If a value must be unique:
'unique:users,email'
Database validation is important because client-side checks cannot guarantee database consistency.
7. Validate Uploaded Files Carefully
Don’t simply trust:
.jpg
.png
.pdf
Validate uploaded files using Laravel’s file-related rules and appropriate MIME/content checks. Laravel specifically recommends not relying on the user-provided extension alone.
8. Return User-Friendly Errors
Technical field names aren’t always appropriate for users.
Instead of:
first_name.required
provide a clear message:
Please enter your first name.
9. Use old() Carefully
Repopulate ordinary form fields:
value="{{ old('name') }}"
But don’t repopulate sensitive values such as passwords.
10. Validate Business Rules
Validation isn’t only about data types.
For example:
'discount' => [
'numeric',
'min:0',
'max:100',
]
can enforce business requirements.
For more complex rules, use custom validation rules or application/domain logic.
Common Laravel Validation Mistakes
Mistake 1: Only Using Client-Side Validation
JavaScript can be bypassed.
Always validate on the server.
Mistake 2: Using $request->all()
This can unintentionally pass fields you didn’t intend to process.
Use validated input instead.
Mistake 3: Forgetting nullable
Optional date or numeric fields often need:
nullable
when null should be accepted.
Mistake 4: Forgetting Unique Validation on Registration
A registration form should generally ensure that identifiers such as email addresses or usernames meet the application’s uniqueness requirements.
Mistake 5: Incorrect Unique Rule During Updates
When updating records, remember to ignore the current record:
Rule::unique('users', 'email')->ignore($user->id)
Mistake 6: Putting Too Much Validation in Controllers
If validation becomes large, use Form Requests.
Mistake 7: Not Validating Nested Arrays
For dynamic forms, validate both the parent array and individual items:
'items' => ['required', 'array'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
Testing Laravel Validation
Validation should also be tested.
Laravel provides testing support and includes PHPUnit/Pest support in the framework ecosystem.
A feature test can verify that invalid input produces the expected validation response.
For example:
$response = $this->post('/register', [
'name' => '',
'email' => 'invalid-email',
'password' => 'short',
]);
$response->assertSessionHasErrors([
'name',
'email',
'password',
]);
You can also test successful submissions:
$response = $this->post('/register', [
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'StrongPassword123!',
'password_confirmation' => 'StrongPassword123!',
]);
$response->assertSessionDoesntHaveErrors();
Laravel is designed with testing in mind and provides built-in testing infrastructure for application behavior.
Laravel Form Validation vs Client-Side Validation
Client-side and server-side Laravel form validation should complement each other rather than replace one another.
| Feature | Client-Side | Laravel Server-Side |
|---|---|---|
| User experience | Excellent | Good |
| Prevents invalid requests | No | Yes |
| Can be bypassed | Yes | Much harder |
| Database validation | No | Yes |
| Business rules | Limited | Yes |
| API protection | No | Yes |
| Immediate feedback | Yes | Usually after request |
| Security layer | No | Yes |
The ideal approach is:
Client-side validation
+
Laravel server-side validation
=
Better UX + Reliable application
Laravel Validation for Modern Applications
Laravel validation works particularly well across different application architectures.
Blade Applications
Use:
$request->validate(...)
or Form Requests.
Display errors with:
@error('field')
{{ $message }}
@enderror
Laravel APIs
Use the same Laravel form validation rules and return JSON responses.
$request->validate([
'email' => ['required', 'email'],
]);
Frontend applications can consume Laravel’s validation response.
React or Vue Frontends
If Laravel is acting as an API backend, validation remains on the Laravel side.
The frontend can display errors returned from the API.
This is particularly useful for applications built using:
- React
- Vue
- Next.js
- Mobile applications
- SPA architectures
Advanced Validation: after
Sometimes initial validation is not enough.
For example, you may need to check a business condition after the normal validation rules have passed.
Form Requests provide an after mechanism for additional validation. Laravel’s documentation describes using after callbacks to perform additional validation with a Validator instance.
This is useful when the condition is more complex than a normal rule.
However, don’t put every business rule into validation. Some business logic belongs in services, policies, domain logic, or database constraints.
Validation and Database Constraints
Laravel validation is important, but database constraints should still be used where appropriate.
For example, if email addresses must be unique, consider both:
'email' => [
'required',
'email',
'unique:users,email',
]
and a database-level unique constraint.
Why?
Because validation happens before the database operation, but concurrent requests can still create race conditions.
A robust application uses:
Application validation
+
Database constraints
rather than relying exclusively on either one.
Validation and Security
Validation is an important security layer, but it isn’t the entire security strategy.
You should also consider:
- Authentication
- Authorization
- CSRF protection
- Mass-assignment protection
- Output escaping
- File upload security
- Rate limiting
- Database constraints
- Secure password hashing
- Proper access control
For example, validating:
'role' => ['in:user,editor']
doesn’t automatically mean every user should be allowed to submit editor.
Authorization should determine whether the current user is permitted to assign that role.
Validation and authorization solve different problems.
When Should You Use Form Requests?
A simple form may only need:
$request->validate([
'name' => ['required'],
'email' => ['required', 'email'],
]);
A larger application may benefit from:
php artisan make:request StoreProductRequest
Use Form Requests when:
- Validation contains many rules.
- Authorization is related to the request.
- Multiple controllers use similar validation.
- Custom messages are required.
- Input needs preprocessing.
- You want thin controllers.
- You want better testability and maintainability.
A useful architecture is:
Route
↓
Form Request
↓
Controller
↓
Service / Domain Logic
↓
Model / Database
This keeps responsibilities separated.
Frequently Asked Questions
What is Laravel form validation?
Laravel form validation is the framework’s built-in mechanism for checking incoming request data against predefined validation rules before the application processes that data.
How do I validate a form in Laravel?
Use the request’s validate() method:
$request->validate([
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]);
How do I display validation errors in Laravel?
In Blade, use:
@error('email')
{{ $message }}
@enderror
Or display all errors using $errors.
How do I keep form values after Laravel form validation fails?
Use the old() helper:
<input
type="text"
name="name"
value="{{ old('name') }}"
>
What is a Laravel Form Request?
A Form Request is a dedicated request class that contains Laravel form validation and authorization logic.
Create one with:
php artisan make:request StorePostRequest
What is the difference between validate() and Validator::make()?
$request->validate() is convenient and integrates directly with Laravel’s request/redirect behavior.
Validator::make() provides more explicit control over the validator instance and is useful for custom validation workflows.
How do I validate an email in Laravel?
Use:
'email' => [
'required',
'email',
]
How do I validate a unique email?
Use:
'email' => [
'required',
'email',
'unique:users,email',
]
For updates, use Rule::unique()->ignore() to exclude the current record.
How do I validate an uploaded image?
For example:
'image' => [
'required',
'image',
'max:2048',
]
Additional MIME, extension, and dimension rules can be added according to your application’s requirements.
What HTTP status does Laravel return for API validation errors?
For XHR requests, Laravel returns validation errors with HTTP status 422.
References
For the complete and version-specific list of Laravel validation rules and advanced laravel validation features, refer to the official Laravel Validation Documentation.
Conclusion
Laravel Form Validation provides a clean and powerful way to protect your Laravel application‘s data and enforce application requirements.
For simple forms, Laravel’s request validation syntax is often all you need:
$request->validate([
'name' => ['required', 'string'],
'email' => ['required', 'email'],
]);
As your application grows, you can move validation into Form Request classes:
php artisan make:request StorePostRequest
You can then use advanced features such as:
- Laravel Custom validation messages
- Conditional validation
- Database validation
- Unique and exists rules
- File and image validation
- Nested array validation
- Password rules
- Laravel Custom validation rules
- Form Request authorization
- Input preparation
- Additional validation
- API validation
The most important principle is to never trust incoming user input. Validate data on the server, process only validated values, enforce important constraints at the database level, and keep complex validation logic organized using Form Requests and custom rules.
Once you understand Laravel validation, you can build everything from simple contact forms to large admin panels, ecommerce checkout systems, authentication flows, and API-driven applications with much greater confidence.