Originally published on laravel.learnio.dev — part of the Laravel best practices course. View this lesson on the original site.
Validate input before it reaches your business logic
Validation is your first safety boundary. It protects controllers and services from malformed or incomplete payloads.
Start with request validation in controllers
For simple actions, validate inline:
public function store(Request $request)
{
$data = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email'],
'password' => ['required', 'min:8'],
]);
// Use $data, not raw input
}
If validation fails, Laravel returns a proper error response automatically.
Move complex rules into form requests
Generate one:
php artisan make:request StoreUserRequest
Controller:
public function store(StoreUserRequest $request, CreateUserService $service)
{
$service->handle($request->validated());
return to_route('users.index');
}
This keeps controllers focused on orchestration.
Example request class app/Http/Requests/StoreUserRequest.php:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email'],
'password' => ['required', 'string', 'min:8'],
];
}
}
Work with validated subsets
Sometimes you only need part of validated input:
$safe = $request->safe()->only(['name', 'email']);
$withoutMeta = $request->safe()->except(['debug_flag']);
This helps avoid passing unnecessary fields deeper into your app.
Bad way vs better way
Bad way (trust and pass everything):
public function store(Request $request, CreateUserService $service)
{
$service->handle($request->all());
}
Better way (validate first, then pass trusted payload):
public function store(StoreUserRequest $request, CreateUserService $service)
{
$service->handle($request->validated());
}
Why better:
- less risk of unexpected fields reaching business logic,
- clearer data contract between controller and service,
- easier tests and safer refactors.
Practical rules for this chapter stage
- validate at the HTTP boundary,
- use form requests once rules grow,
- pass validated arrays, not raw request bags,
- keep business rules outside the controller method.
Validation flow
flowchart LR
A[Form or API payload] --> B[Validation rules]
B --> C[Validated data]
C --> D[Service or domain action]
D --> E[Redirect or JSON response]
Next, you will use this same validation baseline with route model binding to simplify lookup and not-found behaviour.
From Laravel best practices — Routing, Controllers & Request Flow