Step 10 Using AbstractModuleDispatcher
If you've looked at the code of any of the Joomla modules then you may have noticed that their Dispatcher code looks quite different from the code we've developed in this module tutorial. This is because they inherit from the AbstractModuleDispatcher class.
In this step we look at AbstractModuleDispatcher and how we can simplify our code using it.
The source code is available at mod_hello step 10.
AbstractModuleDispatcher Functionality
The code for \Joomla\CMS\Dispatcher\AbstractModuleDispatcher is in libraries/src/Dispatcher/AbstractModuleDispatcher.php, and this class in turn extends \Joomla\CMS\Dispatcher\Dispatcher in libraries/src/Dispatcher/Dispatcher.php
There are a few things to note about AbstractModuleDispatcher:
-
It gets passed into its constructor the module
$module
, application$app
and input$input
variables, and these are stored in instance variables. -
It has a function
loadLanguage
which loads the .ini language file for the module. -
It has a function
getLayoutData
which returns an array of 5 variables (module, app, input, params and template):
return [
'module' => $this->module,
'app' => $this->app,
'input' => $this->input,
'params' => new Registry($this->module->params),
'template' => $this->app->getTemplate(),
];
(These 5 variables were made available to the module code prior to Joomla 4, so are preserved for backward compatibility).
- It has a function
dispatch
which performs the following:- loads the language via
loadLanguage
- calls
getLayoutData
and assigns the array returned to a variable$displayData
- uses PHP
extract
to extract into variables the array elements of$displayData
- executes
require ModuleHelper::getLayoutPath($module->module, $params->get('layout', 'default'));
which runs the tmpl file.
- loads the language via
The last 2 operations are performed inside a separate function to create a clean scope.
Using AbstractModuleDispatcher
Because of the similarity of our mod_hello Dispatcher and AbstractModuleDispatcher we can use the latter to do the following for us:
- store the constructor parameters, making available the
$module
,$app
and$input
variables - load the language
- load the tmpl file
We just have to override getLayoutData
to add the 'hello' element into the $data
array, which will become the $hello
variable when the PHP extract
is executed.
Our update Dispatcher file has several lines deleted and becomes:
<?php
namespace My\Module\Hello\Site\Dispatcher;
\defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\Input\Input;
use Joomla\Registry\Registry;
use Joomla\CMS\Helper\HelperFactoryAwareInterface;
use Joomla\CMS\Helper\HelperFactoryAwareTrait;
class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareInterface
{
use HelperFactoryAwareTrait;
protected function getLayoutData(): array
{
$data = parent::getLayoutData();
$username = $this->getHelperFactory()->getHelper('HelloHelper')->getLoggedonUsername('Guest');
$data['hello'] = Text::_('MOD_HELLO_GREETING') . $username;
return $data;
}
}
In our tmpl file we just have to remember to access the application using $app
instead of $this->app
. The $hello
variable will be defined when the extract
is performed on the $data
array.
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$document = $app->getDocument();
$wa = $document->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('mod_hello');
$wa->useScript('mod_hello.add-suffix');
// Pass the suffix to add down to js
$document->addScriptOptions('mod_hello.vars', ['suffix' => '!']);
$h = $params->get('header', 'h4');
$greeting = "<{$h} class='mod_hello'>{$hello}</{$h}>";
Text::script('MOD_HELLO_AJAX_OK');
Text::script('JLIB_JS_AJAX_ERROR_OTHER');
?>
<?php echo $greeting; ?>
<div>
<p><?php echo Text::_('MOD_HELLO_NUSERS'); ?><span class="mod_hello_nusers"></span></p>
<button class="mod_hello_updateusers"><?php echo Text::_('MOD_HELLO_UPDATE_NUSERS'); ?></button>
</div>
A Pattern for Modules
Modules in general follow a simple pattern:
- get the data you want to display (putting any complex logic into a helper file), and,
- display the data in a section of HTML
If the module you want to develop follows this pattern then you can do the following:
- write a Dispatcher class like that of mod_hello
- customise the getLayoutData to set up the data array to suit your requirements. Remember that the elements of this array get extracted into PHP variables (which are available in the tmpl file)
- put any complex logic into functions within the helper file
- using the data you set up, output the HTML in the tmpl file
If you have more complex requirements then you may need to override more functionality in the Dispatcher.php file, but you can use AbstractModuleDispatcher as a base class.
Updated Manifest File
<?xml version="1.0" encoding="UTF-8"?>
<extension type="module" client="site" method="upgrade">
<name>MOD_HELLO_NAME</name>
<version>1.0.10</version>
<author>me</author>
<creationDate>today</creationDate>
<description>MOD_HELLO_DESCRIPTION</description>
<namespace path="src">My\Module\Hello</namespace>
<files>
<folder module="mod_hello">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
<folder>language</folder>
</files>
<scriptfile>script.php</scriptfile>
<media destination="mod_hello" folder="media">
<filename>joomla.asset.json</filename>
<folder>js</folder>
</media>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="header"
type="list"
label="MOD_HELLO_HEADER_LEVEL"
>
<option value="h3">MOD_HELLO_HEADER_LEVEL_3</option>
<option value="h4">MOD_HELLO_HEADER_LEVEL_4</option>
<option value="h5">MOD_HELLO_HEADER_LEVEL_5</option>
<option value="h6">MOD_HELLO_HEADER_LEVEL_6</option>
</field>
</fieldset>
</fields>
</config>
</extension>