Laravel Validation

Laravel validation checks incoming data before you use it. With one method call you can require fields, check formats, compare values and query the database, and Laravel automatically redirects back with errors or returns a 422 JSON response for APIs.

Validating in a Controller

Only fields listed in the rules appear in $validated, which protects you from unexpected input.

<?php
public function store(Request $request)
{
    $validated = $request->validate([
        'name' => ['required', 'string', 'max:100'],
        'email' => ['required', 'email', 'unique:users,email'],
        'age' => ['nullable', 'integer', 'between:18,120'],
        'password' => ['required', 'min:8', 'confirmed'],
    ]);

    User::create($validated);
    return to_route('users.index');
}
Invalid input -> redirect back with errors and old input
API request   -> 422 {"message":"The email has already been taken.","errors":{"email":["The email has already been taken."]}}

Common Laravel Validation Rules

  • required, nullable, sometimes
  • string, integer, numeric, boolean, array, date
  • email, url, uuid, ip
  • min:, max:, between:, size:
  • in:draft,published and not_in:
  • unique:table,column and exists:table,column
  • confirmed (needs a field_confirmation input)
  • image, mimes:pdf,docx, max:2048 for files (kilobytes)

Showing Errors in Blade

The @error directive shows a message for one field, and old() refills the input after a failed submit.

<form method="POST" action="{{ route('users.store') }}">
    @csrf
    <input name="email" value="{{ old('email') }}">
    @error('email')
        <p class="error">{{ $message }}</p>
    @enderror
    <button>Save</button>
</form>
<input name="email" value="sara@example">
<p class="error">The email field must be a valid email address.</p>

Form Request Classes

For bigger forms, move rules into a Form Request. It keeps controllers clean and can also authorize the request.

php artisan make:request UpdatePostRequest

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('update', $this->route('post'));
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'max:200', Rule::unique('posts')->ignore($this->route('post'))],
            'status' => ['required', Rule::in(['draft', 'published'])],
        ];
    }

    public function messages(): array
    {
        return ['title.unique' => 'Another post already uses this title.'];
    }
}

// Controller
public function update(UpdatePostRequest $request, Post $post)
{
    $post->update($request->validated());
    return back()->with('status', 'Updated');
}
Unauthorized user -> 403 This action is unauthorized.
Duplicate title   -> "Another post already uses this title."

Manual Validators

Use the Validator facade when you need to validate data that is not a request, such as rows from a CSV import.

<?php
use Illuminate\Support\Facades\Validator;

$v = Validator::make(['qty' => -3], ['qty' => 'integer|min:1']);
if ($v->fails()) {
    echo $v->errors()->first('qty');
}
The qty field must be at least 1.

Frequently asked questions

What is a Form Request in Laravel?

A dedicated class created with make:request that holds validation rules, custom messages and authorization for one request. Type-hint it in a controller and validation runs automatically.

How do I ignore the current record in a unique rule?

Use Rule::unique("users")->ignore($user->id) so updating a record does not fail against its own value.

Why does Laravel return 422?

422 Unprocessable Content is the status Laravel uses for validation failures on JSON requests, with an errors object listing messages per field.