¿Qué es el Service Container de Laravel?

Resumen de cómo funciona la inyección de dependencias en Laravel a través del Service Container.

¿Qué es el Service Container de Laravel?
Photo by Jez Timms / Unsplash

El Service Container es uno de los pilares de Laravel. Lo usamos en todas las aplicaciones sin casi darnos cuenta, pero nos permite usar inyección de dependencias. ¿Te suena este controlador?

public class PostController
{
	public function show(Post $post, Request $request)
    {
    	// ...
    }
}

¿Ves ese parámetro Request $request? ¿Cómo sabe Laravel que ahí le tiene que pasar el request actual? ¿Y cómo sabe que tiene que recoger el $post identificado por la URL? ¡Magia! (No, en realidad es el Service Container).

¿Cómo funciona el Service Container de Laravel?

Algún día lo explicaré en profundidad, pero la idea principal es:

  1. Laravel recoge los Service Provider de nuestra aplicación (muchos presentes en app/Providers
  2. Cada Service Provider ejecuta un código parecido a este:
$this->app->bind(PaymentManager::class, function($app) {
    return new StripePaymentManager();
});

Este código le está diciendo a Laravel: "oye, cuando te esté pidiendo un objeto del tipo PaymentManager, devuélveme este objeto que te pongo aquí (el StripePaymentManager).

¿Cómo se usa el Service Container de Laravel?

Una vez tenemos nuestros bindings definidos en nuestros providers, podemos usarlos en nuestra aplicación. Hay varias formas de usarlo pero los dos más comunes son:

  • En los controladores, tal y como vimos antes. Solamente tenemos que añadir un parámetro del tipo que hemos definido y el Service Container nos devolverá nuestro objeto resuelto:
public class PaymentController
{
	public function pay(PaymentManager $manager)
	{
		// $manager será un objeto de tipo StripePaymentManager
	}
}
  • En el resto de nuestra aplicación también podemos usarlo a través de la función app(). Esta función es una interfaz con el Service Container, por lo que podemos hacer esto desde cualquier parte:
$manager = app(PaymentManager::class);
// $manager será la resolución del Service Container (en este caso StripePaymentManager)

Además, la función app() también nos sirve para instanciar objetos usando el Service Container. Por ejemplo, imagina la siguiente clase:

public class PaymentProcessor
{
	public function __construct(
		private PaymentManager $manager,
		private Request $request
	) {
		// ...
	}

	// ...
}

Luego, en cualquier lugar de nuestro código:

// En lugar de hacer:
$processor = new PaymentProcessor(app(PaymentManager::class), request());

// Podemos hacer
$processor = app(PaymentProcessor::class);

Resumen

Como podemos ver, el Service Container juega un papel muy importante en Laravel y es una herramienta que vale la pena tener en nuestra cartera para muchos casos.