¿Qué es el contenedor de servicios?
El contenedor de servicios de Laravel gestiona las dependencias de las clases y realiza la inyección de dependencias. Con “inyección de dependencias” nos referimos a “inyectar” en una clase las dependencias que necesita, ya sea a través del constructor o, según el caso, mediante métodos setter. Fíjate en este ejemplo:PodcastController necesita obtener un podcast desde una fuente como Apple Music. Por eso inyectamos un servicio capaz de obtener podcasts. Al inyectarlo, resulta muy fácil sustituirlo por un mock durante las pruebas.
Entender bien el contenedor de servicios es imprescindible para construir aplicaciones Laravel grandes. También ayuda si vas a contribuir al propio núcleo de Laravel.
Resolución sin configuración
Si una clase solo depende de otras clases concretas (no de interfaces), no hace falta indicar al contenedor cómo resolverla. Por ejemplo, imagina este código enroutes/web.php:
Este ejemplo define la clase en el archivo de rutas por motivos didácticos. En una aplicación real, define las clases de servicio en el directorio
app/Services.Service y la inyecta en el manejador. Puedes disfrutar de la inyección de dependencias sin ningún archivo de configuración.
Muchas de las clases que escribes en una aplicación Laravel (controladores, listeners de eventos, middleware, etc.) reciben sus dependencias por inyección automática a través del contenedor.
Bindings
Binding básico
La mayoría de bindings se registran dentro de un service provider. Dentro de él accedes al contenedor con$this->app.
bind
Conbind registras un binding pasando el nombre de la clase/interfaz y una closure.
App.
Las clases que no dependen de interfaces no necesitan ser registradas en el contenedor. El contenedor puede resolverlas automáticamente mediante reflexión.
singleton
singleton registra un binding que se resuelve una única vez. Una vez resuelto, las llamadas posteriores al contenedor devuelven la misma instancia.
instance
También puedes registrar una instancia ya existente coninstance. Las llamadas posteriores devolverán siempre esa instancia.
Binding de interfaces a implementaciones
Una de las capacidades más potentes del contenedor es enlazar una interfaz con una implementación concreta. Imagina que tienes la interfazEventPusher y la implementación RedisEventPusher.
RedisEventPusher en las clases que necesitan una implementación de EventPusher. Solo tienes que tipar la interfaz en el constructor.
Resolución automática (DI por type-hint)
Cuando el contenedor resuelve clases como controladores, listeners o middlewares, mira los type-hints del constructor e inyecta las dependencias automáticamente.UserRepository no depende de interfaces, no hace falta registrarlo en el contenedor. Basta con acceder a la ruta y el contenedor resolverá e inyectará las dependencias en el controlador.
Resolver desde el contenedor
El método make
Conmake puedes resolver una instancia desde el contenedor.
makeWith para pasar argumentos adicionales.
Inyección automática
En la práctica, casi nunca llamarás amake directamente. Basta con tipar las dependencias en el constructor de las clases que el contenedor resuelve (controladores, listeners, middleware…) y el contenedor las inyectará automáticamente.
Relación entre facades y contenedor
Las facades de Laravel ofrecen una interfaz estática hacia objetos del contenedor. Por ejemplo,Cache::get() obtiene internamente el servicio Cache del contenedor y lo invoca.
Caso práctico de inyección por constructor
Veamos un patrón habitual en aplicaciones reales.
Con este patrón, si cambias de proveedor de pago (por ejemplo, sustituyes
Stripe por otro), basta con cambiar el binding en un único lugar.
Próximos pasos
Service providers
Aprende a registrar bindings usando service providers.