Retour

/ 4 min read

Comprendre les Injections de Dépendances avec Laravel

Last Updated:

Introduction

L’injection de dépendances est un concept clé dans Laravel qui favorise la modularité, le découplage et la facilité des tests dans vos applications. Elle permet à Laravel de fournir automatiquement à vos classes les objets dont elles ont besoin sans que vous ayez à gérer leur instanciation manuellement.

Dans cet article, nous expliquerons le fonctionnement de l’injection de dépendances dans Laravel, avec des exemples concrets et un focus sur les tests.


Qu’est-ce que l’Injection de Dépendances ?

L’injection de dépendances consiste à fournir à une classe les objets dont elle a besoin, plutôt que de les créer directement à l’intérieur de celle-ci. Cela permet :

  • Une meilleure maintenabilité
  • Des tests unitaires faciles avec des mocks
  • Moins de couplage entre les classes
  • Une réutilisabilité accrue des composants

Avec Laravel, cette injection est très facile à utiliser grâce à son mécanisme intégré qui s’occupe de résoudre automatiquement les objets nécessaires.


Exemple Simple

Prenons une classe PaymentService qui dépend d’un service externe de traitement des paiements :

Terminal window
<?php
class PaymentService { private StripePaymentGateway $paymentGateway;
public function __construct(StripePaymentGateway $paymentGateway)
{
$this->paymentGateway = $paymentGateway;
}
public function processPayment(float $amount, string $token): bool
{
// Appelle le service Stripe pour traiter un paiement
return $this->paymentGateway->charge($amount, $token);
}
}

Lorsqu’on utilise cette classe, Laravel résout automatiquement la dépendance grâce au conteneur IoC :

Terminal window
<?php
use App\Services\PaymentService;
Route::post('pay', function (PaymentService paymentService) {
paymentService->processPayment(100.0, 'tok_visa');
});

Injection d’Interfaces

Pourquoi utiliser des interfaces ?

En utilisant une interface au lieu d’une classe concrète, vous rendez votre code plus flexible et facilement testable.

Exemple avec une interface

  1. Déclarez l’interface et deux implémentations possibles :
Terminal window
<?php
interface PaymentGatewayInterface {
public function charge(float amount, stringtoken): bool;
}
class StripePaymentGateway implements PaymentGatewayInterface {
public function charge(float amount, stringtoken): bool {
// Traitement du paiement avec Stripe
return true;
}
}
class PayPalPaymentGateway implements PaymentGatewayInterface {
public function charge(float amount, stringtoken): bool {
// Traitement du paiement avec PayPal
return true;
}
}
  1. Configurez le conteneur IoC pour utiliser une implémentation spécifique :
Terminal window
<?php
// AppServiceProvider
public function register(): void {
$this->app->bind(PaymentGatewayInterface::class, StripePaymentGateway::class);
}
  1. Utilisez l’injection de dépendances dans vos classes :
Terminal window
<?php
class PaymentService {
private PaymentGatewayInterface $paymentGateway;
public function __construct(PaymentGatewayInterface $paymentGateway)
{
$this->paymentGateway = $paymentGateway;
}
public function processPayment(float $amount, string $token): bool
{
return $this->paymentGateway->charge($amount, $token);
}
}

Tests et Mocking

L’un des plus grands avantages de l’injection de dépendances est la facilité à écrire des tests unitaires. Cela devient particulièrement utile lorsque vous travaillez avec des services externes difficiles à tester en environnement local.

Exemple avec un Mock

  1. Écrivons un test pour vérifier que PaymentService appelle correctement charge() :
Terminal window
<?php
use PHPUnit\Framework\TestCase;
use App\Services\PaymentService;
use App\Contracts\PaymentGatewayInterface;
class PaymentServiceTest extends TestCase {
public function testProcessPaymentCallsChargeMethod() {
// Mock de l'interface
PaymentGatewayInterface paymentGatewayMock = this->createMock(PaymentGatewayInterface::class);
// On s'attend à ce que la méthode `charge` soit appelée une fois
$paymentGatewayMock->expects($this->once())
->method('charge')
->with(100.0, 'test_token')
->willReturn(true);
// Instanciation du PaymentService avec le mock
$paymentService = new PaymentService($paymentGatewayMock);
// Exécution de la méthode à tester
$result = $paymentService->processPayment(100.0, 'test_token');
// Assertion sur le résultat
$this->assertTrue($result);
}
}

Pourquoi utiliser un Mock ?

  • Vous n’appelez pas réellement des services externes comme Stripe ou PayPal pendant vos tests.
  • Vous pouvez définir des comportements spécifiques pour vos dépendances simulées.

Scénarios Avancés

Binding Contextuel

Supposons que votre application utilise différents services de PDF en fonction de l’environnement (local ou production).

  1. Configurez un binding contextuel :
Terminal window
<?php
use App\Services\PDF\LocalPDFGenerator;
use App\Services\PDF\CloudPDFGenerator;
$this->app->when(InvoiceService::class) ->needs(PDFGeneratorInterface::class) ->give(function () {
return app()->environment('production') ? new CloudPDFGenerator() : new LocalPDFGenerator();
});
  1. Utilisez le service dans vos classes :
Terminal window
<?php
class InvoiceService {
private PDFGeneratorInterface $pdfGenerator;
public function __construct(PDFGeneratorInterface $pdfGenerator)
{
$this->pdfGenerator = $pdfGenerator;
}
public function generateInvoice(Order $order): string
{
return $this->pdfGenerator->generate($order->details);
}
}

Bonnes Pratiques

  1. Toujours injecter des interfaces, pas des classes concrètes. Cela rend votre code plus maintenable et testable.

  2. Utilisez des Mocks pour les dépendances externes lors des tests. Cela garantit que vos tests unitaires restent indépendants.

  3. Évitez l’utilisation du conteneur IoC directement dans les classes métier. Préférez l’injection constructeur.


Conclusion

L’injection de dépendances est une base solide pour construire des applications Laravel modulaires, testables et maintenables. En combinant cette technique avec des interfaces et du mocking dans vos tests, vous serez en mesure de développer des applications professionnelles de haute qualité.

Adoptez ces pratiques dans vos projets Laravel, et vous constaterez rapidement une amélioration de la lisibilité, de la maintenabilité et de la robustesse de vos applications.