4

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?

3 Answers 3

5

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:

  1. Creating a dispatcher instance based on application. The dispatcher is the entry point of a component. It maps the request to a controller and executes it. Additionally it may perform initial authorization checks and perform component bootstrapping tasks like loading language files.
  2. Implementing various features used across the CMS. This includes things like routing (SEF URLs), tagging, categories, custom fields and more. These features are added by implementing specific interfaces. Then when some part of CMS needs to determine if a component supports a feature, it will boot the component and check if a given interface is implemented.

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;
            }
        );
    }
};
3
5

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

1
  • I just finished working through Astrid's tutorial and while it is full featured there are some issues with things still not working. An example would be the choosing a Foo from a modal in backend menu item creation. Even in the completed project files it doesn't seem to be working.
    – Terry Carter
    Commented Jun 8, 2022 at 20:35
0

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.}

2
  • 1
    Generally speaking, Stack Exchange prefers that all content is statically printed within an answer. There are some fringe scenarios where this is not possible. As with Sharky's answer, please print as much of your insights as you can into your answer and merely support your guidance with a hyperlink. Link-only answers are typically seen as low-quality because if one day in the future, the url changes or is removed, then the entire answer is rendered useless. Pe7er's answer is also technically a link-only answer because its quality would be lost if the urls die. Commented Aug 2, 2022 at 22:35
  • Thank you for so much. I will follow the guideline.
    – DeesreX
    Commented Aug 3, 2022 at 7:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.