Dependency Injection in Joomla 4/it
From Joomla! Documentation
Joomla 4 introduce la pratica dei contenitori a iniezione di dipendenza (DIC) in Joomla. Questo articolo ha lo scopo di spiegare perché li stiamo introducendo e come usarli in Joomla.
Introduzione
Le DIC sono state a lungo nell'ecosistema PHP per supportare gli obiettivi dell'iniezione di dipendenze. Per esempio, Symfony ha introdotto il concetto nel 2009.
Ci sono una serie di ragioni per cui è giunto il momento giusto per introdurre questi elementi in Joomla 4:
- Testing - uno dei temi di Joomla 3 è stato il rilascio di buggy. Abbiamo bisogno di essere in grado di testare classi e componenti in modo più semplice. Dependency injection permette un'iniezione molto più semplice delle classi Mock, permettendoci di ridurre la quantità di bug.
- Reduce the amount of magic in Joomla - Joomla has a large number of magic files you need to guess the names of. This increases the amount of time people new to Joomla have to take researching these conventions. Exposing a concrete class in extensions allows us to easily test extensions compatibility with other extensions (for example categories and associations).
The global container
The Global Dependency injection is very loosely a replacement for the JFactory class. However it shouldn't be mistaken for a direct replacement.
As a majority rule you shouldn't directly replace JFactory calls to the container. The majority of the time you should either get the object out the application OR use dependency injection into your class.
So for example in your Controllers in the CMS instead of substituting \Joomla\CMS\Factory::getDocument()
consider using $this->app->getDocument()
. This uses the injected application and therefore allows for easier testing.
Creating an object in a container
To place something in the Global DIC the most simple way is to pass in an anonymous function. An example for a logger is below
// Assuming we have an instance of a Joomla Container
$container->share(
LoggerInterface::class,
function (Container $container)
{
return \Joomla\CMS\Log\Log::createDelegatedLogger();
},
true
);
The share function takes two compulsory parameters and an optional third parameter.
- A name for the service is nearly always the class name that you're creating
- An anonymous function takes a single parameter - the container instance (this allows you to retrieve any dependencies out the container). The return is the service that you want to place into the container
- (optional) This boolean controls whether the service is protected (i.e. whether anyone else is allowed to override it in the container). Generally for Joomla core services, such as session objects, this is true.
Let's now look at a more complicated example:
$container->alias('AmazingApiRouter', Joomla\CMS\Router\ApiRouter::class)
->share(
\Joomla\CMS\Router\ApiRouter::class,
function (Container $container)
{
return new \Joomla\CMS\Router\ApiRouter($container->get(\Joomla\CMS\Application\ApiApplication::class));
},
true
);
Here you can see we've done two extra things - we've started using dependencies (the api router gets the api application out the container) and we've also created an alias for the ApiRouter. That means whilst the container recognises that if it needs to build an ApiRouter instance it can do that. But in our code to keep things simple we can also run Factory::getContainer()->get('AmazingApiRouter')
to retrieve our router.
Whilst in Joomla our providers can look more complicated than this because the logic to create objects inside the anonymous function is more complicated - all of them follow this base idea.
Providers
Providers in Joomla are a way of registering a dependency into a service container. To do this create a class that implements Joomla\DI\ServiceProviderInterface
. This gives you a register method which contains the container. You can then use the share method again to add any number of objects into the container. You can then register this into the container with the `\Joomla\DI\Container::registerServiceProvider` method in the container. You can see where we register all the core service providers here in the \Joomla\CMS\Factory::createContainer method
Component Containers
Every component also has its own container (which is located in the administrator section of Joomla). However this container is not exposed. It's just there to get the system dependencies and allow a class to represent your extension. This class is the Extension class and at a minimum must implement the relevant extensions type interface. For example a component must implement the \Joomla\CMS\Extension\ComponentInterface
(found on here on GitHub).
For full information on implementing this in your extension, we recommend reading Developing an MVC Component
Using a component container in another extension
Si può facilmente afferrare il contenitore di un'altra estensione attraverso l'oggetto applicazione CMSA. Per esempio
Factory::getApplication()->bootComponent('com_content')->getMVCFactory()->createModel('Articles', 'Site');
Otterrà il contenitore com_content, ottenere la fabbrica MVC e ottenere il modello di articoli dal frontend di Joomla. E questo funzionerà in qualsiasi estensione in frontend, backend o API di Joomla (a differenza del vecchio metodo LegacyModel::getInstance() method)
Leggi tutto
C'è un grande esempio nei documenti Joomla Framework docs sul perché Dependency Injection è buono per la vostra applicazione e come l'aiuto di DIC strutturare esso. Leggilo qui