Wat is de service container
De service container van Laravel is een mechanisme voor het beheren van klasse-afhankelijkheden en het uitvoeren van dependency injection. Dependency injection betekent dat de afhankelijkheden die een klasse nodig heeft via de constructor — of soms via settermethoden — in de klasse worden “geïnjecteerd”. Bekijk het volgende voorbeeld.PodcastController podcasts ophalen uit een databron zoals Apple Music. Daarom injecteren we een service die podcasts kan ophalen. Door de service te injecteren kun je bij het testen eenvoudig een mock (dummy-implementatie) van de AppleMusic-service inwisselen.
Een goed begrip van de service container is onmisbaar voor het bouwen van grootschalige Laravel-applicaties. Het helpt ook bij het bijdragen aan de Laravel-core zelf.
Zero-configuration resolution
Als een klasse alleen afhankelijk is van andere concrete klassen (dus geen interfaces), hoef je de container niet te vertellen hoe die moet worden opgelost. Stel bijvoorbeeld dat je de volgende code inroutes/web.php schrijft.
In dit voorbeeld wordt de klasse in het routebestand gedefinieerd, maar dat is alleen ter demonstratie. In een echte applicatie definieer je serviceklassen in de map
app/Services.Service-klasse automatisch op en injecteert deze in de routehandler. Je profiteert van dependency injection zonder een configuratiebestand aan te maken.
Veel van de klassen die je in een Laravel-applicatie schrijft — controllers, event listeners, middleware — krijgen hun afhankelijkheden automatisch via de container geïnjecteerd.
Bindings
Basisbindings
De meeste bindings registreer je in een service provider. Binnen een service provider heb je via de property$this->app toegang tot de container.
bind
Met debind-methode registreer je een binding door een klasse- of interfacenaam en een closure door te geven.
App-facade.
Klassen die niet van interfaces afhangen, hoef je niet aan de container te binden. De container kan deze objecten automatisch oplossen via reflectie.
singleton
Desingleton-methode bindt een klasse of interface zodat deze slechts één keer wordt opgelost. Eenmaal opgelost geeft de container bij volgende aanroepen dezelfde instantie terug.
singletonIf registreer je alleen een singleton-binding als er voor het opgegeven type nog geen binding is geregistreerd.
Het Singleton-attribuut
Je kunt de container ook via het attribuut#[Singleton] op een klasse of interface instrueren om deze slechts één keer op te lossen.
Scoped singletons binden
Descoped-methode bindt een klasse of interface zodat deze slechts één keer wordt opgelost binnen de levenscyclus van een Laravel-request of -job. Deze lijkt op de singleton-methode, maar instanties die met scoped zijn geregistreerd, worden weggegooid telkens wanneer de Laravel-applicatie een nieuwe “levenscyclus” start — bijvoorbeeld wanneer een Laravel Octane-worker een nieuw request verwerkt of een queue worker een nieuwe job verwerkt.
scopedIf registreer je alleen een scoped binding als er voor het opgegeven type nog geen binding is geregistreerd.
Het Scoped-attribuut
Je kunt de container ook via het attribuut#[Scoped] op een klasse of interface instrueren om deze slechts één keer per request-/job-levenscyclus op te lossen.
instance
Je kunt ook een bestaande objectinstantie aan de container binden met deinstance-methode. Bij volgende aanroepen geeft de container altijd die instantie terug.
Interfaces aan implementaties binden
Een van de krachtigste functies van de service container is het binden van een interface aan een specifieke implementatie. Stel dat je eenEventPusher-interface en een RedisEventPusher-implementatie hebt.
RedisEventPusher in klassen die een implementatie van EventPusher nodig hebben. Je hoeft alleen nog de EventPusher-interface te typehinten in de constructor.
Het Bind-attribuut
Laravel biedt daarnaast het handigeBind-attribuut. Door dit attribuut op een interface te plaatsen, vertel je Laravel welke implementatie automatisch moet worden geïnjecteerd wanneer die interface wordt gevraagd. Bij gebruik van het Bind-attribuut is er geen extra registratie in een service provider nodig.
Bovendien kun je meerdere Bind-attributen op een interface plaatsen om per omgeving een andere implementatie te injecteren.
BindWhen gebruiken. Aan de closure kan de container worden doorgegeven; de closure geeft true terug wanneer de binding moet worden toegepast. Bind- en BindWhen-attributen worden geëvalueerd in de volgorde waarin ze zijn gedeclareerd.
Voor het
BindWhen-attribuut is PHP 8.5 of hoger vereist.Automatisch oplossen (DI via typehints)
Bij het oplossen van klassen zoals controllers, event listeners en middleware kijkt de service container naar de typehints in de constructor en injecteert de afhankelijkheden automatisch.UserRepository niet van een interface afhangt, is registratie in de container niet nodig. Je hoeft alleen de route te bezoeken en de container lost de afhankelijkheid automatisch op en injecteert deze in de controller.
Oplossen vanuit de container
De make-methode
Met demake-methode los je een klasse-instantie op vanuit de container.
makeWith extra argumenten doorgeven.
Automatische injectie
In de praktijk roep je demake-methode zelden rechtstreeks aan. Voeg simpelweg typehints toe aan de constructor van klassen die de container oplost (controllers, event listeners, middleware enz.) en de container injecteert ze automatisch.
De relatie tussen facades en de container
Laravel-facades bieden een statische interface naar objecten in de container. Zo haaltCache::get() intern de Cache-service uit de container en roept die aan.
Praktijkvoorbeeld van constructor injection
Laten we een typisch patroon uit een echte applicatie bekijken.1
De interface definiëren
2
De implementatieklasse maken
3
Binden in een service provider
4
De injectie ontvangen in een controller
Stripe naar een andere provider omzet, alleen de binding op één plek aan te passen.
Volgende stap
Service providers
Leer hoe je bindings registreert met service providers.