Terug naar hoofdinhoud
Dependency injection in Joomla: de DI-container uitgelegd
Op deze pagina
# Topics

Dependency injection in Joomla: de DI-container uitgelegd

15 augustus 2026

Ergens tussen Joomla 3 en Joomla 4 veranderde extensiecode van vorm. De vertrouwde JFactory::getDbo()-aanroepen verdwenen, elke extensie kreeg een bestand services/provider.php, en tutorials begonnen over containers en interfaces. Achter dat alles staat een idee - dependency injection - en het is eenvoudiger dan zijn reputatie doet vermoeden.

Dit artikel legt dependency injection (DI) in Joomla vanaf de grond uit. Het behandelt het idee in gewone taal voor iedereen die wil begrijpen waarom moderne Joomla-extensies eruitzien zoals ze eruitzien, de container en service providers voor ontwikkelaars die de sprong maken vanaf Joomla 3-patronen, en de praktische recepten - services registreren, gebruiken, verwisselen en testen - voor het dagelijkse extensiewerk. Het bouwt voort op het artikel over de bestandsstructuur (dat services/provider.php lokaliseerde) en het authenticatie-artikel (waarvan we de plugins nu eindelijk openmaken).

Dependency injection betekent dat een klasse vraagt om wat ze nodig heeft in plaats van het te grijpen. Al het andere is machinerie rond die ene zin.

Het doel is eenvoudig: de moderne Joomla-bedrading vanzelfsprekend laten voelen, zodat je DI-gebaseerde extensies met vertrouwen kunt lezen, schrijven en debuggen.

Geschreven voor Joomla 6.1. Notities markeren waar Joomla 4 en 5 afwijken.

1. De basis

1.1 Het probleem: klassen die grijpen

Ouderwetse Joomla-code bouwde of haalde haar eigen gereedschap: een model riep JFactory::getDbo() aan voor de database, JFactory::getUser() voor de gebruiker, waar het maar nodig was. Handig - en star. De klasse hangt stiekem aan die globale aanroepen: je kunt haar geen andere database geven (zeg, een testdatabase), je ziet van buiten niet wat ze nodig heeft, en wanneer de global van vorm verandert, breekt elke klasse die greep tegelijk. De migratie van Joomla 3 naar 4 maakte die kosten zichtbaar voor een heel ecosysteem.

1.2 Het idee: klassen die vragen

Dependency injection draait het om: een klasse verklaart wat ze nodig heeft (in haar constructor of een setter), en iemand buiten geeft het aan. De klasse weet niet meer waar de database vandaan komt en het kan haar niet schelen - ze ontvangt er gewoon een. Denk aan een kok die ingredienten op een lijst zet en de keuken laat leveren, in plaats van midden in het recept de winkel in te rennen.

1.3 Waarom niet-ontwikkelaars dit moet interesseren

Zelfs als je nooit PHP schrijft, verklaart DI dingen die je ziet: waarom Joomla 4+-extensies zo anders gestructureerd zijn dan Joomla 3-extensies, waarom goed gebouwde moderne extensies Joomla-upgrades beter overleven (ze hangen aan stabiele interfaces, niet aan interne details), en waarom "deze extensie gebruikt nog verouderde Factory-aanroepen" in een beoordeling een echte waarschuwing is - die statische aanroepen zijn verouderd sinds Joomla 4.3 en staan gepland voor verwijdering in Joomla 7. DI is de reden dat het moderne extensie-ecosysteem stabieler is dan het oude.

Naar boven

2. Dependency injection in een voorbeeld

2.1 Voor: grijpen

class ArticleCounter
{
    public function count(): int
    {
        $db = \Joomla\CMS\Factory::getDbo();   // grijpt een global (verouderd)

        return (int) $db->setQuery(
            'SELECT COUNT(*) FROM #__content'
        )->loadResult();
    }
}

2.2 Na: vragen

use Joomla\Database\DatabaseInterface;

class ArticleCounter
{
    public function __construct(private DatabaseInterface $db)
    {
    }

    public function count(): int
    {
        return (int) $this->db->setQuery(
            'SELECT COUNT(*) FROM #__content'
        )->loadResult();
    }
}

2.3 Wat er net verbeterde

  • Eerlijkheid: de constructor is de ingredientenlijst van de klasse - afhankelijkheden zijn zichtbaar, niet verstopt in methode-lichamen.
  • Flexibiliteit: geef er een willekeurige DatabaseInterface in - de live database, een andere verbinding, een test-dubbelganger.
  • Stabiliteit: de klasse hangt aan een interface die Joomla belooft te behouden, niet aan een statische aanroep die op de nominatie staat voor verwijdering.

Het patroon heeft een formele naam: het Dependency Inversion Principle, de "D" in SOLID - hang aan abstracties, niet aan implementaties. Heb je het voorbeeld tot hier gevolgd, dan begrijp je het al.

De open vraag - wie geeft de database aan - is wat de rest van dit artikel beantwoordt.

Naar boven

3. De container

3.1 Een register van recepten

De dependency injection container (DIC) is de keuken uit de vergelijking: een register dat weet hoe elke service gebouwd wordt. Je registreert een recept (een closure) onder een naam - volgens afspraak de interfacenaam - en de container voert het recept uit wanneer iemand erom vraagt:

use Joomla\DI\Container;
use Joomla\Database\DatabaseInterface;

$container->share(
    DatabaseInterface::class,
    function (Container $container) {
        // bouw en retourneer de database-driver
    }
);

$db = $container->get(DatabaseInterface::class);

3.2 De API die ertoe doet

MethodeBetekenis
set(id, recept) Registreer een recept; een verse instantie per get().
share(id, recept) Registreer een recept; een keer gebouwd, elke keer dezelfde instantie - juist voor een databaseverbinding of de applicatie.
get(id) / has(id) Haal een service op / controleer of een recept bestaat.
alias(alias, id) Een tweede naam voor hetzelfde recept - zo blijven oude namen werken.
protect(id, recept) Zoals set, maar het recept kan niet overschreven worden.
extend(id, closure) Wikkel een bestaand recept in - decoreer een service zonder hem te vervangen.
lazy(klasse, recept) (6.1+) Stel de bouw uit tot de service voor het eerst gebruikt wordt. Echte lazy proxies vereisen PHP 8.4; op oudere PHP wordt het object meteen gebouwd.
createChild() Een child-container die alle recepten erft maar eigen kan toevoegen of overschrijven - onthoud deze voor sectie 5.

3.3 Waar de container woont

Joomla bouwt de container tijdens het opstarten en registreert daarbij de core-recepten, en stelt hem beschikbaar via Factory::getContainer(). (De bouwstap zelf, Factory::createContainer(), is protected - het is Joomla's eigen opstartwerk, geen API die je aanroept.) getContainer() roep je zelden en bewust aan - sectie 7 legt uit waarom overal naar de container grijpen het oude grijpprobleem alleen maar zou herhalen met extra stappen.

Naar boven

4. Service providers

4.1 Recepten in pakketten

Recepten een voor een registreren zou het opstartproces verdrinken in closures. Een service provider bundelt verwante recepten in een klasse met een methode:

use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;

class MyServiceProvider implements ServiceProviderInterface
{
    public function register(Container $container): void
    {
        // $container->share(...), $container->set(...), ...
    }
}

$container->registerServiceProvider(new MyServiceProvider());

4.2 De core-providers

Alles wat je door deze artikelserie heen gebruikt hebt, is precies zo bedraad. libraries/src/Service/Provider/ bevat de providers van de core - een greep:

ProviderRegistreert
Application De site-, administrator-, API- en console-applicaties - de vier deuren uit het bestandsstructuur-artikel.
Database De DatabaseInterface-driver, gebouwd vanuit configuration.php.
Session De sessie, met de handler die in de Algemene configuratie gekozen is.
User De UserFactoryInterface uit het authenticatie- en het ACL-artikel.
Mailer, Logger, Document, Router, Toolbar, … Elk subsysteem, een provider elk - zo'n twee dozijn in totaal.

Deze map lezen is de snelste manier om te leren "hoe krijg ik service X netjes": zoek zijn provider, kijk welk id hij registreert, vraag om die interface.

Naar boven

5. Hoe extensies opstarten

5.1 services/provider.php

Het bestandsstructuur-artikel lokaliseerde het; nu weten we wat het is: elke moderne extensie levert een services/provider.php die een anonieme service provider retourneert. Wanneer Joomla de extensie opstart, doet het iets elegants (geverifieerd in ExtensionManagerTrait): het maakt een child-container, draait jouw provider erin, en haalt de extensie-instantie eruit:

$container = $this->getContainer()->createChild();   // erft alle core-recepten
require $path . '/services/provider.php';            // jouw provider registreert erin
// Joomla haalt er dan ComponentInterface / ModuleInterface / PluginInterface uit

De child-container is waarom je extensie elke core-service kan zien maar nooit per ongeluk de bedrading van een andere extensie kan overschrijven: elke extensie start op in een eigen zandbak die van de core erft.

5.2 Een echte plugin-provider, regel voor regel

De plugin Authentication - Joomla uit het authenticatie-artikel start zo op (ingekort uit het echte bestand):

return new class () implements ServiceProviderInterface {
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            $container->lazy(Joomla::class, function (Container $container) {
                $plugin = new Joomla(
                    (array) PluginHelper::getPlugin('authentication', 'joomla')
                );
                $plugin->setApplication(Factory::getApplication());
                $plugin->setUserFactory($container->get(UserFactoryInterface::class));

                return $plugin;
            })
        );
    }
};

Elk onderdeel heeft nu een naam: het recept is geregistreerd onder PluginInterface::class (waar Joomla om vraagt), lazy() stelt de bouw uit tot de plugin echt nodig is, de configuratie van de plugin wordt via de constructor geinjecteerd, en haar afhankelijkheden - applicatie, user factory - worden uit de container geinjecteerd. Toen het authenticatie-artikel zei "de factory wordt geinjecteerd via de service provider van de plugin", is dit die zin, in code.

Op Joomla 4 en 5 ziet dezelfde provider er anders uit: lazy() bestaat daar nog niet, en de dispatcher is het eerste constructorargument van de plugin - new Joomla($container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin(...))
, geregistreerd met een gewone set(). Vanaf Joomla 6.1 neemt de constructor alleen de pluginconfiguratie, en plugins die de dispatcher nodig hebben, krijgen die via setDispatcher().

Naar boven

6. Binnenin een component-provider

6.1 com_content, ingekort

Componenten bedraden meer, maar met kant-en-klare bouwstenen. De echte provider van com_content:

$container->registerServiceProvider(new CategoryFactory('\\Joomla\\Component\\Content'));
$container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\Content'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Content'));
$container->registerServiceProvider(new RouterFactory('\\Joomla\\Component\\Content'));

$container->set(ComponentInterface::class, function (Container $container) {
    $component = new ContentComponent(
        $container->get(ComponentDispatcherFactoryInterface::class)
    );
    $component->setMVCFactory($container->get(MVCFactoryInterface::class));
    // ...category factory, router factory, HTML-registry...
    return $component;
});

6.2 De vier standaardfactories

Factory-providerGeeft de component
MVCFactory Bouwt zijn modellen, views en controllers op namespace - de bootComponent('com_content')->getMVCFactory() die je door deze serie heen zag, eindigt hier.
ComponentDispatcherFactory Bouwt de dispatcher die een verzoek door de component leidt.
RouterFactory Bouwt de SEF-router (de URL-machinerie uit het SEO-artikel).
CategoryFactory Categorie-afhandeling voor componenten die categorieen gebruiken.

Voor je eigen component is de provider grotendeels deze vier regels met jouw namespace - de container doet de rest. Eigen recepten voeg je alleen toe wanneer je eigen services hebt, en dat is precies sectie 8.

Naar boven

7. Services op de juiste manier gebruiken

7.1 De voorkeursladder

  1. Constructor-injectie: declareer de afhankelijkheid; de factory of je provider geeft hem aan. De standaard voor je eigen klassen.
  2. Framework-setters en -traits: Joomla's basisklassen bieden DatabaseAwareTrait ($this->getDatabase()), getApplication(), getUserFactory() in plugins - gevoed door de provider, gebruikt zonder plichtplegingen.
  3. Factory::getContainer() direct: alleen op grenzen waar injectie niet kan komen - een legacy-integratiepunt, een snel CLI-script. Binnen normale klassen is het het oude grijppatroon in een nieuwe jas (het service locator-antipatroon).

7.2 In de praktijk, in een model

use Joomla\Database\DatabaseInterface;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;

class MessagesModel extends BaseDatabaseModel
{
    public function getMessages(): array
    {
        $db = $this->getDatabase();   // geinjecteerd door de MVCFactory, niet gegrepen

        // query zoals in het ACL- en authenticatie-artikel: binden, niet plakken
    }
}

7.3 De migratie-vuistregel

Oude code moderniseren? Vervang elke Factory::getDbo() en Factory::getUser() binnen klassen door het geinjecteerde equivalent (DatabaseInterface, UserFactoryInterface). Dit zijn de verouderde snelkoppelingen: elke vervanging verplaatst de klasse van "breekt bij de verwijdering" naar "compileert tegen beloften".

Factory::getApplication() is een ander geval. Die methode draagt geen verouderingsmarkering, en binnen een servicerecept blijft ze de normale manier om bij de applicatie te komen - de core-plugin-providers doen precies dat. Binnen je eigen klassen geef je nog steeds de voorkeur aan de geinjecteerde applicatie (setApplication() vanuit de provider, getApplication() vanuit de aware-trait).

Naar boven

8. DI-recepten uit de praktijk

8.1 Registreer je eigen service

Een component met een export-helper die andere klassen nodig hebben? Registreer hem een keer in je provider:

$container->share(
    ExportService::class,
    fn (Container $c) => new ExportService($c->get(DatabaseInterface::class))
);

Modellen vragen dan om ExportService::class - een bouwrecept, geen dubbele bedrading.

8.2 Verwissel een implementatie

Code hangt aan ExportInterface::class; de provider beslist welke implementatie erachter zit. Ontwikkeling registreert een dummy-PDF-renderer, productie de echte, en geen enkele gebruikende klasse verandert - de beloning van vragen in plaats van grijpen.

8.3 Wikkel een SDK van derden in

Een externe API-client (betaling, CRM, maildienst) hoort als gedeelde service in de container: configuratie een keer gelezen, een instantie, en elke gebruiker krijgt hem geinjecteerd. Verandert de SDK, dan verandert een recept.

8.4 Decoreer een bestaande service

extend() wikkelt een recept in zonder het te vervangen - voeg logging toe rond een bestaande service, of wikkel een factory in caching - nuttig in integratiescenario's waar je noch de service noch zijn gebruikers bezit.

Naar boven

9. Onder de motorkap (ontwikkelaarsblik)

9.1 Wat een recept echt is

Intern bewaart de container closures met een sleutel per id, met twee vlaggen: shared (bewaar het eerste resultaat) en protected (weiger overschrijven). get() voert de closure uit - en geeft de container zelf mee, zodat recepten hun eigen afhankelijkheden kunnen ophalen - en alias() wijst simpelweg een tweede sleutel naar dezelfde regel. Geen magie: een kaart van namen naar bouwinstructies.

9.2 buildObject: bedrading via reflectie

Voor klassen zonder recept kan buildObject() een constructor via reflectie inspecteren en elke getypehinte parameter uit de container oplossen - handig voor gereedschap en tests, al geven core-extensies de voorkeur aan expliciete recepten: expliciete bedrading is leesbare bedrading.

9.3 De containerhierarchie tijdens runtime

core-container            (Factory::createContainer + core-providers)
├─ child: com_content    (zijn services/provider.php)
├─ child: mod_menu       (zijn provider)
└─ child: plg_auth_joomla (zijn provider)

Opzoekingen vallen door van child naar parent - een extensie ziet alles wat de core registreerde, terwijl haar eigen registraties in haar zandbak blijven. Klinkt dat patroon bekend: het is hetzelfde parent-terugval-idee als template-overerving in het template overrides-artikel, toegepast op services.

9.4 Waarom de oude Factory-snelkoppelingen op hun retour zijn

Joomla stelde historisch veel services beschikbaar via statische Factory::get*()-methoden. Tijdens de Joomla 4.x-cyclus zijn verschillende daarvan verouderd verklaard. Ze zijn er nog steeds: de markeringen noemden eerst Joomla 6 als verwijderrelease, die verwijdering werd uitgesteld, en in de broncode van Joomla 6.1 noemen dezelfde markeringen Joomla 7.

In de broncode van Joomla 6.1 lezen de markeringen @deprecated 4.3 will be removed in 7.0 (getMailer() verouderd sinds 4.4, createConfig() sinds 4.0). Let op wat die geschiedenis in de praktijk betekent: het backward-compatibility-beleid van Joomla staat de releaseleiding toe een verwijdering uit te stellen, en dat is voor deze methoden al een keer gebeurd. Behandel ze als technische schuld zonder gegarandeerde vervaldatum in plaats van als een vaste deadline - extensies die nog grijpen, breken zodra de verwijdering wel komt; extensies die vragen, merken de release niet. Bij het doorlichten van een extensie (de kwaliteitscontroles uit het beveiligingsartikel) is een snelle grep naar Factory::get binnen klassemethoden een opvallend eerlijk kwaliteitssignaal.

Naar boven

10. Testen: de beloning

10.1 De ontestbare versie

De "voor"-klasse uit sectie 2 is niet unit-testbaar: Factory::getDbo() eist een opgestarte Joomla en een echte database. Haar testen betekent heel Joomla testen.

10.2 De testbare versie

public function testCountReturnsDatabaseResult(): void
{
    $db = $this->createMock(DatabaseInterface::class);
    $db->method('loadResult')->willReturn('42');
    $db->method('setQuery')->willReturnSelf();

    $counter = new ArticleCounter($db);

    $this->assertSame(42, $counter->count());
}

Geen Joomla, geen database, milliseconden per test. Dit is geen luxe: het is het verschil tussen extensies waarvan de logica bij elke wijziging geverifieerd wordt en extensies waarvan de logica geverifieerd wordt doordat klanten bugs vinden. DI is wat de eerste soort mogelijk maakt.

Naar boven

11. DI en de Web Services API

De API-applicatie wordt gebouwd vanuit dezelfde container als al het andere - de Application-serviceprovider registreert alle vier de applicaties, en een API-verzoek start componenten op via hetzelfde child-container-mechanisme als een siteverzoek. Daarom kon het Web Services API-artikel zeggen "dezelfde modellen, dezelfde ACL, hetzelfde gedrag": het is letterlijk dezelfde bedrading, opgevraagd door een andere deur.

Voor extensie-ontwikkelaars betekent dit dat API-ondersteuning geen extra DI-werk kost: de code in api/components/ uit het bestandsstructuur-artikel gebruikt dezelfde services als je sitecode, op dezelfde manier geinjecteerd. Een provider, vier applicaties - de hele belofte van de architectuur in een zin. En het reikt verder dan het web: console-commando's worden als container-services geregistreerd, en scheduler-taak-plugins starten op via hetzelfde provider-mechanisme - de CLI-reddingskit uit het troubleshooting-artikel en de onderhoudstaken uit het artikel over geplande taken draaien op exact deze bedrading.

Naar boven

12. SEO en metadata

Dependency injection heeft geen direct SEO-oppervlak - geen uitvoer, geen URL's, geen metadata. De verbinding is indirect maar echt, en ze herhaalt het eerlijke kader van het authenticatie-artikel: architectuurkwaliteit wordt sitekwaliteit. Extensies gebouwd op geinjecteerde, interface-gebaseerde bedrading overleven Joomla-upgrades zonder nooddowntime (downtime is een SEO-gebeurtenis, zoals het troubleshooting-artikel liet zien), en hun testbaarheid betekent minder van de kapotte-uitvoer-bugs die crawlen en renderen stilletjes schaden.

Wanneer je extensies beoordeelt voor een project - de selectiekaders uit het beveiligings- en het architectuur-artikel - is moderne DI-structuur een van de goedkoopste kwaliteitssignalen om te controleren: open het pakket, zoek naar services/provider.php en namespaced src/-klassen, grep naar verouderde statische aanroepen. Vijf minuten die jaren upgradegedrag voorspellen.

Naar boven

13. Veelvoorkomende fouten en valkuilen

13.1 De container als globale grabbelton

Symptoom: Factory::getContainer()->get(...) verspreid door modellen en helpers.

Oplossing: dat is service location - het oude patroon met meer typwerk. Afhankelijkheden komen binnen via constructors en providers; de container raak je alleen op grenzen aan.

13.2 Joomla 3-tutorials kopieren

Symptoom: nieuwe code met JFactory::getDbo(), helper-bestanden en geen provider - uit een tutorial die zijn Joomla-versie nooit noemt.

Oplossing: de waarschuwing uit het bestandsstructuur-artikel geldt ook voor code: controleer datum en patronen. services/provider.php plus namespaced src/ markeert actueel materiaal.

13.3 De ontbrekende provider

Symptoom: een versgebouwde extensie installeert maar draait nooit - geen fout, niets.

Oplossing: zonder services/provider.php die ComponentInterface/ModuleInterface/PluginInterface registreert, heeft Joomla niets om op te starten. Het manifest moet de map services ook vermelden - de manifestregel uit het bestandsstructuur-artikel.

13.4 Alles gretig, niets gedeeld

Symptoom: zware objecten die bij elk verzoek gebouwd worden, of - het omgekeerde - een "verse" service die gedeelde toestand blijkt te zijn.

Oplossing: kies bewust: share() voor verbindingen en enkelvoudige instanties, set() voor wegwerpobjecten, lazy() zodat de bouw wacht tot het eerste gebruik - zoals de core-plugin-providers doen.

13.5 De concrete klasse typehinten

Symptoom: een constructor eist MysqliDriver in plaats van DatabaseInterface - en breekt op sites met een andere driver.

Oplossing: vraag om de interface; laat de container de implementatie kiezen. De interface is de belofte; de klasse is een detail.

13.6 Alleen door de volledige stack testen

Symptoom: de "tests" van een extensie hebben allemaal een live site nodig, dus niemand draait ze.

Oplossing: met injectie op zijn plek draaien unit-tests met mocks (sectie 10) overal in milliseconden - bewaar full-stack-tests voor de paar paden die ze echt nodig hebben.

13.7 De circulaire afhankelijkheid

Symptoom: het recept van service A heeft service B nodig, wiens recept service A nodig heeft - en de bouw jaagt zijn eigen staart na.

Oplossing: een cirkel is een architectuurboodschap, geen containerbeperking. Haal het deel dat beide services nodig hebben naar een derde service waar beide van afhangen, en de cirkel opent zich tot een keten.

Naar boven

14. Best practices

Als je maar een paar dingen uit dit artikel onthoudt, onthoud dan deze:

  • Klassen vragen via constructors; providers antwoorden via recepten; de container verbindt alleen de twee.
  • Hang aan interfaces (DatabaseInterface, UserFactoryInterface), nooit aan concrete drivers of verouderde statics.
  • Een services/provider.php per extensie: de vier standaardfactories voor componenten, lazy()-pluginbouw zoals de core het doet.
  • share() verbindingen, set() wegwerpers, protect() wat niet overschreven mag worden, extend() in plaats van vervangen.
  • Behandel Factory::getContainer() in klasselichamen als een code-geur; behandel Factory::getDbo() als een migratietaak, gepland op je eigen tempo.
  • Lees libraries/src/Service/Provider/ en de core-extensie-providers als je referentiebibliotheek - het zijn de patronen, geverifieerd.
  • Schrijf minstens een paar unit-tests met gemockte afhankelijkheden - ze zijn het bewijs dat je bedrading echte DI is en geen decoratie.
  • Let op constructorgrootte: een klasse die een half dozijn services eist, heeft te veel taken - splits haar voordat je haar bedraadt.
Naar boven

15. In het kort

IDEE         grijpen (JFactory::getDbo)  →  vragen (constructorparam)
             = Dependency Inversion Principle (de D in SOLID)
             verouderd sinds 4.3, verwijdering gepland in Joomla 7

CONTAINER    set(id, fn)      verse instantie per get()
             share(id, fn)    een keer bouwen, hergebruiken (db, app)
             get / has        ophalen / controleren
             alias(a, id)     tweede naam, zelfde recept
             protect / extend niet-overschrijfbaar / decoreren
             createChild()    ervende zandbak
             lazy(klasse, fn) bouw uitstellen (6.1+, PHP 8.4)

PROVIDERS    core: libraries/src/Service/Provider/  (~2 dozijn)
             extensie: services/provider.php retourneert
             anonieme ServiceProviderInterface

OPSTARTEN    core-container (Factory::createContainer)
             > per extensie createChild()
             > provider registreert Component|Module|PluginInterface
             > Joomla haalt de instantie eruit

COMPONENT    MVCFactory + ComponentDispatcherFactory
             + RouterFactory + CategoryFactory ('\\Vendor\\Naam')
             + ComponentInterface-closure

GEBRUIKEN    1. constructor-injectie       (je eigen klassen)
             2. traits/setters             (getDatabase, getUserFactory)
             3. Factory::getContainer()    (alleen op grenzen!)

TESTS        mock de interface, injecteer, assert - geen Joomla nodig

AUDIT        pakket openen: services/provider.php? src/?
             grep Factory::get binnen klassen = kwaliteitssignaal
Naar boven

16. Samenvatting

Dependency injection in Joomla is een idee en een kleine gereedschapskist eromheen:

  • Het idee: klassen vragen om afhankelijkheden via constructors in plaats van globals te grijpen - zichtbare behoeften, verwisselbare implementaties, testbare logica.
  • De container bewaart bouwrecepten onder interfacenamen: set, share, alias, protect, extend, lazy.
  • Service providers verpakken recepten: zo'n twee dozijn bedraden de core; elke extensie levert er een in services/provider.php.
  • Extensies starten op in child-containers - ze erven elke core-service, afgeschermd van elkaar, hetzelfde parent-terugval-patroon als template-overerving.
  • Gebruik heeft een ladder: eerst constructor-injectie, dan framework-traits, directe containertoegang alleen op grenzen.
  • De schuld is echt: de statische Factory-snelkoppelingen zijn verouderd en gemarkeerd voor verwijdering in Joomla 7 - een datum die al een keer is opgeschoven, dus migreer op je eigen tempo in plaats van op een deadline te wachten. Moderne bedrading is geen stijlvoorkeur, het is upgradeverzekering.

Zodra je het patroon ziet - vragen, registreren, injecteren - is de moderne Joomla-codebase niet meer intimiderend maar consistent: elke extensie start hetzelfde op, elke service wordt hetzelfde gevonden, en elke klasse vertelt je eerlijk wat ze nodig heeft.

En onderhoud je extensies die nog grijpen - je eigen, die van een voorganger, of een bedrijfskritische waarvan de ontwikkelaar vertrok - dan is de migratie naar geinjecteerde bedrading methodisch werk: inventariseer de statische aanroepen, introduceer de provider, injecteer een afhankelijkheid tegelijk, test onderweg. Op tijd gedaan is het een gepland project van een paar dagen; onder upgradedruk gedaan is het een noodgeval dat je in downtime meet - en het geteste, geinjecteerde resultaat is daarna elk jaar goedkoper te onderhouden.

Naar boven
Dependency injection in Joomla: de DI-container uitgelegd
Peter Martin
Peter Martin
Joomla Specialist

Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.

Gerelateerde artikelen