In Joomla 4 there are new files in the components, like:
- services/provider.php
- Extension/XxxComponent.php
- Service/HTML/AdministratorService
Are there available documentation about how to implement this new files for Joomla 4 components?
services/provider.php
should return a service provider which is used to register services with the service container and set up initial dependencies. This is the only file with a hardcoded filename and, technically, the only mandatory file required for creating a functioning modern component. The component's service provider should provide an implementation of Joomla\CMS\Extension\ComponentInterface
, which in turn must be capable of creating an instance of Joomla\CMS\Dispatcher\DispatcherInterface
. If an implementation of component is not provided, Joomla will fallback to a legacy component. Although using a provider file and a legacy component is an unlikely combination. That said, this should be consider the bare minimum for a service provider:
use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Dispatcher\DispatcherInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->set(
ComponentInterface::class,
static fn () => new class implements ComponentInterface
{
public function getDispatcher(CMSApplicationInterface $app): DispatcherInterface
{
return new class implements DispatcherInterface
{
public function dispatch()
{
// Component's execution logic goes here.
}
};
}
}
);
}
};
Of course, you may want to use concrete classes instead of anonymous ones. So let's move the component class to a separate file (e.g. Extension/XxxComponent.php
). Note that class naming convention is not important. You'll only refer to the class in your service provider.
namespace Acme\Component\Barebone\Administrator;
use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Dispatcher\DispatcherInterface;
use Joomla\CMS\Extension\ComponentInterface;
class BareboneComponent implements ComponentInterface
{
public function getDispatcher(CMSApplicationInterface $app): DispatcherInterface
{
// Create and return a dispatcher instance here.
}
}
Update the provider accordingly:
use Acme\Component\Barebone\Administrator\BareboneComponent;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->set(
ComponentInterface::class,
static fn () => new BareboneComponent
);
}
};
The component class has two main purposes:
For example, if you wanted to support SEF URLs in your component, it would need to implement Joomla\CMS\Component\Router\RouterServiceInterface
. Suppose you used the provided trait and router factory service. Then the component class would look like this:
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\CMS\Component\Router\RouterServiceTrait;
class BareboneComponent implements RouterServiceInterface
{
use RouterServiceTrait;
}
Internally RouterServiceTrait
requires a router factory so it needs to be provided when creating the component instance:
use Acme\Component\Barebone\Administrator\BareboneComponent;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\RouterFactory;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->registerServiceProvider(new RouterFactory('\\Acme\\Component\\Barebone'));
$container->set(
ComponentInterface::class,
static function (Container $container)
{
$component = new BareboneComponent;
$component->setRouterFactory($container->get(RouterFactoryInterface::class));
return $component;
}
);
}
};
One thing to consider is that services in the container can be overridden. If you don't need or want this, you can use $container->protect()
method (or pass fourth argument to $container->set()
) when registering services. Or even not use the container at all. So, for example, if you want to use a custom router factory and don't want it overridden you could this:
use Acme\Component\Barebone\Administrator\BareboneComponent;
use Acme\Component\Barebone\Site\Router\MyRouterFactory;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->set(
ComponentInterface::class,
static function ()
{
$component = new BareboneComponent;
$component->setRouterFactory(new MyRouterFactory);
return $component;
}
);
}
};
An excellent resource for Joomla 4 extension development is "Joomla 4.x Tutorial - Extension Development - The Way to Joomla 4 Extensions" written by Astrid Günther: https://blog.astrid-guenther.de/en/der-weg-zu-joomla4-erweiterungen/
She explains the concept of service providers here: https://blog.astrid-guenther.de/en/die-erste-ansicht-im-backend/
The boiler plate code that Astrid uses contains services/provider.php
: https://github.com/astridx/boilerplate/blob/t43/src/administrator/components/com_foos/services/provider.php
You can look up all the classes and what they need from the Joomla Api namespaces. The api is for both Joomla 3 and Joomla 4. The Api really helps out with finding out what is needed in a class, it shows all methods available for each class, what classes they inherit or implement.
https://api.joomla.org/cms-4/namespaces/joomla.html {If the link fails, you can just google joomla api.}