Skip to main content

Qué es el service container

El service container de Laravel es el mecanismo para gestionar las dependencias de las clases y realizar la inyección de dependencias. Inyectar dependencias significa «inyectar» en la clase las dependencias que necesita a través del constructor o, en algunos casos, mediante métodos setter. Fíjate en el siguiente ejemplo.
En este ejemplo, PodcastController necesita obtener podcasts desde una fuente de datos como Apple Music. Por eso inyectamos un servicio capaz de obtener podcasts. Al inyectarlo, en las pruebas podemos sustituir con facilidad AppleMusic por un mock (una implementación falsa).
Entender el service container en profundidad es imprescindible para construir aplicaciones Laravel grandes. También ayuda a contribuir al propio núcleo de Laravel.

Resolución sin configuración

Si tu clase solo depende de otras clases concretas (no de interfaces), no necesitas indicarle al container cómo resolverlas. Por ejemplo, imagina que escribes el siguiente código en routes/web.php.
Este ejemplo define la clase dentro del archivo de rutas con fines de demostración. En una aplicación real, define las clases de servicio en el directorio app/Services.
Al acceder a esta ruta, Laravel resuelve automáticamente la clase Service y la inyecta en el handler. No hace falta preparar ningún archivo de configuración para beneficiarse de la inyección de dependencias. En la mayoría de las clases que escribes en una aplicación Laravel —controladores, listeners de eventos, middlewares, etc.—, las dependencias se inyectan automáticamente a través del container.

Bindings

Bindings básicos

La mayoría de los bindings se registran en los service providers. Dentro de un service provider puedes acceder al container mediante la propiedad $this->app.

bind

Con el método bind registras un binding pasando el nombre de una clase o interfaz y un closure.
El closure puede recibir el propio container como argumento. Puedes usarlo para resolver sub-dependencias. Si necesitas manipular el container fuera de un service provider, usa la fachada App.
Las clases que no dependen de una interfaz no necesitan registrarse en el container. El container puede resolverlas automáticamente usando reflection.

singleton

El método singleton vincula la clase o interfaz de modo que se resuelva una sola vez. Una vez resuelto el singleton, todas las llamadas posteriores al container devuelven la misma instancia.
Con singletonIf puedes registrar el binding singleton solo si aún no existe uno para el tipo indicado.

Atributo Singleton

Puedes indicarle al container que la clase o interfaz debe resolverse una sola vez añadiendo el atributo #[Singleton].

Bindings de singleton con scope

El método scoped vincula la clase o interfaz para que se resuelva una sola vez dentro del ciclo de vida de la petición/job de Laravel. Es similar a singleton, pero las instancias registradas con scoped se descartan cada vez que la aplicación Laravel inicia un nuevo «ciclo de vida», como cuando un worker de Laravel Octane atiende una nueva petición o cuando un worker de cola procesa un nuevo job.
Con scopedIf puedes registrar el binding con scope solo si aún no existe uno para el tipo indicado.

Atributo Scoped

También puedes indicarle al container que resuelva una sola vez por ciclo de petición/job añadiendo el atributo #[Scoped].

instance

También puedes vincular al container una instancia de objeto ya existente con el método instance. En las llamadas posteriores al container se devolverá siempre esa instancia.

Vincular una interfaz a una implementación

Una de las funcionalidades más potentes del service container es la posibilidad de vincular una interfaz a una implementación concreta. Por ejemplo, imagina que tienes la interfaz EventPusher y la implementación RedisEventPusher.
Con esto, el container inyectará RedisEventPusher en cualquier clase que necesite una implementación de EventPusher. Basta con hacer un type hint de la interfaz EventPusher en el constructor.
Al depender de interfaces, cambiar la implementación no requiere modificar el código. Esto facilita las pruebas y los cambios futuros.

Atributo Bind

Laravel también ofrece el útil atributo Bind. Añadiéndolo a una interfaz, indicas a Laravel qué implementación inyectar automáticamente cuando se solicita esa interfaz. Con Bind no necesitas registrar nada adicional en un service provider. Además, colocando varios atributos Bind sobre la interfaz, puedes configurar la inyección de implementaciones distintas por entorno.
Para bindings que dependan de condiciones arbitrarias puedes usar el atributo BindWhen. Al closure se le puede pasar el container y debe devolver true cuando quieras aplicar el binding. Los atributos Bind y BindWhen se evalúan en el orden en que se declaran.
Para usar el atributo BindWhen se requiere PHP 8.5 o superior.
Además, combinándolos con los atributos Singleton o Scoped, puedes especificar que ese binding se resuelva una sola vez o una sola vez por petición/job.

Resolución automática (DI por type hint)

El service container inyecta automáticamente las dependencias mirando los type hints del constructor al resolver clases como controladores, listeners de eventos o middlewares.
Si UserRepository no depende de una interfaz, no hay que registrarlo en el container. Basta con acceder a la ruta correspondiente y el container resolverá automáticamente la dependencia y la inyectará en el controlador.

Resolver desde el container

Método make

Con el método make puedes resolver una instancia de clase desde el container.
Si las dependencias de la clase no pueden resolverse en el container, con makeWith puedes pasar argumentos adicionales.

Inyección automática

En la práctica rara vez llamarás directamente a make. Basta con añadir type hints al constructor de las clases que resuelve el container (controladores, listeners de eventos, middlewares, etc.) y las inyectará automáticamente.

Relación entre fachadas y container

Las fachadas de Laravel ofrecen una interfaz estática a los objetos dentro del container. Por ejemplo, Cache::get() internamente obtiene el servicio Cache desde el container y lo invoca.
Las fachadas son envoltorios cómodos del container. En las pruebas puedes sustituir la fachada por un mock.

Ejemplo práctico de inyección por constructor

Veamos un patrón típico en una aplicación real.
1

Define la interfaz

2

Crea la clase de implementación

3

Vincúlala en el service provider

4

Recibe la inyección en el controlador

Con este patrón, cambiar el servicio de pago de Stripe a otro proveedor requiere modificar un solo binding.

Próximos pasos

Service providers

Aprende a registrar bindings mediante service providers.
Última modificación el 2 de agosto de 2026