Vai al contenuto principale

Cos’è un Contract

I “Contracts” di Laravel sono un insieme di interfacce che definiscono i servizi principali offerti dal framework. Ad esempio, il Contract Illuminate\Contracts\Queue\Queue definisce i metodi necessari a mettere in coda i job, mentre il Contract Illuminate\Contracts\Mail\Mailer definisce i metodi necessari per l’invio di email. Per ogni Contract il framework fornisce un’implementazione corrispondente. Laravel offre ad esempio implementazioni di code con vari driver e un mailer basato su Symfony Mailer. Tutti i Contracts di Laravel si trovano nel loro repository GitHub dedicato. Costituisce un riferimento rapido a tutti i Contract disponibili ed è un pacchetto isolato che puoi usare quando costruisci pacchetti che si integrano con i servizi Laravel.
Un Contract è semplicemente un’interfaccia. Funziona esattamente come le interfacce di PHP. Laravel fornisce le implementazioni di queste interfacce e le inietta tramite il service container.

Differenza tra Contract e Facade

Le facade e le funzioni helper offrono un modo semplice per usare i servizi di Laravel senza dover risolvere i Contract con un type hint dal service container. Nella maggior parte dei casi, ciascuna facade ha un Contract corrispondente. Le differenze principali tra facade e Contract sono le seguenti.
AspettoFacadeContract
Dichiarazione della dipendenzaNon necessaria (richiamabile ovunque)Dichiarata esplicitamente nel costruttore
TestMock con shouldReceive()Sostituzione tramite una libreria di mock standard
Uso principaleUso agevole all’interno dell’applicazioneSviluppo di pacchetti, gestione esplicita delle dipendenze
Leggibilità del codiceBasta una singola riga di importLe dipendenze sono immediatamente visibili nel costruttore
Le facade non richiedono di dichiararle nel costruttore della classe, mentre i Contract puoi definirli come dipendenze esplicite nel costruttore. Alcuni sviluppatori preferiscono questa esplicitezza e usano i Contract; altri preferiscono la comodità delle facade. In generale, la maggior parte delle applicazioni può usare le facade senza problemi durante lo sviluppo.

Quando usare i Contract

Scegliere tra Contract e Facade dipende dalle preferenze personali o del team. Entrambi permettono di creare applicazioni Laravel robuste e testabili. Non si escludono a vicenda: in una parte dell’applicazione puoi usare le Facade e in un’altra affidarti ai Contract. I Contract sono particolarmente utili nelle seguenti situazioni:
  • Quando costruisci pacchetti che si integrano con più framework PHP — Usando il pacchetto illuminate/contracts per definire l’integrazione con i servizi Laravel, non devi richiedere l’implementazione concreta di Laravel nel composer.json.
  • Quando vuoi rendere esplicite le dipendenze — Guardando il costruttore capisci a colpo d’occhio da cosa dipende la classe.
  • Quando vuoi sostituire l’implementazione — Diventa facile sostituire l’implementazione tramite il service container.

Come usare i Contract

Come si ottiene l’implementazione di un Contract? È molto semplice. Molti tipi di classi di Laravel — controller, listener di eventi, middleware, job in coda, closure di route e altre — vengono risolti tramite il service container. Per ottenere l’implementazione di un Contract basta “type-hintare” l’interfaccia nel costruttore della classe risolta. Ad esempio, guarda il seguente event listener.
<?php

namespace App\Listeners;

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

class CacheOrderInformation
{
    /**
     * Crea l'event listener
     */
    public function __construct(
        protected Factory $redis,
    ) {}

    /**
     * Gestisce l'evento
     */
    public function handle(OrderWasPlaced $event): void
    {
        // ...
    }
}
Quando l’event listener viene risolto, il service container legge il type hint del costruttore e inietta il valore appropriato.

Creazione di Contract personalizzati

Creare i propri Contract permette di rendere chiare le dipendenze tra i componenti dell’applicazione.
1

Definisci l'interfaccia

Crea l’interfaccia nella directory app/Contracts.
<?php

namespace App\Contracts;

interface PaymentGateway
{
    /**
     * Effettua un pagamento dell'importo specificato
     */
    public function charge(int $amount, string $token): bool;

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

Crea la classe di implementazione

Crea la classe che implementa il Contract.
<?php

namespace App\Services;

use App\Contracts\PaymentGateway;

class StripePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $token): bool
    {
        // Logica di pagamento tramite l'API di Stripe...
        return true;
    }

    public function refund(string $transactionId): bool
    {
        // Logica di rimborso tramite l'API di Stripe...
        return true;
    }
}
3

Collega Contract e implementazione in un service provider

Effettua il binding tra Contract e implementazione in un service provider.
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;

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

Ricevi l'iniezione tramite type hint

Fai il type hint nel costruttore del controller o di altre classi.
<?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 questo pattern, se in futuro cambi il servizio di pagamento da Stripe a un altro provider, ti basta modificare il binding in un solo punto senza toccare il codice del controller.

Elenco dei Contract principali

Tabella di corrispondenza tra i Contract più usati e le rispettive facade (estratto).
ContractFacade corrispondente
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
Puoi trovare la lista completa dei Contract nel repository illuminate/contracts.

Prossimi passi

Facade

Approfondisci il funzionamento delle facade e i loro metodi di test.
Ultima modifica il 13 luglio 2026