Qu’est-ce que le service container ?
Le service container de Laravel gère les dépendances de vos classes et met en œuvre l’injection de dépendances. L’injection de dépendances consiste à « injecter » dans une classe les dépendances dont elle a besoin via son constructeur ou, plus rarement, une méthode setter. Prenons l’exemple suivant.PodcastController doit récupérer des podcasts depuis une source de données comme Apple Music. Nous lui injectons donc un service capable de récupérer des podcasts. Grâce à cette injection, il devient facile de remplacer AppleMusic par un mock (implémentation factice) au moment des tests.
Bien comprendre le service container est indispensable pour bâtir de grosses applications Laravel. C’est aussi utile pour contribuer au cœur de Laravel.
Résolution sans configuration
Si une classe ne dépend que de classes concrètes (et non d’interfaces), il n’est pas nécessaire d’indiquer au container comment la résoudre. Par exemple :Cet exemple déclare la classe directement dans le fichier de routes à des fins de démonstration. Dans une vraie application, définissez vos services dans
app/Services.Service et l’injecte dans le handler. Vous bénéficiez de l’injection de dépendances sans configuration.
Contrôleurs, event listeners, middlewares… la plupart des classes que vous écrivez dans une application Laravel voient leurs dépendances injectées automatiquement par le container.
Bindings
Bindings de base
La plupart des bindings sont enregistrés dans un service provider. Depuis un provider, la propriété$this->app donne accès au container.
bind
La méthodebind associe une classe ou une interface à une closure.
App permet d’interagir avec le container.
Les classes qui ne dépendent pas d’une interface n’ont pas besoin d’être enregistrées : le container les résout automatiquement par réflexion.
singleton
singleton enregistre un binding qui ne sera résolu qu’une seule fois. Toute résolution ultérieure retourne la même instance.
singletonIf enregistre un binding singleton uniquement si aucun binding n’existe déjà pour ce type.
Attribut Singleton
Vous pouvez également marquer une classe ou une interface avec l’attribut#[Singleton] pour indiquer au container qu’elle ne doit être résolue qu’une seule fois.
Bindings scoped
scoped enregistre un binding résolu une seule fois par cycle de vie de requête ou de job Laravel. Semblable à singleton, à ceci près que l’instance est détruite dès qu’un nouveau cycle démarre : par exemple, quand un worker Laravel Octane traite une nouvelle requête ou qu’un worker de queue traite un nouveau job.
scopedIf enregistre un binding scoped uniquement si aucun binding n’existe déjà pour ce type.
Attribut Scoped
L’attribut#[Scoped] sur une classe ou une interface indique au container qu’elle ne doit être résolue qu’une seule fois par cycle de requête ou de job.
instance
Vous pouvez enregistrer une instance déjà existante avecinstance. Les appels suivants au container renvoient toujours cette instance.
Lier une interface à une implémentation
Une des fonctionnalités les plus puissantes du service container est la possibilité de lier une interface à une implémentation. Par exemple, avec l’interfaceEventPusher et l’implémentation RedisEventPusher :
RedisEventPusher partout où une implémentation de EventPusher est requise. Il suffit de type-hinter l’interface EventPusher dans le constructeur.
Attribut Bind
Laravel propose également l’attributBind, très pratique. En le plaçant sur une interface, vous indiquez quelle implémentation injecter automatiquement quand cette interface est demandée. Aucun enregistrement supplémentaire dans un service provider n’est nécessaire.
Vous pouvez même placer plusieurs Bind sur une même interface pour choisir l’implémentation selon l’environnement.
BindWhen. La closure reçoit le container et doit retourner true pour que le binding s’applique. Les attributs Bind et BindWhen sont évalués dans leur ordre de déclaration.
L’attribut
BindWhen nécessite PHP 8.5 ou plus.Résolution automatique (DI par type-hint)
Lorsqu’il résout des classes telles que des contrôleurs, des event listeners ou des middlewares, le service container lit les type-hints du constructeur et injecte automatiquement les dépendances.UserRepository ne dépend pas d’une interface, aucun enregistrement dans le container n’est nécessaire : il suffit d’accéder à la route pour que le container résolve et injecte la dépendance.
Résolution depuis le container
La méthode make
make permet de résoudre une instance depuis le container.
makeWith autorise le passage d’arguments supplémentaires.
Injection automatique
En pratique, vous appellerez rarementmake directement. Il suffit d’ajouter un type-hint dans le constructeur d’une classe résolue par le container (contrôleur, event listener, middleware…) pour bénéficier de l’injection automatique.
Façades et container
Les façades de Laravel offrent une interface statique vers des objets du container. Par exemple,Cache::get() récupère en interne le service Cache depuis le container.
Exemple concret d’injection dans le constructeur
Voyons un pattern typique d’application réelle.1
Définir une interface
2
Créer une implémentation
3
Déclarer le binding dans un service provider
4
Recevoir l'injection dans le contrôleur
Stripe par un autre fournisseur de paiement ne demande de modifier qu’une seule ligne de binding.
Étapes suivantes
Service providers
Découvrez comment enregistrer des bindings via un service provider.