The module helper
A helper is a class that holds data retrieval: a database query, a call to an external service, calculations. The dispatcher stays short: it only asks the helper for data and passes it to the layout. Let us teach mod_hello to show the latest articles of the site.
Where it lives
Create the src/Helper folder and a file in it by hand:
modules/mod_hello/
├── mod_hello.xml
├── services/provider.php
├── src/
│ ├── Dispatcher/Dispatcher.php
│ └── Helper/
│ └── HelloHelper.php new file
└── tmpl/default.phpThe manifest does not change: the whole src folder is already listed in it.
The helper class
// modules/mod_hello/src/Helper/HelloHelper.php
namespace My\Module\Hello\Site\Helper;
defined('_JEXEC') or die;
use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;
class HelloHelper implements DatabaseAwareInterface
{
use DatabaseAwareTrait;
public function getArticles(int $limit): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'catid', 'language']))
->from($db->quoteName('#__content'))
->where($db->quoteName('state') . ' = 1')
->order($db->quoteName('created') . ' DESC')
->setLimit($limit);
return $db->setQuery($query)->loadObjectList();
}
}The method returns an array of objects — the latest published articles. The DatabaseAwareInterface interface tells Joomla "this class needs the database", and Joomla passes the connection in itself; getDatabase() returns it. Queries are covered in detail in the component chapter.
Calling it from the dispatcher
The helper is obtained through a factory — the one registered in the provider by the HelperFactory line:
// modules/mod_hello/src/Dispatcher/Dispatcher.php
namespace My\Module\Hello\Site\Dispatcher;
defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
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();
$data['greeting'] = 'Hello!';
$data['articles'] = $this->getHelperFactory()
->getHelper('HelloHelper')
->getArticles(5);
return $data;
}
}getHelper('HelloHelper') creates an object of the class from the src/Helper folder. A new variable, $articles, goes to the layout.
Output in the layout
<?php
// modules/mod_hello/tmpl/default.php
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
?>
<div class="mod-hello">
<p><?php echo htmlspecialchars($greeting, ENT_QUOTES, 'UTF-8'); ?></p>
<ul>
<?php foreach ($articles as $article) : ?>
<li>
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($article->id, $article->catid, $article->language)); ?>">
<?php echo htmlspecialchars($article->title, ENT_QUOTES, 'UTF-8'); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>Reload the site page: a list of the five latest articles with links appears under the greeting. RouteHelper::getArticleRoute() is a ready-made method of the articles component that builds an article address, and Route::_() turns it into a readable URL.
What else is in the data
The parent getLayoutData() already puts several values into the array:
| Variable in the layout | What it holds |
|---|---|
$module | The module itself: $module->title, $module->position |
$params | The module settings |
$app | The Joomla application |
$input | The request data |
Common mistake
The database query is written right in the tmpl/default.php layout. It works, but the layout stops being plain markup: a site owner who overrides it in the template gets a copy of the query and breaks the module at the first update. The helper prepares data, the layout only prints it.