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
| Method | What 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
| Rule | What it checks |
|---|---|
required | The field must be present and not empty. |
email | Must be a validly formatted email address. |
max:255 / min:3 | Maximum or minimum length (for strings) or value (for numbers). |
unique:users,email | No existing row in the users table's email column already has this value — essential for things like registration emails. |
confirmed | Requires 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,id | The value must match an existing row's column — e.g. confirming a submitted category_id actually refers to a real category. |
'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.