1

com_mycomponent/services/provider.php:

return new class implements ServiceProviderInterface {
    public function register(Container $container): void {
        $container->registerServiceProvider(new MVCFactory('\\MyCompany\\Component\\MyComponent'));
        $container->registerServiceProvider(new ComponentDispatcherFactory('\\MyCompany\\Component\\MyComponent'));
        $container->set(
            ComponentInterface::class,
            function (Container $container) {
                $component = new MyComponent($container->get(ComponentDispatcherFactoryInterface::class));
                $component->setMVCFactory($container->get(MVCFactoryInterface::class));
                $container->set(
                    "ANumber",
                    fn() => 1,
                );
                return $component;
            }
        );
    }
};

com_mycomponent/src/Controller/MyController.php:

class MyController extends BaseController {
    private int $myNumber;

    public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) {
        parent::__construct($config, $factory, $app, $input);
        $this->myNumber = $container->get("ANumber");
    }
    
    public function display($cachable = false, $urlparams = []) {
        // do something with $this->myNumber
        return parent::display($cachable, $urlparams);
    }
    
}

I am migrating to Joomla 4/5 and have a question after reading the documentation. Is it possible to access the dependencies that I registered in my provider.php file from a controller?

2 Answers 2

1

If I understand your question correctly you are trying to access your com_mycomponent from the controller of another component.

The first step is to boot your component from 'a controller'

$fred = Factory::getApplication()->bootComponent('com_mycomponent');

From there you can get the Controller of your component as shown below.

$barney = $fred->getMVCFactory()->createController('My', 'Site', , [], $this->app, $this->input);

The params of the createController() function are

 * @param   string                   $name    The name of the controller
 * @param   string                   $prefix  The controller prefix
 * @param   array                    $config  The configuration array for the controller
 * @param   CMSApplicationInterface  $app     The app
 * @param   Input                    $input   The input
   public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input);

It is a similar process to access the other MVC elements with a createModel() or createView()

You can find more details by looking in ..\libraries\src\MVC\Factory\MVCFactory.php and if you do a search of the core Joomla code for bootComponent you will find other examples to learn from.

2
  • Hey @Irata, I think you misunderstood my question or I did not explain it correctly. I am injecting my dependencies (in this example just a simple number) that I need for my component via a ServiceProviderInterface located in com_mycomponent/services/provider.php. My question is how I can access my dependency or the container in a controller that I own located e.g. com_mycomponent/src/Controller/MyController.php. I have searched some resources [dionysopoulos.me/book/com-extension-dic-proxy.html] but I don't like this approach. Looking for alternatives.
    – Exprove
    Commented Jun 18 at 9:41
  • My misunderstanding of your requirement. I was working from the idea you wanted to access myComponent from 'a controller' in another component.
    – Irata
    Commented Jun 19 at 3:02
1

This is a very complex question that spans beyond Joomla. Short answer is you can inject anything anywhere you want. But you will need to write your own code to do that. Joomla essentially only provides the DI container in this case. How you pass the services to your objects is up to you. It should be mentioned, though, that using DI container as service locator (i.e. injecting the container itself everywhere) is an anti-pattern. Speaking strictly about the core MVC layer, it's not really designed for DI. While the default MVC factory implementation does perform some DI, it does so in a really bad way:

  1. Manually instantiates classes, assuming the same constructor for all classes.
  2. Hardcoded list of dependencies is injected using setters.
  3. Some MVC classes expect dependencies passed inside configuration arrays.
  4. Contextual controller dependencies (i.e. application and input objects) are passed to the constructor via createController() method.

If you don't mind using setter/property injection, the simplest option is to extend Joomla\CMS\MVC\Factory\MVCFactory, and override the create*() methods:

public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input)
{
    if ($controller = parent::createController($name, $prefix, $config, $app, $input))
    {
        $controller->foo = $this->container->get('foo');
        $controller->setBar($this->container->get('bar'));
    }

    return $controller;
}

It's by no means a good practice but it's essentially how the core currently works. If you want proper constructor DI, you can try writing your own Joomla\CMS\VMC\Factory\MVCFactoryInterface implementation. But because the container does not support passing custom arguments during autowiring, you'd have to write custom autowiring code too or still mix autowiring with setter/property injection (e.g. to inject the correct app/input instances into controller):

public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input)
{
    // Some method to build controller class name.
    $className = $this->getClassName($name, $prefix, 'Controller');

    // Autowire the controller
    if ($controller = $this->container->buildObject($className))
    {
        // Inject contextual dependencies.
        $controller->setConfig($config);
        $controller->setApplication($app);
        $controller->setInput($input);
    }

    return $controller;
}

Ant then there's the extreme option of dropping the core MVC interface and introducing your own stack. This is obviously more difficult upfront but avoids Joomla's idiosyncrasies and, in theory, should make maintenance easier since Joomla upgrades are less likely to break it.

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.