0 XP
?
API REST et Backend avec Laravel/Validation et gestion des erreurs
Intermédiaire45 min30 XP

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 ?

  1. Laravel teste chaque champ contre ses règles
  2. Si tout passevalidate() retourne les données validées
  3. Si une règle échoue → Laravel lève une ValidationException
  4. L'exception est automatiquement convertie en réponse JSON 422 Unprocessable Entity

Les règles de validation courantes 📋

Règles de présence

RègleDescription
requiredLe champ doit être présent et non vide
presentLe champ doit être présent (peut être vide)
filledLe champ ne doit pas être vide s'il est présent

Règles de type

RègleDescription
stringDoit être une chaîne de caractères
integerDoit être un entier
numericDoit être un nombre (entier ou décimal)
booleanDoit être true, false, 1, 0, "1", "0"
arrayDoit être un tableau
dateDoit être une date valide

Règles de contenu

RègleDescription
emailFormat email valide
urlFormat URL valide
min:NValeur minimale (nombre, string, array)
max:NValeur maximale (nombre, string, array)
between:N,MEntre N et M (inclus)
size:NTaille exacte

Règles de base de données

RègleDescription
unique:table,columnLa valeur doit être unique dans la table
exists:table,columnLa valeur doit exister dans la table (clé étrangère)
distinctPas 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() et Rule::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 ✅

  1. Toujours valider avant d'enregistrer — même en dev, même pour un test
  2. Utiliser les Form Requests pour les validations complexes (réutilisabilité)
  3. Personnaliser les messages pour une meilleure UX frontend
  4. Logger les erreurs de validation pour le débogage
  5. Utiliser sometimes pour les mises à jour partielles (PUT/PATCH)
  6. Préférer les règles précises : numeric plutôt que integer si tu acceptes les décimaux

Pièges fréquents 🚨

PiègeSolution
❌ Oublier requiredUn champ optionnel passe silencieusement avec null
unique sans gestion des exceptionsImpossible de créer deux utilisateurs avec le même email
❌ Faire confiance à required avec des espacesUne chaîne d'espaces passe required
❌ Valider uniquement en frontendLa validation frontend est contournable
❌ Ignorer les messages en anglaisUtilise les fichiers de langage dans lang/

Exercices pour toi 🎯

  1. Pratique : Ajoute une validation à ProductController@store : nom requis (max:255), prix requis (numeric, min:0), stock optionnel (integer, min:0).
  2. Messages : Personnalise les messages d'erreur en français pour les 3 champs.
  3. Form Request : Crée StoreProductRequest et déplace la validation dedans.
  4. Règle personnalisée : Crée une règle PositiveNumber qui valide qu'un nombre est strictement positif.
  5. 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/6
📝 Pseudo-code
1# Validation des données entrantes
2$validated = $request->validate([ # ⬅ règles de validation
3 'name' => 'required|string|max:255',
4 'email' => 'required|email|unique:users',
5 'age' => 'required|integer|min:18',
6]);
7
8# Données valides → 200 OK (passent toutes les règles) # ⬅ succès
9# Données invalides → 422 avec erreurs # ⬅ échec
10
11try { $request->validate([...]); }
12catch (ValidationException $e) { # ⬅ gestion d'erreur
13 return response()->json($e->errors(), 422);
14}
✅ Validation des entrées
📋 Règles Valide Manquant Type✏️ Messages Try/Catch
Règles de validation
name=Alicerequired | string | max:255
email=alice@ex.comrequired | email | unique:users
age=25required | integer | min:18
💡 Définis les règles avec la syntaxe | (pipe). Chaque règle est testée dans l'ordre.
Les règles sont définies comme un tableau associatif : champ → règles séparées par |.
💡 Règles de validation courantes
required | Champ obligatoire
string | Doit être une chaîne
email | Format email valide
integer | Doit être un entier
min:18 | Valeur minimale
max:255 | Longueur max
unique:users | Valeur unique en BDD
exists:roles | Clé étrangère valide

Les règles de validation sont déclaratives. Laravel les applique une par une dans l'ordre.