Skip to content

Chapitre 2.7 : Gestion des erreurs

Temps d'étude : 40 minutes


1. Pourquoi les erreurs standards sont-elles mauvaises ?

Si une erreur se produit dans votre application Laravel (par exemple, un enregistrement introuvable dans la base de données) et que vous ne l'avez pas gérée, l'utilisateur verra une énorme page HTML avec des informations de débogage ou un message non informatif "Server Error".

Pour une API, c'est une catastrophe. Votre application frontend s'attend à recevoir du JSON, pas du HTML. Notre tâche est d'intercepter toute erreur et de la transformer en une réponse JSON structurée.


2. Dispatcher d'erreurs central : bootstrap/app.php

Dans les anciennes versions de Laravel, il y avait un fichier volumineux App\Exceptions\Handler.php. Dans Laravel 11/12, tout est devenu beaucoup plus simple et élégant. Le centre de gestion des erreurs se trouve désormais directement dans le fichier de configuration de votre application — bootstrap/app.php.

Ouvrez bootstrap/app.php. Tout en bas, vous verrez le bloc .withExceptions(...). C'est notre "dispatcher central".

<?php
// bootstrap/app.php

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        // ...
    })
    ->withExceptions(function (Exceptions $exceptions) {
        // <-- C'EST ICI QUE NOUS ALLONS TRAVAILLER
    })->create();

3. Gérer l'erreur la plus courante : "Non trouvé" (404)

L'erreur la plus courante dans une API est lorsque l'utilisateur demande une ressource qui n'existe pas (par exemple, GET /api/planets/999). Dans ce cas, Laravel génère une exception ModelNotFoundException ou NotFoundHttpException. Interceptons-les.

Ajoutez le code suivant à l'intérieur de .withExceptions(...) :

<?php
// bootstrap/app.php

->withExceptions(function (Exceptions $exceptions) {

    // Intercepte l'exception lorsque le modèle n'est pas trouvé dans la base de données
    $exceptions->render(function (ModelNotFoundException $e, Request $request) {
        // Vérifie que la requête provient bien de notre API
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'La ressource demandée est introuvable dans notre galaxie.'
            ], 404);
        }
    });

    // Intercepte l'exception lorsque le chemin lui-même n'est pas trouvé
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Une telle route spatiale n\'existe pas.'
            ], 404);
        }
    });

})->create();
Qu'avons-nous fait ?

  1. $exceptions->render(...) — nous enregistrons un "gestionnaire". Il dit : "Si une exception de type ModelNotFoundException se produit, exécute ce code".
  2. if ($request->is('api/*')) — c'est une vérification importante. Elle garantit que notre belle réponse JSON ne sera envoyée que pour les requêtes API, sans affecter les pages web normales.
  3. return response()->json(...) — nous créons et renvoyons une réponse JSON standardisée avec le code 404.

Maintenant, si vous demandez une planète inexistante, au lieu d'une page HTML disgracieuse, vous obtiendrez un JSON propre.


4. Exceptions personnalisées : Créer nos propres "alertes"

Parfois, les exceptions standards ne suffisent pas. Imaginons que nous ayons une règle métier : "il est interdit de supprimer la planète 'Terre'". Si quelqu'un tente de le faire, nous devons renvoyer une erreur significative.

Étape 1 : Créer notre classe d'exception Exécuter dans le terminal :

php artisan make:exception CannotDeleteEarthException

Étape 2 : L'utiliser dans le contrôleur Ouvrez PlanetController.php et modifiez la méthode destroy :

<?php
// app/Http/Controllers/PlanetController.php
use App\Exceptions\CannotDeleteEarthException; // <-- Importons notre exception
use App\Models\Planet;

public function destroy(Planet $planet)
{
    // Notre nouvelle règle métier
    if (strtolower($planet->name) === 'земля') {
        throw new CannotDeleteEarthException('La suppression de la planète Terre est interdite par le Code Galactique.');
    }

    $planet->delete();
    return response()->json(null, 204);
}
Maintenant, si quelqu'un tente d'exécuter DELETE /api/planets/1 (où 1 est l'ID de la Terre), notre code lèvera une exception CannotDeleteEarthException.

Étape 3 : Apprendre à Laravel à gérer joliment notre "alerte" Revenons à bootstrap/app.php et ajoutons un nouveau gestionnaire pour notre exception.

<?php
// bootstrap/app.php

->withExceptions(function (Exceptions $exceptions) {

    // Notre nouveau gestionnaire
    $exceptions->render(function (CannotDeleteEarthException $e, Request $request) {
        return response()->json([
            'message' => 'Opération interdite.',
            'details' => $e->getMessage() // Récupère le message que nous avons passé à throw
        ], 403); // 403 Forbidden - "Accès interdit"
    });

    // ... (autres gestionnaires pour 404)

})->create();
C'est fait ! Nous avons créé notre propre exception nommée, ce qui rend le code du contrôleur plus propre, et nous avons appris à Laravel à la transformer en une belle réponse JSON significative avec le bon statut HTTP.


5. Gestion de tous les autres échecs (500 Internal Server Error)

Que faire de toutes les autres erreurs imprévues ? Par exemple, si la base de données tombe en panne ou s'il y a une erreur de syntaxe dans le code. Pour cela, nous pouvons enregistrer un gestionnaire "universel" pour le type d'erreur le plus général — Throwable.

Important : Ce gestionnaire doit être le dernier pour ne pas intercepter les exceptions plus spécifiques que nous avons définies ci-dessus.

<?php
// bootstrap/app.php

->withExceptions(function (Exceptions $exceptions) {

    // ... (gestionnaires pour CannotDeleteEarthException et 404)

    // GESTIONNAIRE UNIVERSEL (à la toute fin)
    $exceptions->render(function (Throwable $e, Request $request) {
        if ($request->is('api/*')) {
            // En mode débogage, il est possible d'afficher le véritable message d'erreur
            $message = config('app.debug')
                ? 'Une erreur est survenue : ' . $e->getMessage()
                : 'Une erreur inattendue est survenue à bord. Les ingénieurs ont déjà été appelés.';

            return response()->json(['message' => $message], 500);
        }
    });

})->create();

Maintenant, toute exception "inconnue" sera proprement interceptée et transformée en JSON avec le code 500, sans casser votre API et sans montrer d'informations superflues à l'utilisateur.


6. Journalisation des erreurs : La boîte noire du vaisseau spatial

Paramètres de journalisation dans config/logging.php :

<?php
'channels' => [
    'space_api' => [
        'driver' => 'daily',
        'path' => storage_path('logs/space_api.log'),
        'level' => 'error',
        'days' => 14,
    ],
],

Ajouter une entrée au journal :

<?php
try {
    // Code avec risque d'erreur
} catch (Exception $e) {
    Log::channel('space_api')->error('Erreur d\'accès aux planètes', [
        'exception' => $e,
        'request' => request()->all(),
        'user_id' => auth()->id()
    ]);
    throw $e;
}


Quiz de révision

1. Statut HTTP pour "Planète introuvable" :

2. Classe pour le traitement global des erreurs :

3. Méthode pour créer une exception personnalisée :

4. Canal pour la journalisation séparée des erreurs d'API :

5. Principal avantage de la création d'exceptions personnalisées :


🚀 Résumé du chapitre :

Vous avez équipé votre API d'un système de sauvetage fiable : - 🛟 Interception globale des erreurs standard - 🪐 Exceptions personnalisées avec des codes clairs - 📝 Format JSON unifié pour toutes les erreurs - 🔍 Journalisation avec les détails de l'incident - 📡 Intégration avec les systèmes de surveillance

Le vaisseau spatial est prêt aux situations d'urgence ! Dans le chapitre final de cette section, nous allons tester tous les systèmes.

📌 Vérification :

  1. Créez une exception PlanetNotFoundException
  2. Ajoutez la gestion des erreurs 404 dans ->withExceptions
  3. Testez une requête vers une planète inexistante

⚠️ Si les erreurs ne sont pas interceptées :

  • Assurez-vous que is('api/*') correspond à vos routes
  • Vérifiez l'ordre des gestionnaires dans register()
  • Pour les exceptions personnalisées, utilisez throw new