To use J4 style or custom naming conventions with AJAX you have to convert the module to new service-based format. At a minimum, service provider (services/provider.php
) needs to provide a module extension, a dispatcher factory and a helper factory:
defined('_JEXEC') or exit;
use Joomla\CMS\Extension\Service\Provider\HelperFactory;
use Joomla\CMS\Extension\Service\Provider\Module;
use Joomla\CMS\Extension\Service\Provider\ModuleDispatcherFactory;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->registerServiceProvider(new ModuleDispatcherFactory('\\Joomla\\Module\\ArticlesLatest'));
$container->registerServiceProvider(new HelperFactory('\\Joomla\\Module\\ArticlesLatest\\Site\\Helper'));
$container->registerServiceProvider(new Module);
}
};
The mod_articles_latest.php
entry file is replaced by a dispatcher class. Default dispatcher factory constructs the class this way:
[Base Namespace]\[Application Name]\Dispatcher\Dispatcher
So with the above example namespace the class would be this:
Joomla\Module\ArticlesLatest\Site\Dispatcher\Dispatcher
You can use custom naming conventions, as well as inject dependencies by creating your own implementation of Joomla\CMS\Dispatcher\ModuleDispatcherFactoryInterface
.
For the dispatcher you can extend Joomla\CMS\Dispatcher\AbstractModuleDispatcher
or create your own implementation of Joomla\CMS\Dispatcher\DispatcherInterface
. The dispatcher should fetch the data and render the module layout. If extending the abstract dispatcher, override getLayoutData()
method to add the data you need. Strangely, neither the module extension instance, nor the helper is available in the dispatcher. You have to boot the module through application:
namespace Joomla\Module\ArticlesLatest\Site\Dispatcher;
\defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
class Dispatcher extends AbstractModuleDispatcher
{
protected function getLayoutData()
{
// Gets some common data like application instance, module instance, module params, etc.
$data = parent::getLayoutData();
// Gets the module helper.
$helper = $this->app->bootModule('mod_articles_latest', 'Site')->getHelper('ArticlesLatestHelper');
$model = $this->app->bootComponent('com_content')->getMVCFactory()->createModel('Articles', 'Site', ['ignore_request' => true]);
// Data array will be extracted to variables, i.e. this will be accessible as $list in the layout.
$data['list'] = $helper->getList($data['params'], $model);
return $data;
}
}
Helper's class name is built simply from the namespace defined in helper factory plus the class name passed its getHelper()
method. In this example, the class would be Joomla\Module\ArticlesLatest\Site\Helper\ArticlesLatestHelper
. For custom naming conventions you could create your own implementation of Joomla\CMS\Helper\HelperFactoryInterface
. Except that not really because com_ajax
expects helper to be named in certain way. For this module com_ajax
would always pass ArticlesLatestHelper
to getHelper()
method. So your custom factory would still need to be aware of this format. This was a really bad decision by the developers which partly beats the purpose of having such factory in the first place... This would have been better off with AJAX-specific interface.
Finally, some changes might be needed for the helper. Helpers must be instantiable, meaning they must not be abstract and must have public constructors (not necessary if using custom factory, e.g. which could call a getInstance()
method on the helper). This is in contrast to J3 helpers which were entirely static (at least in core modules).
helper.php
?