Objectifs de cette leçon
- Valider les données avec les règles Laravel
- Créer des validations personnalisées
- Gérer les erreurs de validation côté client
Validation et gestion des erreurs ✅
Pourquoi valider les données ?
Quand un client envoie des données à ton API (via POST, PUT, etc.), tu ne peux jamais faire confiance aux entrées. Les données peuvent être :
- Manquantes : un champ obligatoire absent
- Mal formatées : un email sans @, un âge négatif
- Malveillantes : tentative d'injection SQL, XSS
- Incohérentes : une date de naissance future, un prix négatif
💡 Règle d'or : Never trust user input. — Toujours valider, toujours filtrer.
La méthode validate() 🛡️
Laravel propose une méthode validate() directement sur l'objet Request :
public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', 'age' => 'required|integer|min:18', ]); // $validated contient uniquement les données validées $user = User::create($validated); return response()->json($user, 201); }
Que se passe-t-il ?
- Laravel teste chaque champ contre ses règles
- Si tout passe →
validate()retourne les données validées - Si une règle échoue → Laravel lève une ValidationException
- L'exception est automatiquement convertie en réponse JSON 422 Unprocessable Entity
Les règles de validation courantes 📋
Règles de présence
| Règle | Description |
|---|---|
required | Le champ doit être présent et non vide |
present | Le champ doit être présent (peut être vide) |
filled | Le champ ne doit pas être vide s'il est présent |
Règles de type
| Règle | Description |
|---|---|
string | Doit être une chaîne de caractères |
integer | Doit être un entier |
numeric | Doit être un nombre (entier ou décimal) |
boolean | Doit être true, false, 1, 0, "1", "0" |
array | Doit être un tableau |
date | Doit être une date valide |
Règles de contenu
| Règle | Description |
|---|---|
email | Format email valide |
url | Format URL valide |
min:N | Valeur minimale (nombre, string, array) |
max:N | Valeur maximale (nombre, string, array) |
between:N,M | Entre N et M (inclus) |
size:N | Taille exacte |
Règles de base de données
| Règle | Description |
|---|---|
unique:table,column | La valeur doit être unique dans la table |
exists:table,column | La valeur doit exister dans la table (clé étrangère) |
distinct | Pas de doublons dans un tableau |
Format alternatif : tableau de règles
Au lieu de la syntaxe |, tu peux utiliser un tableau :
$validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', Rule::unique('users')], 'age' => ['required', 'integer', 'min:18'], ]);
💡 La syntaxe tableau est plus lisible pour les règles complexes. Utilise
Rule::unique()etRule::exists()pour la version objet.
Messages d'erreur personnalisés ✏️
Par défaut, Laravel retourne des messages en anglais. Tu peux les personnaliser :
$validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', 'age' => 'required|integer|min:18', ], [ 'name.required' => 'Le nom est obligatoire.', 'email.required' => 'L\'email est obligatoire.', 'email.email' => 'Format d\'email invalide.', 'age.min' => 'Vous devez avoir au moins :min ans.', ]); ``' ### Réponse d'erreur typique (422) ```json { "message": "Le nom est obligatoire. (and 2 more errors)", "errors": { "name": ["Le nom est obligatoire."], "email": ["L'email est obligatoire."], "age": ["Vous devez avoir au moins 18 ans."] } }
Gestion manuelle des erreurs avec try/catch ⚡
Si tu veux un contrôle plus fin, capture ValidationException :
use Illuminate\Validation\ValidationException; public function store(Request $request) { try { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', ]); $user = User::create($validated); return response()->json($user, 201); } catch (ValidationException $e) { // Log l'erreur Log::warning('Validation échouée', [ 'errors' => $e->errors(), 'input' => $request->except('password'), ]); // Retourne une réponse personnalisée return response()->json([ 'success' => false, 'message' => 'Données invalides', 'errors' => $e->errors(), ], 422); } }
Form Request — La classe dédiée 🎯
Pour les validations complexes, crée une classe Form Request :
php artisan make:request StoreUserRequest
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreUserRequest extends FormRequest { public function authorize(): bool { return true; // Gère les permissions } public function rules(): array { return [ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', 'age' => 'required|integer|min:18', ]; } public function messages(): array { return [ 'name.required' => 'Le nom est obligatoire.', 'age.min' => 'Vous devez avoir au moins :min ans.', ]; } }
Puis utilise-la directement dans le contrôleur :
public function store(StoreUserRequest $request) { // Déjà validé ! $request->validated() contient les données propres $user = User::create($request->validated()); return response()->json($user, 201); }
💡 Les Form Requests sont automatiquement injectées par Laravel. La validation s'exécute avant l'entrée dans la méthode.
Règles avancées 🧠
Validation conditionnelle
$validated = $request->validate([ 'role' => 'required|in:user,admin', // 'permissions' n'est requis que si role = admin 'permissions' => 'required_if:role,admin|array', ]);
Règles personnalisées avec closure
use Illuminate\Support\Str; $validated = $request->validate([ 'slug' => [ 'required', 'string', function (string $attribute, mixed $value, Closure $fail) { if (Str::contains($value, 'admin')) { $fail("Le slug ne peut pas contenir 'admin'."); } }, ], ]);
Règles personnalisées avec classe
php artisan make:rule Uppercase
<?php namespace App\Rules; use Closure; use Illuminate\Contracts\Validation\ValidationRule; class Uppercase implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void { if (strtoupper($value) !== $value) { $fail('Le champ :attribute doit être en majuscules.'); } } }
'name' => ['required', new Uppercase],
Bonnes pratiques ✅
- Toujours valider avant d'enregistrer — même en dev, même pour un test
- Utiliser les Form Requests pour les validations complexes (réutilisabilité)
- Personnaliser les messages pour une meilleure UX frontend
- Logger les erreurs de validation pour le débogage
- Utiliser
sometimespour les mises à jour partielles (PUT/PATCH) - Préférer les règles précises :
numericplutôt queintegersi tu acceptes les décimaux
Pièges fréquents 🚨
| Piège | Solution |
|---|---|
❌ Oublier required | Un champ optionnel passe silencieusement avec null |
❌ unique sans gestion des exceptions | Impossible de créer deux utilisateurs avec le même email |
❌ Faire confiance à required avec des espaces | Une chaîne d'espaces passe required |
| ❌ Valider uniquement en frontend | La validation frontend est contournable |
| ❌ Ignorer les messages en anglais | Utilise les fichiers de langage dans lang/ |
Exercices pour toi 🎯
- Pratique : Ajoute une validation à
ProductController@store: nom requis (max:255), prix requis (numeric, min:0), stock optionnel (integer, min:0). - Messages : Personnalise les messages d'erreur en français pour les 3 champs.
- Form Request : Crée
StoreProductRequestet déplace la validation dedans. - Règle personnalisée : Crée une règle
PositiveNumberqui valide qu'un nombre est strictement positif. - Défi : Ouvre le simulateur ci-dessous. Observe les différents scénarios : données valides → 200, données invalides → 422 avec erreurs.
⚡ Simulateur — Validation des données
Étape 1/6Les règles de validation sont déclaratives. Laravel les applique une par une dans l'ordre.