Skip to main content

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.
Dans cet exemple, 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.
Lorsque vous accédez à cette route, Laravel résout automatiquement la classe 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éthode bind associe une classe ou une interface à une closure.
La closure reçoit le container lui-même en argument, ce qui permet de résoudre des dépendances imbriquées. Hors d’un provider, la façade 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.
La méthode 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 avec instance. 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’interface EventPusher et l’implémentation RedisEventPusher :
Le container injectera alors RedisEventPusher partout où une implémentation de EventPusher est requise. Il suffit de type-hinter l’interface EventPusher dans le constructeur.
En dépendant d’une interface, vous pouvez changer d’implémentation sans modifier le code appelant. Cela facilite les tests et les évolutions futures.

Attribut Bind

Laravel propose également l’attribut Bind, 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.
Pour des bindings soumis à une condition arbitraire, utilisez 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.
Combinez avec les attributs Singleton ou Scoped pour préciser si le binding doit être résolu une seule fois, ou une fois par cycle de requête/job.

Binding contextuel

Il arrive que deux classes utilisant la même interface aient besoin, chacune, d’une implémentation différente. Par exemple, imaginons que deux contrôleurs dépendent d’implémentations différentes du contrat Illuminate\Contracts\Filesystem\Filesystem. Laravel fournit une interface simple et fluide pour décrire ce comportement.
when() accepte une classe ou un tableau de classes. needs() désigne l’interface visée, et give() fournit la closure utilisée pour la résolution. Vous pouvez ainsi injecter des disques différents dans chaque contrôleur, même si tous type-hintent la même interface Filesystem.

Attributs contextuels

Comme le binding contextuel sert souvent à injecter l’implémentation d’un driver ou une valeur de configuration, Laravel propose divers attributs de binding contextuel qui vous évitent de définir manuellement ces bindings dans un service provider. Par exemple, l’attribut Storage permet d’injecter un disque de stockage spécifique.
En plus de l’attribut Storage, Laravel fournit les attributs Auth, Cache, Config, Context, DB, Give, Log, RequestAttribute, RouteParameter et Tag.
L’attribut RouteParameter résout le paramètre de route dont le nom correspond à celui de la propriété. Si besoin, vous pouvez spécifier explicitement le nom du paramètre de route via #[RouteParameter('photo')]. L’attribut RequestAttribute résout une valeur d’entrée de l’instance Request courante. Si vous voulez référencer une clé différente du nom de la propriété, spécifiez-la explicitement, par exemple #[RequestAttribute('user_organization')].
L’attribut Give s’utilise pour désigner une implémentation particulière au lieu de la résolution automatique habituelle par type-hint, lorsque vous résolvez une interface vers une classe concrète.

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.
Si 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.
Si les dépendances de la classe ne peuvent pas être résolues par le container, makeWith autorise le passage d’arguments supplémentaires.

Injection automatique

En pratique, vous appellerez rarement make 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.
Les façades sont un wrapper pratique sur le container. Pour les tests, vous pouvez les remplacer par un mock.

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

Grâce à ce pattern, remplacer 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.
Dernière modification le 11 septembre 2026