Dependency injection in Joomla: de DI-container uitgelegd
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 boven2. 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
DatabaseInterfacein - 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 boven3. 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
| Methode | Betekenis |
|---|---|
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.
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:
| Provider | Registreert |
|---|---|
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 boven5. 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), , geregistreerd met een gewone
(array) PluginHelper::getPlugin(...))set(). Vanaf Joomla 6.1 neemt de constructor alleen de pluginconfiguratie, en plugins die de dispatcher nodig hebben, krijgen die via setDispatcher().
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-provider | Geeft 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 boven7. Services op de juiste manier gebruiken
7.1 De voorkeursladder
- Constructor-injectie: declareer de afhankelijkheid; de factory of je provider geeft hem aan. De standaard voor je eigen klassen.
- Framework-setters en -traits: Joomla's basisklassen bieden
DatabaseAwareTrait($this->getDatabase()),getApplication(),getUserFactory()in plugins - gevoed door de provider, gebruikt zonder plichtplegingen. - 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).
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.
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.
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 boven11. 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.
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.
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 boven14. 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.phpper 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; behandelFactory::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.
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 boven16. 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

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












