Skip to main content

Cos’è il service container

Il service container di Laravel è il meccanismo che gestisce le dipendenze delle classi ed esegue la dependency injection. Per “dependency injection” si intende “iniettare” nella classe le sue dipendenze attraverso il costruttore o, in alcuni casi, un setter. Vediamo un esempio.
PodcastController deve recuperare un podcast da una fonte dati come Apple Music. Iniettiamo quindi il servizio che sa recuperarlo. Grazie all’injection, in test possiamo sostituire facilmente AppleMusic con un mock (implementazione fittizia).
Capire a fondo il service container è indispensabile per costruire grandi applicazioni Laravel. Ti aiuta anche a contribuire al core di Laravel stesso.

Risoluzione zero-configuration

Se una classe dipende solo da altre classi concrete (non interfacce), non devi dire al container come risolverla. Ad esempio, questo codice in routes/web.php:
L’esempio definisce la classe direttamente nel file di route a scopo dimostrativo. In un’applicazione reale, le classi di servizio vanno nella directory app/Services.
Accedendo a questa route, Laravel risolve automaticamente Service e lo inietta nell’handler della route. Beneficerai della dependency injection senza scrivere alcuna configurazione. Nella maggior parte delle classi di un’app Laravel — controller, event listener, middleware ecc. — le dipendenze vengono iniettate automaticamente dal container.

Binding

Binding di base

La maggior parte dei binding si registra all’interno dei service provider. Nel provider puoi accedere al container tramite $this->app.

bind

Con bind registri un binding passando un nome di classe o interfaccia e una closure.
La closure riceve come argomento il container stesso, con cui puoi risolvere le sub-dipendenze. Fuori da un service provider puoi operare sul container tramite la facade App.
Le classi che non dipendono da interfacce non hanno bisogno di essere bindate. Il container le risolve automaticamente tramite reflection.

singleton

Il metodo singleton registra un binding che verrà risolto una sola volta. Una volta risolto, tutte le chiamate successive restituiranno la stessa istanza.
Con singletonIf registri il binding singleton solo se il tipo non è già bindato.

Attributo Singleton

Puoi indicare al container di risolvere una sola volta anche applicando l’attributo #[Singleton] alla classe o interfaccia.

Binding scoped

Con scoped bindi una classe o interfaccia a un’istanza risolta una sola volta all’interno del ciclo di vita di una richiesta / job. Simile a singleton, ma le istanze registrate con scoped vengono scartate ogni volta che l’applicazione avvia un nuovo “ciclo di vita” — ad esempio quando un worker di Laravel Octane processa una nuova richiesta o un queue worker elabora un nuovo job.
Con scopedIf registri il binding scoped solo se non è già registrato.

Attributo Scoped

Puoi ottenere lo stesso comportamento applicando l’attributo #[Scoped] alla classe o interfaccia.

instance

Puoi bindare un’istanza già esistente con instance. Tutte le chiamate successive restituiscono quella stessa istanza.

Bindare un’interfaccia a un’implementazione

Una delle potenzialità del service container è la possibilità di bindare un’interfaccia a un’implementazione specifica. Ad esempio, dati l’interfaccia EventPusher e la sua implementazione RedisEventPusher:
Il container inietterà RedisEventPusher in tutte le classi che dipendono da EventPusher. Basta usare il type hint dell’interfaccia nel costruttore.
Dipendere dall’interfaccia permette di sostituire l’implementazione senza modificare il codice. Rende più semplici test e futuri cambiamenti.

Attributo Bind

Laravel offre anche il pratico attributo Bind. Applicandolo all’interfaccia, dici a Laravel quale implementazione iniettare automaticamente quando quell’interfaccia viene richiesta. Con Bind non serve registrare il binding nel service provider. Puoi mettere più Bind sulla stessa interfaccia per iniettare implementazioni diverse a seconda dell’ambiente.
Per binding basati su condizioni arbitrarie, usa l’attributo BindWhen. La closure riceve il container e restituisce true quando il binding va applicato. Bind e BindWhen sono valutati nell’ordine di dichiarazione.
Per usare BindWhen serve PHP 8.5 o superiore.
Combinandoli con gli attributi Singleton o Scoped indichi se il binding va risolto una sola volta o una volta per richiesta / job.

Risoluzione automatica (DI tramite type hint)

Nella risoluzione di classi come controller, event listener e middleware, il service container guarda i type hint del costruttore e inietta automaticamente le dipendenze.
Se UserRepository non dipende da un’interfaccia, non serve registrarla nel container: basta accedere a una route che usi il controller e il container risolve e inietta le dipendenze.

Risolvere dal container

Metodo make

Con make risolvi un’istanza dal container.
Se una dipendenza non può essere risolta dal container, puoi passare argomenti aggiuntivi con makeWith.

Injection automatica

In pratica, quasi non chiamerai mai make direttamente. Basta aggiungere i type hint al costruttore delle classi che il container risolve (controller, event listener, middleware ecc.) e il container inietterà automaticamente le dipendenze.

Facade e container

Le facade di Laravel sono un’interfaccia statica agli oggetti del container. Ad esempio, Cache::get() recupera internamente il servizio Cache dal container e ne chiama il metodo.
Le facade sono comodi wrapper del container. Nei test puoi sostituire una facade con un mock.

Esempio pratico di constructor injection

Vediamo un pattern tipico in un’applicazione reale.
1

Definisci l'interfaccia

2

Crea la classe di implementazione

3

Registra il binding nel service provider

4

Ricevi l'injection nel controller

Con questo pattern, se passi da Stripe a un altro provider di pagamento, basta cambiare il binding in un unico punto.

Prossimi passi

Service provider

Come registrare i binding usando i service provider.
Ultima modifica il 2 agosto 2026