Naar hoofdinhoud gaan
Wanneer je wilt dat meerdere packages of de gebruikende applicatie functionaliteit kunnen toevoegen aan dezelfde bundelende service, kun je tag() en tagged() van de container gebruiken. De bundelende kant hoeft dan niet alle implementatieklassen te kennen. Deze pagina is gericht op Laravel 13 en is gecontroleerd aan de hand van de implementatie en de officiële tests van Framework v13.38.0. Zie Service container voor de basis van bindings en Packageontwikkeling voor het distribueren van providers.

Een tag groepeert de registratiesleutels van services

tag() voegt containerregistratiesleutels, zoals klassenamen, toe aan de lijst van een tag. Alleen een tag toekennen maakt de service nog niet aan. tagged() geeft een iterable terug waarmee je de services opsomt die onder die tag zijn geregistreerd. Het is geen functie die automatisch zoekt naar klassen die een bepaalde interface implementeren, maar een mechanisme waaraan elke provider expliciet deelneemt. Het staat ook los van de tags voor publiceerbare resources van vendor:publish --tag.

Een uitbreidbaar rapportagepackage bouwen

Een contract voorbereiden dat de implementaties delen

Als voorbeeld bouwen we het package acme/reports, dat meerdere rapporten verzamelt. We gaan uit van een PSR-4-configuratie in Composer waarin Acme\Reports\ overeenkomt met src/. Definieer in src/Contracts/Report.php het contract dat plugins implementeren.
Als minimale implementaties maak je src/RuntimeReport.php en src/MemoryReport.php aan.

De bundelende service ontvangt een iterable

src/ReportCollector.php roept de verzamellogica aan terwijl het elke implementatie opsomt. Als je de publieke tagnaam in een constante zet, kan ook de gebruikende kant naar dezelfde naam verwijzen.
Met alleen het PHP-type iterable kun je het type van de elementen niet valideren, en ook tag() zelf controleert niet of iets een implementatie van Report is. In dit voorbeeld controleert daarom ook de bundelende kant het contract.

In register() alleen bindings en tags registreren

Registreer in src/ReportsServiceProvider.php de standaardimplementaties en de bundelende service. De factory van de bundelende service geeft de opgehaalde iterable ongewijzigd door.
Andere packages of de gebruikende applicatie maken bijvoorbeeld een DailyReport die Report implementeert en voegen die in register() van hun eigen provider aan dezelfde tag toe.
Wanneer je na de registratie van alle providers app(ReportCollector::class)->collect() aanroept, worden ook de toegevoegde implementaties gebundeld. Ook als er meer deelnemende implementaties bijkomen, hoef je de aanmaaklogica van ReportCollector niet te wijzigen.

tagged() en giveTagged() verschillen in het moment van aanmaken

In de gecontroleerde implementatie geeft tagged() voor een geregistreerde tag een RewindableGenerator terug. make() van elke service wordt pas tijdens het opsommen aangeroepen; als je het opsommen halverwege stopt, worden de overige services niet aangemaakt. Daarnaast maakt RewindableGenerator bij elke opsomming een nieuwe Generator aan. Het is geen functie die de resultaten van services cachet: bij een gewone bind() worden de instanties bij opnieuw opsommen ook opnieuw aangemaakt. Services met singleton() of scoped() volgen hun eigen deelregels. Contextuele binding met giveTagged() daarentegen zet het resultaat van tagged() bij het resolven met iterator_to_array() om naar een array. Wil je de afhankelijkheden in één keer als array resolven, dan kun je de eerdere bind() met factory vervangen door de volgende registratie.
Ook als het type van $reports iterable is, is de waarde die op deze manier wordt vastgehouden een array. Het type van het argument alleen garandeert geen lazy resolving. Bovendien resolvet giveTagged() alleen de instanties; het roept collect() van de afzonderlijke implementaties niet aan. resolve() van het Tag-attribuut geeft tagged() ongewijzigd terug. Wanneer je het attribuut gebruikt, zet je het zoals bij Contextuele attributen op een constructorargument en laat je het opbouwen van de klasse aan de container over. Het attribuut wordt niet automatisch toegepast op een new binnen je eigen factory.

Het registratiecontract van plugins niet breken

Dubbele registratie en volgorde expliciet maken

tag() voegt toe aan een lijst en verwijdert geen duplicaten wanneer je dezelfde registratiesleutel nogmaals toevoegt. Wordt dezelfde implementatie twee keer opgesomd, dan wordt collect() twee keer aangeroepen, ook als een singleton hetzelfde object teruggeeft. Breng de tagregistratie samen op één plek: de provider die verantwoordelijk is voor de betreffende implementatie. bindIf() is een API om het overschrijven van bindings te voorkomen en voorkomt geen dubbele tag() daarna. De volgorde van opsommen is de volgorde waarin items aan de taglijst zijn toegevoegd. Tags kennen geen sortering op prioriteit, dus laat de verwerkingsvolgorde tussen verschillende packages niet alleen afhangen van de laadvolgorde van providers. Is de volgorde nodig als publieke specificatie, neem dan een prioriteit of identificatie op in het contract en ontwerp het zo dat de bundelende kant expliciet sorteert.

De provider die tags toevoegt eerst registreren

tagged() voor een niet-geregistreerde tag geeft geen exception maar een lege array terug. Tagregistraties die later worden gedaan, worden niet doorgevoerd in een eerder ontvangen lege array of in een array die al met giveTagged() is aangemaakt. Ga ook bij het lazy opsommen van geregistreerde tags er niet van uit dat de lijst tijdens de uitvoering verandert. De gecontroleerde tagged() geeft het aantal door op het moment van ophalen, maar raadpleegt bij het opsommen de taglijst. Als je het zo ontwerpt dat je pas gebruikmaakt nadat alle providers zijn geregistreerd, voorkom je ook een mismatch tussen het aantal en wat er wordt opgesomd.
Het opsommen van een tag is geen proces dat aan de hand van de tagnaam zoekt naar providers die nog niet zijn geladen. Als je de provider die tags toevoegt een DeferrableProvider maakt, wordt de tagregistratie pas uitgevoerd wanneer die service via een andere weg wordt geresolved, waardoor implementaties in de lijst kunnen ontbreken. Gebruik voor de tagregistratie van implementaties die altijd moeten deelnemen een gewone provider, en ontwerp het zo dat er bij het registreren geen implementaties worden aangemaakt.
De startvoorwaarden van uitgestelde providers lees je in DeferrableProvider, en het laden bij installatie in Automatische detectie van packages.

De levenscyclus van tags en services scheiden

Een tag toekennen maakt een service niet automatisch singleton of scoped. Dit voorbeeld gebruikt een gewone bind() en slaat geen verzamelresultaten op in het bundelende object. Vooral wanneer je een met giveTagged() geresolvede array vasthoudt in een singleton-bundelservice, kunnen ook scoped services in die array vastgehouden blijven. Het resetten van de scope vervangt geen referenties die al in een ander object zijn opgeslagen. Zie ook Levensduur van packageservices voor ontwerpen die in Octane of queue workers worden gebruikt.

Lazy resolving en omzetting naar een array testen

In een omgeving met de Composer-autoload van het package en PHPUnit kun je de volgende test toevoegen aan tests/ReportCollectorTest.php. Deze roept de registratie van de provider aan op alleen de container en controleert hoe vaak er iets wordt aangemaakt. Tests die ook de integratie in de applicatie en automatische detectie omvatten, vul je aan met Orchestra Testbench.
Controleer bij het updaten van het package daarnaast de volgende combinaties.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 26 oktober 2026