Salta al contenuto principale
Laravel

Laravel Service Container: Dependency Injection Made Easy

Comprendi il Service Container di Laravel e come sfruttare la Dependency Injection per codice più testabile.

Giovanni D'Ippolito
3 min di lettura
Autore
Giovanni D'Ippolito
Pubblicato
11 November 2025
Lettura
3 minuti
Tags
#Laravel #Service Container #Dependency Injection #Design Patterns

Cos'è il Service Container?

Il Service Container è uno strumento potente per gestire le dipendenze delle classi e eseguire la dependency injection in Laravel.

Dependency Injection Base

Laravel risolve automaticamente le dipendenze nei controller:

<?php
namespace App\Http\Controllers;

use App\Services\PaymentService;
use App\Repositories\OrderRepository;

class OrderController extends Controller
{
    // Laravel inietta automaticamente le dipendenze
    public function __construct(
        protected PaymentService $paymentService,
        protected OrderRepository $orderRepository
    ) {}

    public function store(Request $request)
    {
        $order = $this->orderRepository->create($request->validated());
        $this->paymentService->process($order);

        return response()->json($order, 201);
    }
}
?>

Binding nel Service Container

Registra i tuoi servizi nel container:

<?php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\PaymentService;
use App\Services\StripePaymentService;
use App\Repositories\OrderRepository;
use App\Repositories\EloquentOrderRepository;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Binding semplice
        $this->app->bind(PaymentService::class, StripePaymentService::class);

        // Singleton - una sola istanza condivisa
        $this->app->singleton(OrderRepository::class, EloquentOrderRepository::class);

        // Binding con closure per logica complessa
        $this->app->bind(PaymentService::class, function ($app) {
            $gateway = config('services.payment.gateway');

            return match($gateway) {
                'stripe' => new StripePaymentService(),
                'paypal' => new PayPalPaymentService(),
                default => throw new \Exception('Invalid payment gateway'),
            };
        });
    }
}
?>
La differenza tra bind() e singleton() è che singleton crea una sola istanza riutilizzata, mentre bind crea una nuova istanza ogni volta.

Interfacce e Implementazioni

Programma verso interfacce per codice più flessibile:

<?php
// Interfaccia
namespace App\Contracts;

interface PaymentGateway
{
    public function charge(int $amount, string $currency): bool;
    public function refund(string $transactionId): bool;
}

// Implementazione Stripe
namespace App\Services;

use App\Contracts\PaymentGateway;

class StripePaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $currency): bool
    {
        // Logica Stripe
        return true;
    }

    public function refund(string $transactionId): bool
    {
        // Logica rimborso Stripe
        return true;
    }
}

// Implementazione PayPal
class PayPalPaymentGateway implements PaymentGateway
{
    public function charge(int $amount, string $currency): bool
    {
        // Logica PayPal
        return true;
    }

    public function refund(string $transactionId): bool
    {
        // Logica rimborso PayPal
        return true;
    }
}
?>

Registra l'interfaccia nel Service Provider:

<?php
// AppServiceProvider.php
public function register(): void
{
    $this->app->bind(
        \App\Contracts\PaymentGateway::class,
        \App\Services\StripePaymentGateway::class
    );
}
?>

Ora puoi usare l'interfaccia nei tuoi controller:

<?php
use App\Contracts\PaymentGateway;

class CheckoutController extends Controller
{
    public function __construct(
        protected PaymentGateway $paymentGateway
    ) {}

    public function process(Request $request)
    {
        // Funziona con qualsiasi implementazione!
        $success = $this->paymentGateway->charge(
            $request->amount,
            $request->currency
        );

        return response()->json(['success' => $success]);
    }
}
?>
Usando interfacce e il Service Container, puoi facilmente cambiare implementazione senza modificare il codice che le usa!

Vantaggi per i Test

La dependency injection rende il testing molto più semplice:

<?php
namespace Tests\Feature;

use Tests\TestCase;
use App\Contracts\PaymentGateway;
use Mockery;

class CheckoutTest extends TestCase
{
    public function test_checkout_process()
    {
        // Mock del payment gateway
        $mock = Mockery::mock(PaymentGateway::class);
        $mock->shouldReceive('charge')
            ->once()
            ->with(1000, 'EUR')
            ->andReturn(true);

        // Sostituisci nel container
        $this->app->instance(PaymentGateway::class, $mock);

        // Testa il controller
        $response = $this->postJson('/api/checkout', [
            'amount' => 1000,
            'currency' => 'EUR'
        ]);

        $response->assertJson(['success' => true]);
    }
}
?>

Articoli Correlati

Laravel

Laravel Eloquent: Query Optimization per Performance Migliori

Tecniche avanzate per ottimizzare le query Eloquent e migliorare le performance della tua...

03 Nov 2025 Leggi
Laravel

Laravel Jobs e Queues: Guida Completa all'Elaborazione Asincrona

Impara a usare Jobs e Queues in Laravel per migliorare le performance e l'esperienza utent...

19 Nov 2025 Leggi
Laravel

Laravel Testing: TDD con Pest PHP

Impara il Test-Driven Development con Pest, il framework di testing moderno per PHP.

27 Nov 2025 Leggi

Rimani Aggiornato

Iscriviti alla nostra newsletter per ricevere gli ultimi articoli direttamente nella tua casella di posta.