Skip to main content
Lorsqu’un package ajoute des fonctionnalités à un service Laravel, on souhaite souvent ne pas créer l’instance tant qu’elle n’est pas utilisée, tout en appliquant la configuration si elle a déjà été créée. La méthode protégée callAfterResolving() du service provider sert précisément à combiner ces deux besoins. Cette page examine l’implémentation de Laravel Framework v13.35.0 et approfondit l’extension depuis boot() présentée dans Développement de packages Laravel. Il ne s’agit pas d’un hook qui attend la fin du démarrage de toute l’application, mais d’un hook lié à la résolution d’un service donné.

Attendre si non résolu, exécuter immédiatement si déjà résolu

ServiceProvider::callAfterResolving() effectue le traitement en deux étapes :
  1. Elle enregistre le callback auprès de afterResolving() du conteneur.
  2. Si resolved() renvoie true, elle récupère le service avec make() et appelle aussi le callback immédiatement.
Si le service n’est pas encore résolu, la méthode elle-même n’appelle pas make() sur la cible. Lors d’une résolution normale, le conteneur construit l’objet, applique les extenders, appelle les callbacks resolving, puis appelle les callbacks afterResolving. Lors de la récupération classique d’un singleton, le conteneur renvoie au plus tôt l’instance stockée : afterResolving n’est donc pas déclenché à chaque récupération. Point important : se contenter d’enregistrer afterResolving() laisse sans configuration un singleton créé avant l’enregistrement.

Ajouter une règle à la Factory de validation

Pour illustrer la fourniture d’une règle de chaîne propre au package, on enregistre une règle après la résolution de validator. Le ValidationServiceProvider officiel enregistre cette clé en tant que singleton, et le provider lui-même prend en charge le chargement différé.
Dans l’application qui utilise le package, on la combine avec les règles habituelles.
Dans cet exemple, extend() est l’API d’enregistrement de règles de Illuminate\Validation\Factory. Il s’agit d’une méthode distincte du extend() du conteneur évoqué plus loin. Les règles personnalisées classiques peuvent ne pas être exécutées sur des valeurs vides, c’est pourquoi le caractère obligatoire est indiqué via required. Pour la conception détaillée de la règle elle-même, consultez Règles de validation personnalisées. Factory::extend() écrase l’élément du tableau portant le même nom de règle : réenregistrer le même traitement, comme dans cet exemple, n’ajoute donc pas d’entrée supplémentaire. Toutefois, pour ne pas écraser les règles d’un autre package, préfixez les noms de règles publiés avec un identifiant propre à votre package.
Le callback est exécuté lors de la résolution de la Factory. La closure de validation de la règle est exécutée plus tard, lorsque le Validator valide la valeur concernée. Les données saisies ne sont pas validées au moment de l’enregistrement du hook.

Aligner la clé cible et la vérification de résolution

Lors des événements de résolution classiques du conteneur, sont sélectionnés non seulement les callbacks correspondant à la clé enregistrée, mais aussi ceux correspondant au type de l’objet obtenu. En revanche, resolved($name), utilisée immédiatement par callAfterResolving(), vérifie si la clé normalisée (alias résolus) possède un indicateur de résolution ou une instance stockée. Elle ne parcourt pas l’ensemble des objets déjà créés. Par conséquent, le fait qu’une interface ou une classe soit résolue ne garantit pas que resolved() renvoie aussi true pour une autre clé. Vérifiez la liaison et les alias réels du service cible, et testez le cas déjà résolu avec la même clé. Dans l’exemple ci-dessus, on cible validator, enregistré par le provider officiel. Par ailleurs, resolved() inclut aussi le fait d’« avoir été résolu par le passé ». Cela ne signifie pas que l’instance courante est forcément toujours présente.

Ce n’est pas une API garantissant une « exécution unique »

callAfterResolving() laisse le callback enregistré. Même après une exécution immédiate, le callback enregistré sera exécuté chaque fois que l’objet sera de nouveau résolu à l’avenir. De plus, rappeler la méthode ajoute un nouveau callback. Soyez particulièrement vigilant lorsqu’une liaison classique non partagée a déjà été résolue. Dans l’implémentation examinée, le déroulement est le suivant :
  1. Le nouveau callback est enregistré.
  2. Comme resolved() renvoie true, make() est appelé.
  3. make() crée un nouvel objet, et le callback est exécuté lors de son événement de résolution.
  4. Le callback immédiat est aussi exécuté sur ce même objet renvoyé par make().
Dans ce cas, le callback enregistré est appliqué deux fois au même objet. Ne réutilisez pas tel quel, sur une liaison classique, un design pensé uniquement pour la récupération d’un singleton.
N’envoyez pas d’e-mails, n’appelez pas d’API externes, ne déclenchez pas de facturation et n’ajoutez pas d’écouteurs sans condition dans le callback. Limitez son usage à l’application idempotente de configuration au service, et concevez-le de sorte que les réenregistrements ou nouvelles résolutions ne dupliquent pas les effets de bord.

Utiliser une autre API pour remplacer l’objet

La valeur de retour d’un callback afterResolving n’est pas utilisée pour remplacer l’objet renvoyé par le conteneur. Si vous souhaitez renvoyer un décorateur pour substituer le service lui-même, envisagez le extend() du conteneur. La closure de cette API a pour contrat de renvoyer le service modifié. De même, callAfterResolving() n’est pas un mécanisme permettant de mettre à jour toutes les dépendances déjà stockées dans d’autres objets. Si l’objectif est de mettre à jour les dépendances lors d’une nouvelle liaison, consultez rebinding() dans la documentation officielle ainsi que la conception de la classe concernée. Pour les exigences d’un provider qui charge réellement ses services de façon différée, consultez DeferrableProvider. Utiliser un hook et rendre différé le provider qui enregistre ce hook sont deux choses distinctes.

Combinaisons à vérifier lors des mises à jour

Avant de publier une version du package, vérifiez non seulement l’ordre de démarrage habituel, mais aussi le cas où un autre provider a utilisé le service en premier. Enregistrer via un indicateur statique qu’une configuration a été appliquée une seule fois pour toute l’application risque de laisser sans configuration les services recréés dans un nouveau conteneur ou lors des tests. Assurez l’idempotence de la configuration autant que possible au niveau de l’objet cible ou de la clé enregistrée.

Pages connexes

Surcharger et mettre à jour les vues d'un package

Découvrez un exemple où loadViewsFrom() utilise un hook de résolution pour enregistrer un namespace de vues.

Surcharge et mise à jour des traductions d'un package

Découvrez l’enregistrement des namespaces du Translator et le traitement des traductions déjà chargées.

Sources primaires consultées

Dernière modification le 10 octobre 2026