Requests & Validation

Once a request has passed through routing and any middleware, it lands in your controller method — and Laravel hands you everything about that request through a single object. This lesson covers reading data off that object and, just as importantly, validating it before you trust it enough to save into your database.

The Request Object

Every controller method can simply type-hint Illuminate\Http\Request as a parameter, and Laravel automatically injects the current request into it — no manual wiring needed, thanks to the service container (covered in full in a later lesson). That single $request object gives you access to form fields, query string parameters, uploaded files, headers, and more.

<?php
// app/Http/Controllers/ArticleController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ArticleController extends Controller
{
    public function store(Request $request)
    {
        $title = $request->input('title');
        // ...
    }
}

Reading Input

MethodWhat it returns
$request->input('title')A single named field, from either the query string or the request body — the most common way to read one value.
$request->all()Every piece of input on the request as a plain array — handy for passing straight into mass assignment, though usually you validate first.
$request->query('page')Reads specifically from the URL's query string (e.g. ?page=2), ignoring the request body — useful when you specifically want a query parameter and nothing else.

Inline Validation

Trusting raw user input is never safe — a form field can be empty, too long, malformed, or missing entirely, and your database (or your application logic) will break in confusing ways if you don't check first. The simplest way to validate in Laravel is calling validate() directly on the request inside your controller, passing an array of field names mapped to pipe-separated rule strings.

<?php
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'email' => 'required|email',
    ]);

    Article::create($validated);
}

Common Validation Rules

RuleWhat it checks
requiredThe field must be present and not empty.
emailMust be a validly formatted email address.
max:255 / min:3Maximum or minimum length (for strings) or value (for numbers).
unique:users,emailNo existing row in the users table's email column already has this value — essential for things like registration emails.
confirmedRequires a matching *_confirmation field — e.g. a password field with confirmed requires a password_confirmation field with the same value. The classic password-confirmation pattern.
exists:categories,idThe value must match an existing row's column — e.g. confirming a submitted category_id actually refers to a real category.
Rules stack: Combine as many rules as needed for one field, separated by pipes — 'email' => 'required|email|unique:users,email' checks all three at once, and validation fails if any one of them doesn't pass.

What Happens Automatically on Failure

This is one of the most convenient parts of Laravel's validation: you never write the "what if it fails" branch yourself for a typical web form. If any rule fails, validate() throws an exception internally that Laravel already knows how to handle — it automatically redirects the user back to the previous page (usually the form they just submitted), and flashes the validation errors into the session so the next page load can display them. Your controller code after the validate() call simply never executes on a failed request.

Displaying Errors and Repopulating Old Input

Since the errors are flashed to the session and the old input is available too, your Blade view can display both without any extra controller code. The @error directive checks whether a specific field failed validation, and the old() helper fetches the value the user previously typed for a given field — a genuinely important UX detail, since without it a failed form submission would come back completely blank, forcing the user to retype everything.

<!-- resources/views/articles/create.blade.php -->
<form method="POST" action="/articles">
    @csrf

    <label>Title</label>
    <input type="text" name="title" value="{{ old('title') }}">

    @error('title')
        <span class="error">{{ $message }}</span>
    @enderror

    <button type="submit">Save</button>
</form>

On a failed submission, old('title') refills the text box with whatever the user typed, and @error('title') ... @enderror prints the specific error message for that field ("The title field is required.", for example) right next to it — without you ever writing an if statement to check for errors yourself.

Form Request Classes

Inline validation is great for a quick controller method, but once a form has many rules, or the same rules are needed in more than one place, cramming them into the controller starts to clutter it. The preferred approach for anything non-trivial is a dedicated Form Request class — its whole job is to hold the validation rules (and, optionally, an authorization check) for one specific request, separate from the controller's actual logic.

# From your project's terminal
php artisan make:request StoreArticleRequest

This generates app/Http/Requests/StoreArticleRequest.php with two methods to fill in:

<?php
// app/Http/Requests/StoreArticleRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreArticleRequest extends FormRequest
{
    public function authorize()
    {
        return true; // or a real permission check
    }

    public function rules()
    {
        return [
            'title' => 'required|max:255',
            'email' => 'required|email',
        ];
    }
}

The authorize() method decides whether this user is even allowed to make this request at all (returning false stops it with a 403, before validation even runs); the rules() method is exactly the same rule array you'd have passed to $request->validate() inline.

The refactor in the controller is small but meaningful: swap the type-hint from the generic Request to your new Form Request class, and Laravel runs the validation automatically before your method body even executes.

<?php
// Before: inline validation in the controller
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'email' => 'required|email',
    ]);

    Article::create($validated);
}

// After: rules moved into StoreArticleRequest
use App\Http\Requests\StoreArticleRequest;

public function store(StoreArticleRequest $request)
{
    Article::create($request->validated());
}

The controller method is now purely about what to do with valid data — creating the article — and has no idea what the actual validation rules are. If those rules ever need reuse (an API endpoint that accepts the same fields, for instance), the Form Request class can be reused directly instead of copy-pasting the rule array.

Next up: Now that you can safely validate incoming data, the next lesson covers where that data usually ends up — saving and retrieving it with Laravel's ORM, in Eloquent ORM Basics.