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.

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 2 août 2026