response()->acmeFailure(...) sans imposer aux contrôleurs d’hériter d’une classe de base.
Cette page s’appuie sur la documentation officielle de Laravel sur la branche par défaut 13.x et sur l’implémentation officielle de la dernière version du Framework, v13.40.0, vérifiées le 8 novembre 2026. En partant des bases des Réponses et du trait Macroable, elle traite des limites à respecter lorsque vous maintenez une macro comme API publique d’un package distribué.
L’enregistrement se fait sur la Factory, pas sur la réponse
Le nomResponse désigne à la fois une facade qui demande la construction d’une réponse et la classe de la réponse HTTP générée. Ce que Response::macro() étend dans la documentation officielle, c’est ResponseFactory, la classe vers laquelle la facade est résolue.
response() sans argument renvoie la Factory. En revanche, response('corps') et response(null) renvoient une réponse créée par make(), ils n’appellent donc pas la même cible.
Publier une API de construction pour le JSON d’erreur
L’enregistrer dans un service provider
Enregistrez une macro préfixée par le nom du package dans la méthodeboot() de src/HttpServiceProvider.php. Pour savoir comment le provider est chargé, consultez Découverte de packages.
Le contrat public de cet exemple est un JSON comportant error.code et error.message, accompagné d’un statut HTTP compris entre 400 et 599. Cette restriction de statut n’est pas une limite du mécanisme de macros de Laravel. C’est le package qui la définit pour une API dédiée aux erreurs.
runningInConsole() inversée. Les tests fonctionnels HTTP s’exécutent eux aussi depuis la CLI.
Un provider dont le seul rôle est cet enregistrement n’a pas non plus besoin d’être un provider différé qui attend la résolution d’un service. Pour le fonctionnement de l’enregistrement différé, consultez Service providers différés.
L’appeler depuis un contrôleur
Une fois qu’un conflit a été détecté par votre logique métier, transmettez le code d’erreur choisi et un message destiné à l’utilisateur.409 et le JSON a la forme suivante. Comme avec un json() classique, le Content-Type est application/json.
La macro ne construit que les réponses là où vous l’appelez. Elle ne convertit pas automatiquement les exceptions de validation ou les erreurs d’authentification dans ce format. Elle n’ajoute pas non plus de bascule entre HTML et JSON selon l’en-tête
Accept.Le $this de la Closure et le chemin d’appel
La Factory utilise Macroable. Lorsque vous appelez une méthode non définie sur une instance, __call() recherche la macro enregistrée et relie à nouveau une Closure ordinaire à l’instance sur laquelle elle a été appelée.
Même si le code d’enregistrement se trouve dans une méthode du service provider, $this à l’exécution est la Factory et non le service provider. C’est pourquoi $this->json() dans l’exemple appelle la méthode ordinaire de la Factory.
- Définissez les macros qui utilisent
$thisavec unefunction (...) { ... }ordinaire. Unestatic functionn’a pas de$thisd’instance. - Si vous enregistrez un objet invocable, il n’est pas relié à nouveau comme une Closure. Faites recevoir à cet objet les dépendances dont il a besoin, par exemple via son propre constructeur.
- Les arguments de la macro sont ceux que passe l’appelant. Même si vous déclarez le type
Requestou un type similaire, Macroable ne l’injecte pas automatiquement depuis le conteneur.
Response::acmeFailure() semble statique, mais il est transmis en interne à l’instance de la Factory. En revanche, Illuminate\Routing\ResponseFactory::acmeFailure() sur la classe concrète est un véritable appel statique, et __callStatic() lie la Closure sans instance. Une macro comme celle-ci, qui utilise $this->json(), ne prend pas en charge les appels statiques sur la classe concrète.
Traiter les collisions de noms comme un problème de compatibilité
macro() se contente d’affecter une valeur dans un tableau statique indexé par nom. Enregistrer un nom existant ne produit aucun avertissement.
Préférez un nom qui identifie votre package, comme
acmeFailure, à un nom générique comme failure. Lors des mises à jour de Laravel, vérifiez aussi les collisions avec les méthodes réelles à l’aide de method_exists(ResponseFactory::class, 'acmeFailure').
Si vous vérifiez hasMacro() et ignorez l’enregistrement sans condition, vous risquez d’utiliser l’implémentation d’un autre package comme s’il s’agissait de votre propre API. Ce n’est que si vous faites de « l’application peut la remplacer en l’enregistrant au préalable » une partie de la spécification que vous devez publier cette priorité avec les arguments et la valeur de retour requis. L’exemple ci-dessus suit le principe d’enregistrer la définition propre au package à chaque démarrage du provider.
Séparer l’état d’enregistrement de la durée de vie des réponses
Le registre des macros est une propriété statique de la Factory. Recréer la Factory ou effacer l’instance résolue de la facade ne vide pas ce registre. L’état d’enregistrement peut aussi persister dans les tests qui recréent l’application au sein du même processus PHP. En revanche, la macro de l’exemple fait créer une nouvelleJsonResponse par json() à chaque appel. Ce qui est partagé, c’est la logique de construction, et non le corps ou les en-têtes de la réponse eux-mêmes.
Voici les limites à respecter dans les workers de longue durée.
- Ne capturez pas la Request courante, l’utilisateur authentifié ou le tenant avec
use (...)au moment de l’enregistrement. Passez ces valeurs en arguments au moment de l’appel. - Ne réutilisez pas une réponse créée au moment de l’enregistrement. Renvoyez une nouvelle réponse à chaque fois.
- Si vous capturez une configuration sous forme de valeur au moment de l’enregistrement, indiquez dans la spécification que cette valeur est conservée dans le processus.
- Après une mise à jour du package, redémarrez les processus de longue durée pour que le nouveau code d’enregistrement soit pris en compte.
Tester le contrat HTTP avec de vraies valeurs de retour
Dans une application de test Laravel 13 capable de charger le package via Composer, ajoutez le fichiertests/Feature/ResponseMacroTest.php suivant. Pour Tests\TestCase et les bases des tests HTTP, consultez Tests HTTP. Pour préparer un environnement de package autonome, consultez Tester les packages.
Response::shouldReceive('acmeFailure'), ni l’enregistrement de la macro, ni la liaison de la Closure, ni la génération du JSON ne sont exécutés. Séparez la vérification du nombre d’appels et contrôlez la vraie réponse HTTP comme ci-dessus.
En outre, considérez le corps, le statut et les en-têtes reçus par les utilisateurs comme relevant de la compatibilité.
- Gardez
error.codestable pour les traitements automatiques, et distinguez-le des modifications du message affiché. - Les noms d’arguments sont utilisés comme arguments nommés. Vérifiez donc l’impact de leur modification au même titre que les valeurs par défaut ou l’ordre.
- Renvoyez les réponses sans corps, comme
204, avec une autre API commenoContent(), et non sous forme de JSON d’erreur. - Si vous autorisez des extensions qui modifient le
Content-Typevia des en-têtes personnalisés, gardez-le cohérent avec le format du corps. - Si votre objectif est la sérialisation des données ou la pagination, envisagez aussi les ressources d’API Eloquent plutôt que de les réimplémenter avec des macros seules.
Liste de contrôle pour la maintenance à long terme
- Sur les combinaisons Laravel 13 prises en charge, le provider démarre et la macro peut être appelée depuis le helper comme depuis la facade.
- Le nom de la macro n’entre en collision ni avec d’autres enregistrements ni avec des méthodes réelles de la Factory.
- La compatibilité des clés JSON, du statut HTTP, des en-têtes, des valeurs par défaut et des arguments nommés est testée.
- Les appels successifs ne partagent pas de réponses, et aucune information de Request ou d’utilisateur n’est conservée au moment de l’enregistrement.
- Les tests qui réinitialisent les enregistrements sont isolés des autres packages.
- Le redémarrage des processus de longue durée fait partie de la procédure de mise à jour.
Sources primaires consultées
- Documentation officielle de Laravel : Response macros et JSON responses (13.x)
- Documentation officielle de Laravel : Package development (13.x)
- La facade Response et sa cible de résolution (v13.40.0)
- Transmission de la Facade vers l’instance (v13.40.0)
- Le helper response() (v13.40.0)
- Logique de construction de ResponseFactory (v13.40.0)
- Enregistrement, liaison et réinitialisation dans Macroable (v13.40.0)
- Tests officiels de Macroable (v13.40.0)