Work In Progress

This book is currently work in progress. Some sections are not yet written. Thank you for your understanding!

Console plugins

The Joomla CLI Application needs to somehow know about our custom CLI command classes. The way Joomla decided to implement this is with plugins. This makes perfect sense! The “One True Joomla Way” for implementing extensible features is with plugins.

We need to create a new plugin in the console group which will register our commands to the Joomla CLI application. This plugin must follow the Joomla 4 conventions[5] as it will be handling an event.

The service provider

Joomla 4 plugins do need a service provider. We are going to use the service provider to also get ahold of the MVCFactory object of our component, pass it to the plugin object which can then pass it to our command class.

defined('_JEXEC') || die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Console\ATS\Extension\ATS;

return new class implements ServiceProviderInterface {
  public function register(Container $container)
  {
    $container->registerServiceProvider(new MVCFactory('Acme\\Component\\Example'));

    $container->set(
      PluginInterface::class,
      function (Container $container) {
        $config     = (array) PluginHelper::getPlugin('console', 'example');
        $subject    = $container->get(DispatcherInterface::class);
        $mvcFactory = $container->get(MVCFactoryInterface::class);
        $plugin     = new Example($subject, $config)

        $plugin->setMVCFactory($mvcFactory);

        return $plugin;
      }
    );
  }
};

The plugin class

The plugin class only listens to one event, the \Joomla\Application\ApplicationEvents::BEFORE_EXECUTE one. This event is an \Joomla\Application\Event\ApplicationEvent which is raised by the Joomla CLI Application before it tries to execute the user's instructions.

To make things easier, and our example reusable in the real world with minimal modifications, we have a private static variable which lists the class names of the command classes to register. The registerCLICommands method iterates through them, creates a command object and adds it to the CLI application.

<?php
namespace Joomla\Plugin\Console\Example\Extension;

defined('_JEXEC') or die;

use Acme\Component\Example\Administrator\CliCommand\ItemsList;
use Joomla\Application\ApplicationEvents;
use Joomla\Application\Event\ApplicationEvent;
use Joomla\CMS\MVC\Factory\MVCFactoryAwareTrait;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Throwable;

class Example extends CMSPlugin implements SubscriberInterface
{
  use MVCFactoryAwareTrait;

  private static $commands = [
    ItemsList::class,
  ];

  protected $autoloadLanguage = true;

  public static function getSubscribedEvents(): array
  {
    return [
      ApplicationEvents::BEFORE_EXECUTE => 'registerCLICommands',
    ];
  }

  public function registerCLICommands(ApplicationEvent $event)
  {
    foreach (self::$commands as $commandFQN)
    {
      try
      {
        if (!class_exists($commandFQN))
        {
          continue;
        }

        $command = new $commandFQN();

        if (method_exists($command, 'setMVCFactory'))
        {
          $command->setMVCFactory($this->getMVCFactory());
        }

        $this->getApplication()->addCommand($command);
      }
      catch (Throwable $e)
      {
        continue;
      }
    }
  }
}

As you can see, after creating the command object we push the MVCFactory into it — if it supports that feature, i.e. it is using the MVCFactoryAwareTrait itself.

We could do the same for the database object. We'll let you figure it out. For the solution, you can read the footnote[6].

Remember to not just install this plugin, but also publish it. You can publish the plugin automatically in the install section of your package's installation script. Remember, that code only runs on a new installation. If you want to enable this plugin also on updates you will need to do the same in the update section as well. Yes, it's a bit of a kludge but in practice it works very well.



[5] We'll learn more about creating plugins in the Plugins section of this book.

[6] At the top of the plugin object define the $db property to let Joomla push the database object into the plugin object.

protected $db;

In the registerCLICommand method, right after pushing the MVCFactory, push the database object:

if (method_exists($command, 'setDbo'))
{
  $command->setDbo($this->db);
}