As discussed in the previous section, the service provider file is
mandatory for Joomla 4 MVC components and lives in the component's
services
folder and always named
provider.php
.
The absolutely minimal minimal service provider file looks like the following:
<?php defined('_JEXEC') || die; use Acme\Component\Example\Administrator\Extension\ExampleComponent; use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface; use Joomla\CMS\Extension\ComponentInterface; use Joomla\CMS\Extension\Service\Provider\MVCFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; return new class implements ServiceProviderInterface { public function register(Container $container) { // PART 1: Register service providers to the component's container $container->registerServiceProvider(new MVCFactory('Acme\\Example')); // PART 2: Instantiate and set up your extension object $container->set( ComponentInterface::class, function (Container $container) { /** * PART 2.a: Instantiate the extension object * * If you do not have a custom extension class use \Joomla\CMS\Extension\MVCComponent instead. */ $component = new \Acme\Component\Example\Administrator\Extension\ExampleComponent( $container->get(ComponentDispatcherFactoryInterface::class) ); // PART 2.b: Set up the extension object $component->setMVCFactory($container->get(MVCFactoryInterface::class)); // PART 2.c: return the extension object return $component; } ); } };
As you can see the service provider returns an anonymous
PHP class which implements the
Joomla\DI\ServiceProviderInterface
. That's the standard way
to extend Joomla's DIC. Remember, as we learned in the lifetime of a component, Joomla creates
a copy of its DIC and uses it as our component's own
DIC. The service providers we set up in our component stay with our
component, they do not leak out to the global application scope (the
global Joomla DIC you get through the
Joomla\CMS\Factory::getContainer()
static method).
Part 1 of that file lets us register service providers to our
component's DIC. At the bare minimum we need to register a
Joomla\CMS\Extension\Service\Provider\MVCFactory
provider.
This returns an object implementing
Joomla\CMS\MVC\Factory\MVCFactoryInterface
which is used by
our component's code (and Joomla's core code) to get the MVC objects of
our component (Controllers, Models, Views, Tables). If we want to use more
core Joomla features with our component we may have to register more
service providers as we'll see in the Extension class section and the rest of the
sections of this chapter.
Part 2 sets up a service provider returning an object implementing
the Joomla\CMS\Extension\ComponentInterface
, i.e. our
component's extension object.
Part 2.a is where we create an instance of our extension class. If you have a dead simple
component which does not use a custom extension class you can use Joomla's
built-in \Joomla\CMS\Extension\MVCComponent
class instead.
Most components will need a custom extension class so we instantiate it
here.
Part 2.b is where we set up the extension object we created in Part 2.a. Remember when I told you that Joomla's DIC is not really a Dependency Injection Container but more of a Service Locator? Because of that fact, when we create the extension object it does not know of any service objects it needs to use. Therefore we have to push them manually.
Sidebar: In my opinion, the three biggest shortcomings of the Joomla 4 MVC are the lack of convention over configuration, the lack of abstracted configuration and the lack of a real Dependency Injection container. These three shortcomings are, respectively, why we need to have a service provider file, why we need Part 1 and Part 2.b and why we need Part 2.a. Most other PHP frameworks have already solved these shortcomings. This means that any complaints that Joomla is moving to a very abstracted approach just to compete with other PHP frameworks are entirely unfounded; Joomla's architecture is still a good 10 or so years behind the PHP state of the art. This is not a bad thing! Too much abstraction may be a good thing for experienced, hardcore developers like yours truly but not necessarily the easiest thing to get your head around if you're a newcomer to Joomla or PHP in general.
Finally, we have Part 3 where we return the configured extension object, necessary for our custom service provider to actually work.
Warning | |
---|---|
You may be tempted to think that the service provider is only ever called when Joomla is about to render your component. This is not the case. Joomla will load a component's service provider any time it needs to interact with any of its services including the Router, or because another extension (such as a plugin, or module) wants to get access to your component's Models or some other service or object. Just because your service provider's code is running DOES NOT mean you are about to render an HTML page with your component, it DOES NOT mean that you necessarily have access to the JPATH_COMPONENT and JPATH_COMPONENT_ADMINISTRATOR constants and absolutely DOES NOT mean that you should ever, ever, try to push CSS, JavaScript or run any other code which assumes that Joomla is in an HTML output state. If you have such code (which is generally a bad idea) you should
run it in your component's Dispatcher and only after checking that
you are indeed running in an HTML application (check the type of the
object returned from calling
|