La validation Laravel est la manière la plus propre de vérifier les données d'une requête entrante de façon sûre et cohérente, avant même qu'elles n'atteignent la logique de votre contrôleur. Une validation bien structurée donne aux utilisateurs un retour clair et empêche toute donnée malformée d'arriver jusqu'à la base de données. Dans cet article, je présente l'approche que j'utilise réellement en pratique, depuis la syntaxe des règles jusqu'aux classes Form Request, aux règles personnalisées et à la gestion des messages d'erreur.
La voie la plus rapide : validate() dans le contrôleur
Pour les petits formulaires, l'option la plus pratique est d'appeler directement $request->validate() dans la méthode du contrôleur. Si les règles passent, l'exécution continue ; sinon, Laravel redirige automatiquement en arrière et place les erreurs dans la session (ou renvoie un 422 pour les requêtes JSON).
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');
}
Un détail important : validate() ne retourne que les champs que vous avez réellement validés. C'est aussi un gain de sécurité, car cela empêche des champs supplémentaires et non désirés de se glisser dans le modèle (mass assignment). Plutôt que d'enchaîner les règles avec des barres verticales, vous pouvez aussi utiliser la forme tableau ; pour des règles complexes contenant des objets Rule, la forme tableau est bien plus lisible.
Règles courantes et leur logique
Laravel est livré avec des centaines de règles intégrées. Au quotidien, ce sont surtout celles-ci que vous utilisez :
required/nullable— indique si un champ est obligatoire ou peut rester vide.string,integer,boolean,array,date— contrôles de type.min/max/between— valeur pour les nombres, caractères pour les chaînes, nombre d'éléments pour les tableaux.email,url,uuid— validation de format.unique:table,columnetexists:table,column— contrôles basés sur la base de données.confirmed— exige quepasswordcorresponde à un champpassword_confirmation.
Pour la logique conditionnelle, des variantes comme required_if, required_with et required_without sont salvatrices. Par exemple, « le numéro de carte est requis lorsque le type de paiement est carte » tient en une seule ligne : 'card_number' => 'required_if:payment,card'.
Form Request : sortir la validation du contrôleur
À mesure que les règles grandissent, le contrôleur s'alourdit. La bonne solution est de créer une classe Form Request. Elle regroupe en un seul endroit l'autorisation, les règles et les messages.
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'],
];
}
}
Il suffit ensuite de typer cette classe dans la méthode du contrôleur. Laravel valide la requête automatiquement et n'entre jamais dans la méthode en cas d'échec :
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
Retourner false depuis authorize() produit un 403 ; si vous gérez l'autorisation dans une couche distincte, vous pouvez simplement laisser cette méthode à return true;. L'approche Form Request facilite aussi les tests, car vous pouvez valider les règles de façon isolée.
Règles personnalisées et règles closure
Lorsque les règles intégrées ne suffisent pas, vous avez deux options. Pour un contrôle ponctuel, une closure est la plus rapide :
'slug' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
if (str_contains($value, ' ')) {
$fail("Le champ $attribute ne peut pas contenir d'espaces.");
}
},
],
Si vous réutilisez la même règle à plusieurs endroits, générer une classe de règle réutilisable est plus propre :
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.');
}
}
}
L'usage est identique à celui de n'importe quelle règle : 'code' => ['required', new Uppercase]. Le marqueur :attribute dans le message est automatiquement remplacé par le nom du champ.
Gérer les messages d'erreur
Les messages par défaut sont en anglais et génériques. Pour des messages plus conviviaux et localisés, surchargez les méthodes messages() et attributes() dans le Form Request :
public function messages(): array
{
return [
'title.required' => 'Le champ titre est obligatoire.',
'title.max' => 'Le titre ne peut pas dépasser :max caractères.',
];
}
public function attributes(): array
{
return ['title' => 'titre'];
}
Côté Blade, la directive @error et l'assistant old() suffisent ; old() empêche la saisie de l'utilisateur de disparaître lorsque le formulaire est ré-affiché :
<input name="title" value="{{ old('title') }}">
@error('title')
<span class="error">{{ $message }}</span>
@enderror
Dans les projets multilingues, plutôt que de coder les messages en dur, il est plus maintenable d'utiliser les fichiers de traduction du répertoire lang/. Vous pouvez ainsi servir le même jeu de règles de façon cohérente dans différentes langues.
Questions fréquentes
Quelle est la différence entre validate() et un Form Request ?
Les deux utilisent le même moteur. validate() est idéal pour des formulaires petits et rapides ; un Form Request regroupe règles, autorisation et messages dans une classe dédiée, gardant le contrôleur propre et facilitant la réutilisation.
Comment les erreurs de validation sont-elles renvoyées pour les requêtes API ?
Si la requête porte un en-tête Accept: application/json, Laravel ne redirige pas ; il renvoie une réponse JSON avec un statut 422 Unprocessable Entity et un objet errors. Le frontend peut traiter cette structure directement.
Puis-je valider un champ uniquement sous certaines conditions ?
Oui. Vous pouvez utiliser des règles conditionnelles comme required_if, ou ajouter des règles dynamiques à l'exécution avec la méthode sometimes() dans un Form Request.
La couche de validation est le socle de sécurité de votre projet. Si vous souhaitez rendre votre architecture de validation Laravel propre et testable, ou ranger les règles complexes d'un formulaire existant, contactez-moi.