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 packageacme/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.
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.
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 insrc/ReportsServiceProvider.php de standaardimplementaties en de bundelende service. De factory van de bundelende service geeft de opgehaalde iterable ongewijzigd door.
DailyReport die Report implementeert en voegen die in register() van hun eigen provider aan dezelfde tag toe.
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 geefttagged() 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.
$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.
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 gewonebind() 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 aantests/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.
Geraadpleegde primaire bronnen
- Officiële Laravel-documentatie: Tagging
- Officiële Laravel-documentatie: Binding primitives en Variadic tag dependencies
- Officiële Laravel-documentatie: Service providers
- Officiële Laravel-documentatie: Packageontwikkeling
- Container: tag() en tagged()
- ContextualBindingBuilder: giveTagged()
- RewindableGenerator: opnieuw opsommen en aantal
- Tag-attribuut: resolve()
- Officiële test: ContainerTaggingTest
- Officiële test: ContextualBindingTest