Estamos actualizando el sitio. Durante unos días es posible que veas fallos de diseño o de traducción. La documentación sigue disponible: si una página se ve mal, recárgala más tarde.

Inicio Laravel 10.x Contenedor de Servicios

Contenedor de Servicios

10.x 7 de mar. de 2026

#Introducción

El contenedor de servicios de Laravel es una herramienta poderosa para gestionar las dependencias de las clases y realizar inyección de dependencias. La inyección de dependencias es una expresión sofisticada que básicamente significa esto: las dependencias de una clase son "inyectadas" en la clase a través del constructor o, en algunos casos, mediante métodos "setter".

Veamos un ejemplo sencillo:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Crear una nueva instancia del controlador.
     */
    public function __construct(
        protected UserRepository $users,
    ) {}

    /**
     * Mostrar el perfil del usuario dado.
     */
    public function show(string $id): View
    {
        $user = $this->users->find($id);

        return view('user.profile', ['user' => $user]);
    }
}

En este ejemplo, el UserController necesita obtener usuarios de una fuente de datos. Por lo tanto, inyectamos un servicio que puede recuperar usuarios. En este contexto, nuestro UserRepository probablemente utiliza Eloquent para obtener la información del usuario desde la base de datos. Sin embargo, dado que el repositorio es inyectado, podemos cambiarlo fácilmente por otra implementación. También podemos "mockear" fácilmente, o crear una implementación ficticia del UserRepository al probar nuestra aplicación.

Una comprensión profunda del contenedor de servicios de Laravel es esencial para construir una aplicación poderosa y grande, así como para contribuir al núcleo de Laravel.

#Resolución sin Configuración

Si una clase no tiene dependencias o solo depende de otras clases concretas (no interfaces), el contenedor no necesita instrucciones para resolver esa clase. Por ejemplo, puede colocar el siguiente código en su archivo routes/web.php:

<?php

class Service
{
    // ...
}

Route::get('/', function (Service $service) {
    die($service::class);
});

En este ejemplo, al acceder a la ruta / de su aplicación, se resolverá automáticamente la clase Service y se inyectará en el manejador de la ruta. Esto es revolucionario. Significa que puede desarrollar su aplicación y aprovechar la inyección de dependencias sin preocuparse por archivos de configuración voluminosos.

Afortunadamente, muchas de las clases que escribirá al construir una aplicación Laravel reciben automáticamente sus dependencias a través del contenedor, incluyendo controladores, listeners de eventos, middleware y más. Además, puede type-hint dependencias en el método handle de los trabajos en cola. Una vez que prueba el poder de la inyección automática y sin configuración, parece imposible desarrollar sin ella.

#Cuándo Utilizar el Contenedor

Gracias a la resolución sin configuración, a menudo type-hintará dependencias en rutas, controladores, listeners de eventos y otros lugares sin interactuar manualmente con el contenedor. Por ejemplo, puede type-hint el objeto Illuminate\Http\Request en la definición de su ruta para acceder fácilmente a la solicitud actual. Aunque nunca interactuamos directamente con el contenedor para escribir este código, él gestiona la inyección de estas dependencias en segundo plano:

use Illuminate\Http\Request;

Route::get('/', function (Request $request) {
    // ...
});

En muchos casos, gracias a la inyección automática de dependencias y a los facades, puede construir aplicaciones Laravel sin nunca vincular o resolver manualmente nada del contenedor. Entonces, ¿cuándo interactuaría manualmente con el contenedor? Examinemos dos situaciones.

Primero, si escribe una clase que implementa una interfaz y desea type-hint esa interfaz en una ruta o constructor de clase, debe indicar al contenedor cómo resolver esa interfaz. Segundo, si está escribiendo un paquete Laravel que planea compartir con otros desarrolladores Laravel, puede necesitar vincular los servicios de su paquete en el contenedor.

#Binding

#Conceptos Básicos de Binding

#Bindings Simples

Casi todas sus vinculaciones en el contenedor de servicios se registrarán dentro de los service providers, por lo que la mayoría de estos ejemplos mostrarán el uso del contenedor en ese contexto.

Dentro de un service provider, siempre tiene acceso al contenedor a través de la propiedad $this->app. Podemos registrar un binding usando el método bind, pasando el nombre de la clase o interfaz que deseamos registrar junto con un closure que devuelve una instancia de la clase:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

Note que recibimos el contenedor mismo como argumento del resolvedor. Luego podemos usar el contenedor para resolver sub-dependencias del objeto que estamos construyendo.

Como se mencionó, normalmente interactuará con el contenedor dentro de los service providers; sin embargo, si desea interactuar con el contenedor fuera de un service provider, puede hacerlo a través del facade App:

use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;

App::bind(Transistor::class, function (Application $app) {
    // ...
});

Puede usar el método bindIf para registrar un binding en el contenedor solo si no se ha registrado previamente un binding para el tipo dado:

$this->app->bindIf(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
Примечание

No es necesario vincular clases en el contenedor si no dependen de ninguna interfaz. El contenedor no necesita instrucciones para construir estos objetos, ya que puede resolverlos automáticamente usando reflexión.

#Binding de un Singleton

El método singleton vincula una clase o interfaz en el contenedor que solo debe resolverse una vez. Una vez que un binding singleton es resuelto, la misma instancia del objeto será devuelta en llamadas posteriores al contenedor:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

Puede usar el método singletonIf para registrar un binding singleton solo si no se ha registrado previamente un binding para el tipo dado:

$this->app->singletonIf(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

#Binding de Singletons con Ámbito

El método scoped vincula una clase o interfaz en el contenedor que solo debe resolverse una vez dentro del ciclo de vida de una solicitud o trabajo de Laravel. Aunque este método es similar a singleton, las instancias registradas con scoped se reinician cada vez que la aplicación Laravel inicia un nuevo "ciclo de vida", como cuando un trabajador de Laravel Octane procesa una nueva solicitud o cuando un trabajador de la cola de Laravel procesa un nuevo trabajo:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->scoped(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

#Binding de Instancias

También puede vincular una instancia de objeto existente en el contenedor usando el método instance. La instancia dada siempre será devuelta en llamadas posteriores al contenedor:

use App\Services\Transistor;
use App\Services\PodcastParser;

$service = new Transistor(new PodcastParser);

$this->app->instance(Transistor::class, $service);

#Vinculación de Interfaces a Implementaciones

Una característica muy poderosa del contenedor de servicios es su capacidad para vincular una interfaz a una implementación dada. Por ejemplo, supongamos que tenemos una interfaz EventPusher y una implementación RedisEventPusher. Una vez que hemos codificado nuestra implementación RedisEventPusher de esta interfaz, podemos registrarla en el contenedor de servicios así:

use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;

$this->app->bind(EventPusher::class, RedisEventPusher::class);

Esta declaración le indica al contenedor que debe inyectar RedisEventPusher cuando una clase necesite una implementación de EventPusher. Ahora podemos type-hint la interfaz EventPusher en el constructor de una clase que es resuelta por el contenedor. Recuerde, controladores, listeners de eventos, middleware y varios otros tipos de clases dentro de aplicaciones Laravel siempre son resueltos usando el contenedor:

use App\Contracts\EventPusher;

/**
 * Crear una nueva instancia de clase.
 */
public function __construct(
    protected EventPusher $pusher
) {}

#Binding Contextual

A veces puede tener dos clases que utilizan la misma interfaz, pero desea inyectar diferentes implementaciones en cada clase. Por ejemplo, dos controladores pueden depender de diferentes implementaciones del contrato Illuminate\Contracts\Filesystem\Filesystem. Laravel proporciona una interfaz simple y fluida para definir este comportamiento:

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when([VideoController::class, UploadController::class])
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

#Binding de Primitivos

A veces puede tener una clase que recibe algunas clases inyectadas, pero también necesita un valor primitivo inyectado como un entero. Puede usar fácilmente binding contextual para inyectar cualquier valor que su clase necesite:

use App\Http\Controllers\UserController;

$this->app->when(UserController::class)
          ->needs('$variableName')
          ->give($value);

A veces una clase puede depender de un array de instancias etiquetadas. Usando el método giveTagged, puede inyectar fácilmente todas las vinculaciones del contenedor con esa etiqueta:

$this->app->when(ReportAggregator::class)
    ->needs('$reports')
    ->giveTagged('reports');

Si necesita inyectar un valor desde uno de los archivos de configuración de su aplicación, puede usar el método giveConfig:

$this->app->when(ReportAggregator::class)
    ->needs('$timezone')
    ->giveConfig('app.timezone');

#Binding de Variádicos Tipados

Ocasionalmente, puede tener una clase que recibe un array de objetos tipados usando un argumento variádico en el constructor:

<?php

use App\Models\Filter;
use App\Services\Logger;

class Firewall
{
    /**
     * Las instancias de filtro.
     *
     * @var array
     */
    protected $filters;

    /**
     * Crear una nueva instancia de clase.
     */
    public function __construct(
        protected Logger $logger,
        Filter ...$filters,
    ) {
        $this->filters = $filters;
    }
}

Usando binding contextual, puede resolver esta dependencia proporcionando al método give un closure que devuelve un array de instancias Filter resueltas:

$this->app->when(Firewall::class)
          ->needs(Filter::class)
          ->give(function (Application $app) {
                return [
                    $app->make(NullFilter::class),
                    $app->make(ProfanityFilter::class),
                    $app->make(TooLongFilter::class),
                ];
          });

Para mayor comodidad, también puede simplemente proporcionar un array de nombres de clases para que el contenedor las resuelva cuando Firewall necesite instancias de Filter:

$this->app->when(Firewall::class)
          ->needs(Filter::class)
          ->give([
              NullFilter::class,
              ProfanityFilter::class,
              TooLongFilter::class,
          ]);

#Dependencias Variádicas Etiquetadas

A veces una clase puede tener una dependencia variádica que está type-hint como una clase dada (Report ...$reports). Usando los métodos needs y giveTagged, puede inyectar fácilmente todas las vinculaciones del contenedor con esa etiqueta para la dependencia dada:

$this->app->when(ReportAggregator::class)
    ->needs(Report::class)
    ->giveTagged('reports');

#Etiquetado

Ocasionalmente, puede necesitar resolver toda una "categoría" de bindings. Por ejemplo, tal vez está construyendo un analizador de reportes que recibe un array de muchas implementaciones diferentes de la interfaz Report. Después de registrar las implementaciones de Report, puede asignarles una etiqueta usando el método tag:

$this->app->bind(CpuReport::class, function () {
    // ...
});

$this->app->bind(MemoryReport::class, function () {
    // ...
});

$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');

Una vez que los servicios han sido etiquetados, puede resolverlos fácilmente todos a través del método tagged del contenedor:

$this->app->bind(ReportAnalyzer::class, function (Application $app) {
    return new ReportAnalyzer($app->tagged('reports'));
});

#Extensión de Bindings

El método extend permite modificar servicios resueltos. Por ejemplo, cuando un servicio es resuelto, puede ejecutar código adicional para decorar o configurar el servicio. El método extend acepta dos argumentos: la clase del servicio que está extendiendo y un closure que debe devolver el servicio modificado. El closure recibe el servicio que se está resolviendo y la instancia del contenedor:

$this->app->extend(Service::class, function (Service $service, Application $app) {
    return new DecoratedService($service);
});

#Resolución

#El Método make

Puede usar el método make para resolver una instancia de clase desde el contenedor. El método make acepta el nombre de la clase o interfaz que desea resolver:

use App\Services\Transistor;

$transistor = $this->app->make(Transistor::class);

Si algunas de las dependencias de su clase no son resolubles a través del contenedor, puede inyectarlas pasando un array asociativo al método makeWith. Por ejemplo, podemos pasar manualmente el argumento $id requerido por el constructor del servicio Transistor:

use App\Services\Transistor;

$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);

El método bound puede usarse para determinar si una clase o interfaz ha sido vinculada explícitamente en el contenedor:

if ($this->app->bound(Transistor::class)) {
    // ...
}

Si está fuera de un service provider en una parte de su código que no tiene acceso a la variable $app, puede usar el facade App o el helper app para resolver una instancia de clase desde el contenedor:

use App\Services\Transistor;
use Illuminate\Support\Facades\App;

$transistor = App::make(Transistor::class);

$transistor = app(Transistor::class);

Si desea que la instancia del contenedor Laravel sea inyectada en una clase que está siendo resuelta por el contenedor, puede type-hint la clase Illuminate\Container\Container en el constructor de su clase:

use Illuminate\Container\Container;

/**
 * Crear una nueva instancia de clase.
 */
public function __construct(
    protected Container $container
) {}

#Inyección Automática

Alternativamente, y de manera importante, puede type-hint la dependencia en el constructor de una clase que es resuelta por el contenedor, incluyendo controladores, listeners de eventos, middleware y más. Además, puede type-hint dependencias en el método handle de los trabajos en cola. En la práctica, esta es la forma en que la mayoría de sus objetos deberían ser resueltos por el contenedor.

Por ejemplo, puede type-hint un repositorio definido por su aplicación en el constructor de un controlador. El repositorio será resuelto e inyectado automáticamente en la clase:

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;
use App\Models\User;

class UserController extends Controller
{
    /**
     * Crear una nueva instancia del controlador.
     */
    public function __construct(
        protected UserRepository $users,
    ) {}

    /**
     * Mostrar el usuario con el ID dado.
     */
    public function show(string $id): User
    {
        $user = $this->users->findOrFail($id);

        return $user;
    }
}

#Invocación de Métodos e Inyección

A veces puede desear invocar un método en una instancia de objeto permitiendo que el contenedor inyecte automáticamente las dependencias de ese método. Por ejemplo, dada la siguiente clase:

<?php

namespace App;

use App\Repositories\UserRepository;

class UserReport
{
    /**
     * Generar un nuevo reporte de usuario.
     */
    public function generate(UserRepository $repository): array
    {
        return [
            // ...
        ];
    }
}

Puede invocar el método generate a través del contenedor así:

use App\UserReport;
use Illuminate\Support\Facades\App;

$report = App::call([new UserReport, 'generate']);

El método call acepta cualquier callable de PHP. El método call del contenedor incluso puede usarse para invocar un closure inyectando automáticamente sus dependencias:

use App\Repositories\UserRepository;
use Illuminate\Support\Facades\App;

$result = App::call(function (UserRepository $repository) {
    // ...
});

#Eventos del Contenedor

El contenedor de servicios dispara un evento cada vez que resuelve un objeto. Puede escuchar este evento usando el método resolving:

use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;

$this->app->resolving(Transistor::class, function (Transistor $transistor, Application $app) {
    // Llamado cuando el contenedor resuelve objetos del tipo "Transistor"...
});

$this->app->resolving(function (mixed $object, Application $app) {
    // Llamado cuando el contenedor resuelve un objeto de cualquier tipo...
});

Como puede ver, el objeto que se está resolviendo será pasado al callback, permitiéndole establecer propiedades adicionales en el objeto antes de que sea entregado a su consumidor.

#PSR-11

El contenedor de servicios de Laravel implementa la interfaz PSR-11. Por lo tanto, puede type-hint la interfaz del contenedor PSR-11 para obtener una instancia del contenedor Laravel:

use App\Services\Transistor;
use Psr\Container\ContainerInterface;

Route::get('/', function (ContainerInterface $container) {
    $service = $container->get(Transistor::class);

    // ...
});

Se lanzará una excepción si el identificador dado no puede ser resuelto. La excepción será una instancia de Psr\Container\NotFoundExceptionInterface si el identificador nunca fue vinculado. Si el identificador fue vinculado pero no pudo ser resuelto, se lanzará una instancia de Psr\Container\ContainerExceptionInterface.