Passer au contenu principal

Qu’est-ce qu’un contrat

Les « Contracts » de Laravel sont un ensemble d’interfaces qui définissent les services centraux fournis par le framework. Par exemple, le contrat Illuminate\Contracts\Queue\Queue définit les méthodes nécessaires pour mettre en file d’attente des jobs, et Illuminate\Contracts\Mail\Mailer définit les méthodes nécessaires pour envoyer des e-mails. Chaque contrat possède une implémentation fournie par le framework. Par exemple, Laravel fournit des implémentations de file pour divers drivers, et un mailer basé sur Symfony Mailer. Tous les contrats Laravel se trouvent dans un dépôt GitHub dédié. Ce dépôt sert de référence rapide aux contrats disponibles et constitue un package isolé utilisable pour construire des packages qui s’intègrent aux services Laravel.
Un contrat n’est qu’une interface. Il fonctionne comme n’importe quelle interface PHP. Laravel fournit une implémentation et l’injecte via le conteneur de services.

Différences entre contrat et façade

Les façades et les fonctions helper permettent d’utiliser facilement les services Laravel sans devoir résoudre un contrat via un type-hint. Dans la plupart des cas, à chaque façade correspond un contrat. Différences majeures :
AspectFaçadeContrat
Déclaration de dépendanceInutile (appelable partout)Déclarée explicitement dans le constructeur
TestsMock via shouldReceive()Remplacement via une bibliothèque de mock standard
Usage principalUtilisation pratique dans l’applicationDéveloppement de packages, gestion explicite des dépendances
LisibilitéImportation en une ligneDépendances visibles dans le constructeur
Les façades n’ont pas besoin d’être déclarées dans le constructeur, alors que les contrats se déclarent explicitement comme dépendances. Certains développeurs préfèrent cette déclaration explicite et utilisent les contrats. D’autres apprécient la commodité des façades. En général, les façades conviennent à la plupart des applications sans problème.

Quand utiliser un contrat

Le choix entre contrats et façades relève de la préférence personnelle ou d’équipe. Les deux permettent d’écrire des applications Laravel robustes et testables. Ils ne s’excluent pas : vous pouvez utiliser les façades dans certaines parties et les contrats dans d’autres. Les contrats sont particulièrement utiles pour :
  • Construire un package qui s’intègre à plusieurs frameworks PHP — en utilisant le package illuminate/contracts, vous définissez l’intégration avec les services Laravel sans exiger une implémentation concrète dans composer.json.
  • Rendre explicites les dépendances — regarder le constructeur suffit à voir de quoi la classe dépend.
  • Remplacer une implémentation — le remplacement via le conteneur de services devient très simple.

Utiliser un contrat

Comment récupérer l’implémentation d’un contrat ? C’est très simple. Contrôleurs, listeners, middlewares, jobs de queue, closures de routes… de nombreux types de classes Laravel sont résolus via le conteneur. Pour obtenir l’implémentation d’un contrat, il suffit de le « type-hinter » dans le constructeur de la classe. Par exemple, ce listener :
<?php

namespace App\Listeners;

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

class CacheOrderInformation
{
    /**
     * Crée un nouveau listener.
     */
    public function __construct(
        protected Factory $redis,
    ) {}

    /**
     * Traite l'événement.
     */
    public function handle(OrderWasPlaced $event): void
    {
        // ...
    }
}
Lorsque le listener est résolu, le conteneur lit les type-hints du constructeur et injecte la valeur appropriée.

Créer un contrat personnalisé

Créer vos propres contrats clarifie les dépendances entre composants.
1

Définir l'interface

Créez l’interface dans app/Contracts.
<?php

namespace App\Contracts;

interface PaymentGateway
{
    /**
     * Débite le montant donné.
     */
    public function charge(int $amount, string $token): bool;

    /**
     * Rembourse le paiement.
     */
    public function refund(string $transactionId): bool;
}
2

Créer une implémentation

Créez la classe qui implémente le contrat.
<?php

namespace App\Services;

use App\Contracts\PaymentGateway;

class StripePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $token): bool
    {
        // Traitement du paiement via l'API Stripe...
        return true;
    }

    public function refund(string $transactionId): bool
    {
        // Traitement du remboursement via l'API Stripe...
        return true;
    }
}
3

Enregistrer le binding dans un service provider

Reliez le contrat à son implémentation dans un service provider.
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;

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

Recevoir l'injection via type-hint

Utilisez le type-hint dans le constructeur d’un contrôleur ou d’une autre classe.
<?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
        );

        // ...
    }
}
Grâce à ce pattern, changer de fournisseur (Stripe vers un autre) revient à modifier un seul binding — sans toucher au contrôleur.

Principaux contrats

Contrats fréquents et leurs façades correspondantes (extrait).
ContratFaçade correspondante
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
La liste complète est disponible dans le dépôt illuminate/contracts.

Prochaines étapes

Façades

Revenez sur le fonctionnement et les tests des façades.
Dernière modification le 13 juillet 2026