Laravel validation is de schoonste manier om binnenkomende requestdata veilig en consistent te controleren voordat die ook maar in de buurt van je controllerlogica komt. Goed opgezette validatie geeft gebruikers duidelijke feedback en voorkomt dat misvormde data ooit je database raakt. In dit artikel loop ik door de aanpak die ik in de praktijk echt gebruik, van de regelsyntaxis tot Form Request-klassen, eigen regels en het beheer van foutmeldingen.
De snelste route: validate() in de controller
Voor kleine formulieren is het meest praktische de methode $request->validate() rechtstreeks in de controller aan te roepen. Slagen de regels, dan gaat de uitvoering door; falen ze, dan stuurt Laravel automatisch terug en plaatst de fouten in de sessie (of geeft een 422 terug bij JSON-requests).
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'email' => 'required|email',
'age' => 'nullable|integer|min:18',
'tags' => 'array',
'tags.*' => 'string|max:30',
]);
Post::create($validated);
return redirect()->route('posts.index');
}
Een belangrijk detail: validate() geeft alleen de velden terug die je daadwerkelijk hebt gevalideerd. Dat is ook winst voor de veiligheid, want het voorkomt dat extra, ongewenste velden in het model lekken (mass assignment). In plaats van regels met pipes aan elkaar te knopen kun je ook de array-vorm gebruiken; bij complexe regels met Rule-objecten is de array-vorm veel leesbaarder.
Veelgebruikte regels en hun logica
Laravel komt met honderden ingebouwde regels. In het dagelijks werk grijp je het meest naar deze:
required/nullable— of een veld verplicht is of leeg mag blijven.string,integer,boolean,array,date— typecontroles.min/max/between— waarde voor getallen, tekens voor strings, aantal elementen voor arrays.email,url,uuid— formaatvalidatie.unique:table,columnenexists:table,column— controles op basis van de database.confirmed— vereist datpasswordovereenkomt met een veldpassword_confirmation.
Voor conditionele logica zijn varianten als required_if, required_with en required_without reddend. Bijvoorbeeld "kaartnummer is verplicht wanneer het betaaltype kaart is" wordt één regel: 'card_number' => 'required_if:payment,card'.
Form Request: haal validatie uit de controller
Naarmate de regels groeien, raakt de controller opgeblazen. De juiste oplossing is een Form Request-klasse maken. Die bundelt autorisatie, regels en meldingen op één plek.
php artisan make:request StorePostRequest
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'slug' => ['required', 'string', Rule::unique('posts')],
'body' => ['required', 'string'],
];
}
}
Vervolgens type-hint je deze klasse simpelweg in de controllermethode. Laravel valideert de request automatisch en betreedt de methode nooit bij een fout:
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
Een false uit authorize() levert een 403 op; regel je autorisatie in een aparte laag, dan kun je deze methode gewoon op return true; laten staan. De Form Request-aanpak maakt ook het testen makkelijker, omdat je de regels geïsoleerd kunt valideren.
Eigen regels en closure-regels
Wanneer de ingebouwde regels tekortschieten, heb je twee opties. Voor een eenmalige controle is een closure het snelst:
'slug' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
if (str_contains($value, ' ')) {
$fail("Het veld $attribute mag geen spaties bevatten.");
}
},
],
Hergebruik je dezelfde regel op meerdere plekken, dan is een herbruikbare regelklasse netter:
php artisan make:rule Uppercase
class Uppercase implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('The :attribute must be uppercase.');
}
}
}
Het gebruik is net als bij elke andere regel: 'code' => ['required', new Uppercase]. De plaatshouder :attribute in de melding wordt automatisch vervangen door de veldnaam.
Foutmeldingen beheren
De standaardmeldingen zijn in het Engels en generiek. Voor vriendelijkere, gelokaliseerde meldingen overschrijf je de methoden messages() en attributes() in de Form Request:
public function messages(): array
{
return [
'title.required' => 'Het titelveld is verplicht.',
'title.max' => 'De titel mag niet langer zijn dan :max tekens.',
];
}
public function attributes(): array
{
return ['title' => 'titel'];
}
Aan de Blade-kant zijn de directive @error en de helper old() alles wat je nodig hebt; old() voorkomt dat de invoer van de gebruiker verdwijnt wanneer het formulier opnieuw wordt getoond:
<input name="title" value="{{ old('title') }}">
@error('title')
<span class="error">{{ $message }}</span>
@enderror
In meertalige projecten is het onderhoudsvriendelijker om meldingen niet hard in de code te zetten, maar de vertaalbestanden in de map lang/ te gebruiken. Zo kun je dezelfde set regels consistent in verschillende talen aanbieden.
Veelgestelde vragen
Wat is het verschil tussen validate() en een Form Request?
Beide gebruiken dezelfde engine. validate() is ideaal voor snelle, kleine formulieren; een Form Request bundelt regels, autorisatie en meldingen in een aparte klasse, houdt de controller schoon en maakt hergebruik makkelijk.
Hoe worden validatiefouten teruggegeven bij API-requests?
Draagt de request een Accept: application/json-header, dan stuurt Laravel niet terug; het geeft een JSON-respons met de status 422 Unprocessable Entity en een errors-object. De frontend kan deze structuur direct verwerken.
Kan ik een veld alleen onder een bepaalde voorwaarde valideren?
Ja. Je kunt conditionele regels als required_if gebruiken, of tijdens runtime dynamische regels toevoegen met de methode sometimes() in een Form Request.
De validatielaag is het veiligheidsfundament van je project. Wil je je Laravel validation-architectuur schoon en testbaar maken, of de complexe regels in een bestaand formulier opruimen, neem dan contact met me op.