4

I'm trying to add an ajax call to Article Latest module.

I added the code below to /modules/mod_articles_latest/src/Helper/ArticlesLatestHelper.php

public static function getAjax()
    {
        $data = ['test'];
        return $data;
    }

Then manually added /modules/mod_articles_latest/helper.php and added these code :

defined('_JEXEC') or die;
require_once JPATH_SITE . '/modules/mod_articles_latest/src/Helper/ArticlesLatestHelper.php';

Then I test it by call : http://localhost/index.php?option=com_ajax&module=articles_latest&method=get&format=json

Those steps above are working properly in Joomla 3.x but I got this unsuccessful message in Joomla 4 :

{"success":false,"message":"Method getAjax does not exist.","messages":null,"data":null}

Can anybody tell me what I'm missing here?

2
  • What is the name of your class in helper.php?
    – Zollie
    Oct 4, 2021 at 6:22
  • @Zollie It's ArticlesLatestHelper
    – webchun
    Oct 4, 2021 at 6:26

2 Answers 2

3
+50

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

1
  • 1
    Great, clean answer, Sharky, as always. It is about how to build a module correctly for J4, anybody can learn from this a lot. The com_ajax problem however is still there - as you also mentioned that - it is looking for a helper class name like $class = 'Mod' . ucfirst($module) . 'Helper';. I think this must be corrected in the component too, at least with an added definition, to keep it backward compatible of course.
    – Zollie
    Oct 10, 2021 at 9:22
1

I had two extra minutes to test this according to my previous comments under your question.

I created a helper.php inside the ArticlesLatest Module (root of that module) and created a class: class ModArticlesLatestHelper

This looks simply:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  mod_articles_latest
 *
 * @copyright   (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

/**
 * added helper.php for mod_articles_latest
 *
 * @since  1.6
 */
class ModArticlesLatestHelper
{
    public static function getAjax()
    {
        $data = ['test'];
        return $data;
    }
}

And I just checked the response on the URL you provided:

http://localhost/index.php?option=com_ajax&module=articles_latest&method=get&format=json

And I just got a success response with the 'Test':

{"success":true,"message":null,"messages":null,"data":["test"]}

So, it looks like that the problem was your inadequate class name inside your added helper.php file.

5
  • Hmm so ajax still doesn't work with namespaced module in Joomla 4? Indeed if I comment out namespace Joomla\Module\ArticlesLatest\Site\Helper; in file /modules/mod_articles_latest/src/Helper/ArticlesLatestHelper.php and change the class name to ModArticlesLatestHelper I will get a success message.
    – webchun
    Oct 4, 2021 at 11:35
  • @webchun - It looks like com_ajax still looks for Mod class prefix in helper class name. I know, others are also embarrassed as I’ve seen it.
    – Zollie
    Oct 4, 2021 at 12:25
  • I think an additional if block at $class declaration inside com_ajax could solve this problem, but I did not think too much about this yet… or I also do not see something here… looks a bit buggy…
    – Zollie
    Oct 4, 2021 at 12:57
  • I used to post this issue on github.com/joomla/joomla-cms/issues/32136 and github.com/joomla/joomla-cms/issues/32455 . Both of those ticket's statuses are closed but seems the issue still persist?
    – webchun
    Oct 4, 2021 at 13:20
  • @webchun- I do not see that they solved this problem, unfortunately at the moment I do not have more time to look into this more deeply.
    – Zollie
    Oct 4, 2021 at 13:42

Your Answer

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

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