Form requests
When a form has ten fields, the validation rules take up half of the controller method. A request class — a Form Request — takes them over. The controller receives data that is already validated and minds its own business.
Creating
php artisan make:request StorePostRequestThe class appears in app/Http/Requests.
Rules
The rules() method returns the rules. They are conveniently written as arrays:
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'],
'category_id' => ['required', 'exists:categories,id'],
'tags' => ['array'],
'tags.*' => ['integer', 'exists:tags,id'],
];
}
}The exists rule checks that such a record is in the table. The tags.* form sets the rules for every element of an array.
Using it in a controller
It is enough to name the class as the parameter type. Validation runs before the method is entered:
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
return redirect()->route('posts.show', $post);
}validated() returns only the fields that have rules. Extra data from the request never reaches the database.
Checking permissions
The authorize() method decides whether the request may run at all. If it returns false, the user gets a 403 error:
public function authorize(): bool
{
return $this->user()->can('update', $this->route('post'));
}Custom messages
public function messages(): array
{
return [
'title.required' => 'Please enter the post title.',
'title.max' => 'The title must not exceed 255 characters.',
];
}
public function attributes(): array
{
return [
'category_id' => 'category',
];
}attributes() replaces the field name in the standard messages with a readable one.
Preparing data
prepareForValidation() runs before validation — it is where data is brought into shape:
use Illuminate\Support\Str;
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Str::slug($this->title),
'is_published' => $this->boolean('is_published'),
]);
}Rules that depend on the record
When editing, uniqueness has to be checked while ignoring the record itself:
use Illuminate\Validation\Rule;
public function rules(): array
{
return [
'slug' => ['required', Rule::unique('posts')->ignore($this->route('post'))],
'status' => ['required', Rule::in(['draft', 'published'])],
];
}Common mistake
The $request->all() method returns everything the user sent, including fields that have no rules. Passing it to create() lets someone slip in an extra field — is_admin, for example. Take data only from validated().