> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks de résolution des services d'un package

> En analysant callAfterResolving() de Laravel 13, découvrez comment étendre des services non résolus ou déjà résolus, ainsi que les points d'attention liés aux singletons, aux liaisons classiques et aux réenregistrements.

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](/fr/advanced/package-development). 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`.

```mermaid theme={null}
flowchart TD
    A["callAfterResolving() dans boot()"] --> B["Enregistrement dans afterResolving"]
    B --> C{"La cible est resolved() ?"}
    C -->|Non| D["La cible n'est pas créée maintenant"]
    D --> E["La cible est résolue plus tard"]
    E --> F["Exécution du callback enregistré"]
    C -->|Oui| G["Récupération de la cible via make()"]
    G --> H["Exécution immédiate du callback"]
```

| Méthode | Non résolu à l'enregistrement | Singleton déjà résolu à l'enregistrement |
| - | - | - |
| Configuration directe via `$this->app->make()` | Crée l'instance immédiatement | Configure l'instance existante |
| `$this->app->afterResolving()` | Attend les résolutions futures | L'enregistrement seul ne configure pas l'instance existante |
| `$this->callAfterResolving()` | Attend les résolutions futures | Configure aussi immédiatement l'instance existante |

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é.

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->callAfterResolving('validator', function (Factory $factory): void {
            $factory->extend(
                'courier_tracking_code',
                static function ($attribute, $value, $parameters, $validator): bool {
                    return is_string($value)
                        && preg_match('/\ACR-[0-9]{8}\z/', $value) === 1;
                },
                'The :attribute must be a valid courier tracking code.',
            );
        });
    }
}
```

Dans l'application qui utilise le package, on la combine avec les règles habituelles.

```php theme={null}
$request->validate([
    'tracking_code' => ['required', 'string', 'courier_tracking_code'],
]);
```

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](/fr/advanced/custom-validation-rules).

`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.

<Info>
  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.
</Info>

## 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.

<Warning>
  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.
</Warning>

## 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](/fr/advanced/deferred-provider). 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.

| Cas | Ce qu'il faut vérifier |
| - | - |
| Enregistrement du hook alors que la cible n'est pas résolue | L'enregistrement seul ne crée pas la cible, et la configuration est appliquée lors de la première résolution |
| Enregistrement après la résolution préalable d'un singleton | La configuration est appliquée immédiatement à la même instance |
| Nouvelle récupération d'un singleton configuré via un `make()` classique | La même instance est renvoyée et les callbacks de résolution ne sont pas exécutés en double |
| Enregistrement sur une liaison classique déjà résolue par le passé | Rien ne casse même avec la double application (`make()` à l'enregistrement et appel explicite) |
| Enregistrements multiples du hook ou recréation de l'instance | Les mêmes règles, écouteurs, chemins, etc. ne s'accumulent pas involontairement |
| Mise à jour des versions de Laravel prises en charge | Revérifier les clés de liaison, le caractère partagé et le chemin d'exécution des hooks |

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

<Columns cols={2}>
  <Card title="Surcharger et mettre à jour les vues d'un package" icon="eye" href="/fr/advanced/package-views">
    Découvrez un exemple où loadViewsFrom() utilise un hook de résolution pour enregistrer un namespace de vues.
  </Card>

  <Card title="Surcharge et mise à jour des traductions d'un package" icon="language" href="/fr/advanced/package-translations">
    Découvrez l'enregistrement des namespaces du Translator et le traitement des traductions déjà chargées.
  </Card>
</Columns>

## Sources primaires consultées

* [Documentation officielle de Laravel : développement de packages](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Documentation officielle de Laravel : Container events, Extending bindings, Rebinding](https://github.com/laravel/docs/blob/13.x/container.md#container-events)
* [Documentation officielle de Laravel : règles de validation personnalisées](https://github.com/laravel/docs/blob/13.x/validation.md#custom-validation-rules)
* [ServiceProvider : callAfterResolving() et son utilisation pour l'enregistrement des ressources](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [Container : resolved(), resolve(), afterResolving()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Container/Container.php)
* [ValidationServiceProvider : enregistrement de validator en singleton](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/ValidationServiceProvider.php)
* [Validation Factory : enregistrement de règles via extend()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/Factory.php)


## Related topics

- [Développement de packages Laravel](/fr/advanced/package-development.md)
- [Sujets avancés](/fr/advanced/index.md)
- [Hooks de session](/fr/packages/laravel-copilot-sdk/hooks.md)
- [Enregistrement et cache des routes d'un package](/fr/advanced/package-routes.md)
- [Surcharge et mise à jour des traductions d'un package](/fr/advanced/package-translations.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.