Saltar al contenido principal

¿Qué es un Contract?

Los “Contracts” de Laravel son un conjunto de interfaces que definen los servicios principales que ofrece el framework. Por ejemplo, el Contract Illuminate\Contracts\Queue\Queue define los métodos necesarios para encolar jobs, y Illuminate\Contracts\Mail\Mailer define los necesarios para enviar correos. Cada Contract tiene su implementación correspondiente proporcionada por el framework. Por ejemplo, Laravel ofrece implementaciones de cola para distintos drivers y un mailer basado en Symfony Mailer. Todos los Contracts de Laravel están en un repositorio de GitHub dedicado. Es una referencia rápida a todos los Contracts disponibles y un paquete aislado que puedes usar para construir paquetes que se integran con los servicios de Laravel.
Un Contract es simplemente una interfaz. Funciona exactamente igual que cualquier otra interfaz de PHP. Laravel proporciona una implementación para cada una y la inyecta a través del contenedor de servicios.

Diferencia entre Contract y Facade

Las facades y las funciones helper ofrecen una forma sencilla de usar los servicios de Laravel sin necesidad de resolver un Contract mediante type-hinting desde el contenedor. En la mayoría de los casos, cada facade tiene su Contract equivalente. Las principales diferencias son:
AspectoFacadeContract
Declaración de dependenciaNo es necesaria (se llama desde cualquier lugar)Se declara explícitamente en el constructor
PruebasSe mockea con shouldReceive()Se sustituye con librerías de mock estándar
Uso principalUso cómodo dentro de la aplicaciónDesarrollo de paquetes, gestión explícita de dependencias
LegibilidadBasta una línea de importLas dependencias se ven de un vistazo en el constructor
Las facades no requieren declararse en el constructor de la clase, mientras que los Contracts se definen como dependencias explícitas ahí. Algunos desarrolladores prefieren esa dependencia explícita y usan Contracts; otros prefieren la comodidad de las facades. En general, la mayoría de las aplicaciones pueden usar facades sin problemas durante su desarrollo.

Cuándo usar Contracts

Elegir entre Contract y Facade depende del gusto personal o del gusto del equipo. Ambos permiten crear aplicaciones Laravel robustas y fáciles de testear. No son excluyentes: puedes usar Facades en una parte de la aplicación y Contracts en otra. Los Contracts resultan especialmente útiles en estos casos:
  • Construir paquetes que se integran con varios frameworks PHP: usando el paquete illuminate/contracts puedes definir la integración con los servicios de Laravel sin exigir implementaciones concretas de Laravel en tu composer.json.
  • Hacer explícitas las dependencias: basta con leer el constructor para saber de qué depende la clase.
  • Intercambiar implementaciones: es fácil sustituir por otra implementación a través del contenedor de servicios.

Cómo usar un Contract

¿Cómo se obtiene la implementación de un Contract? Es muy sencillo. Muchas clases de Laravel (controladores, listeners de eventos, middlewares, jobs de cola, closures de ruta, etc.) se resuelven a través del contenedor de servicios. Para obtener la implementación de un Contract basta con hacer “type-hint” de la interfaz en el constructor de la clase que se resuelve. Por ejemplo, mira este listener:
<?php

namespace App\Listeners;

use App\Events\OrderWasPlaced;
use App\Models\User;
use Illuminate\Contracts\Redis\Factory;

class CacheOrderInformation
{
    /**
     * Crea el listener del evento
     */
    public function __construct(
        protected Factory $redis,
    ) {}

    /**
     * Maneja el evento
     */
    public function handle(OrderWasPlaced $event): void
    {
        // ...
    }
}
Cuando se resuelve el listener, el contenedor lee los type-hints del constructor de la clase e inyecta los valores adecuados.

Creación de un Contract propio

Crear un Contract propio te permite dejar claras las dependencias entre los componentes de tu aplicación.
1

Definir la interfaz

Crea la interfaz en el directorio app/Contracts.
<?php

namespace App\Contracts;

interface PaymentGateway
{
    /**
     * Cobra el importe indicado
     */
    public function charge(int $amount, string $token): bool;

    /**
     * Reembolsa un pago
     */
    public function refund(string $transactionId): bool;
}
2

Crear la clase de implementación

Crea una clase que implemente el Contract.
<?php

namespace App\Services;

use App\Contracts\PaymentGateway;

class StripePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $token): bool
    {
        // Procesamiento del pago vía la API de Stripe...
        return true;
    }

    public function refund(string $transactionId): bool
    {
        // Reembolso vía la API de Stripe...
        return true;
    }
}
3

Registrar el binding en un service provider

Registra la relación Contract / implementación en un service provider.
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;

$this->app->singleton(PaymentGateway::class, StripePaymentGateway::class);
4

Recibir la inyección por type-hint

Añade el type-hint en el constructor del controlador o clase que lo necesite.
<?php

namespace App\Http\Controllers;

use App\Contracts\PaymentGateway;
use Illuminate\Http\Request;

class OrderController extends Controller
{
    public function __construct(
        protected PaymentGateway $payment,
    ) {}

    public function store(Request $request)
    {
        $this->payment->charge(
            $request->amount,
            $request->payment_token
        );

        // ...
    }
}
Con este patrón, si cambias tu proveedor de pagos de Stripe a otro, basta con modificar el binding en un solo sitio, y el código del controlador no cambia.

Lista de Contracts principales

Tabla de correspondencia (parcial) entre Contracts habituales y sus facades.
ContractFacade correspondiente
Illuminate\Contracts\Auth\Access\GateGate
Illuminate\Contracts\Auth\FactoryAuth
Illuminate\Contracts\Bus\DispatcherBus
Illuminate\Contracts\Cache\FactoryCache
Illuminate\Contracts\Cache\RepositoryCache::driver()
Illuminate\Contracts\Config\RepositoryConfig
Illuminate\Contracts\Console\KernelArtisan
Illuminate\Contracts\Container\ContainerApp
Illuminate\Contracts\Encryption\EncrypterCrypt
Illuminate\Contracts\Events\DispatcherEvent
Illuminate\Contracts\Filesystem\FactoryStorage
Illuminate\Contracts\Filesystem\FilesystemStorage::disk()
Illuminate\Contracts\Hashing\HasherHash
Illuminate\Contracts\Mail\MailerMail
Illuminate\Contracts\Notifications\DispatcherNotification
Illuminate\Contracts\Queue\FactoryQueue
Illuminate\Contracts\Queue\QueueQueue::connection()
Illuminate\Contracts\Queue\ShouldQueue
Illuminate\Contracts\Redis\FactoryRedis
Illuminate\Contracts\Routing\ResponseFactoryResponse
Illuminate\Contracts\Routing\UrlGeneratorURL
Illuminate\Contracts\Session\SessionSession::driver()
Illuminate\Contracts\Translation\TranslatorLang
Illuminate\Contracts\Validation\FactoryValidator
Illuminate\Contracts\View\FactoryView
Puedes consultar la lista completa de Contracts en el repositorio illuminate/contracts.

Próximos pasos

Facades

Descubre el funcionamiento y las técnicas de prueba de las facades.
Última modificación el 13 de julio de 2026