A arquitetura moderna do Joomla transformou a maneira como desenvolvemos módulos. Com a introdução de Namespaces e Containers de Injeção de Dependências (DI), o código tornou-se mais seguro, testável e organizado. Neste guia, vamos explorar o mod_hello do zero, analisando cada arquivo da sua estrutura real passo a passo.
Passo 1: A Estrutura do Projeto
Nossa base será a hierarquia de pastas padrão de um módulo moderno no Joomla. Certifique-se de ter criado a seguinte estrutura dentro da pasta mod_hello:
mod_hello/ ├── language/ # Arquivos de linguagem │ └── en-GB/ # Diretório com traduções │ ├── mod_hello.ini # Texto exibido pelo módulo │ └── mod_hello.sys.ini # Metadados (nome e descrição) ├── media/ # Recursos estáticos │ ├── css/ # Estilos CSS do módulo │ ├── images/ # Imagens usadas pelo módulo │ └── js/ # Scripts JavaScript ├── services/ # Serviços para injeção de dependências │ └── provider.php # Configuração de serviços ├── src/ # Arquivos principais de lógica do módulo │ ├── Dispatcher/ │ │ └── Dispatcher.php # Controlador para tratar requisições │ └── Helper/ │ └── HelloHelper.php # Funções auxiliares do módulo ├── tmpl/ # Layouts e templates │ └── default.php # Arquivo de exibição padrão └── mod_hello.xml # Manifesto do módulo (configuração e estrutura)
Passo 2: O Manifesto XML mod_hello.xml
O manifesto é o documento de identidade do seu módulo. Além de listar os arquivos para a instalação, ele define o Namespace base (Joomla\Module\Hello) e os parâmetros do módulo, como a saudação ("greeting") e configurações avançadas de cache e layout.
<?xml version="1.0" encoding="UTF-8"?>
<extension type="module" client="site" method="upgrade">
<name>mod_hello</name>
<author>Ponto Mega</author>
<creationDate>2024-04-01</creationDate>
<copyright>(C) 2024 Ponto Mega</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail></authorEmail>
<authorUrl>https://www.pontomega.com.br</authorUrl>
<version>1.0.0</version>
<description>MOD_HELLO_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Module\Hello</namespace>
<files>
<folder module="mod_hello">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/mod_hello.ini</language>
<language tag="en-GB">language/en-GB/mod_hello.sys.ini</language>
</languages>
<media destination="mod_hello" folder="media">
<folder>images</folder>
<folder>css</folder>
<folder>js</folder>
</media>
<help key="Site_Modules:_Hello" />
<config>
<fields name="params" addfieldprefix="Joomla\Component\Content\Administrator\Field">
<fieldset name="basic">
<field name="greeting" type="text" label="MOD_HELLO_GREETING_LABEL" description="MOD_HELLO_GREETING_DESC" default="Hello, World!" />
</fieldset>
<fieldset name="advanced">
<field name="layout" type="modulelayout" label="JFIELD_ALT_LAYOUT_LABEL" class="form-select" validate="moduleLayout" />
<field name="moduleclass_sfx" type="textarea" label="COM_MODULES_FIELD_MODULECLASS_SFX_LABEL" rows="3" validate="CssIdentifier" />
<field name="cache" type="list" label="COM_MODULES_FIELD_CACHING_LABEL" default="1" filter="integer" validate="options">
<option value="1">JGLOBAL_USE_GLOBAL</option>
<option value="0">COM_MODULES_FIELD_VALUE_NOCACHING</option>
</field>
<field name="cache_time" type="number" label="COM_MODULES_FIELD_CACHE_TIME_LABEL" default="900" filter="integer" />
<field name="cachemode" type="hidden" default="static">
<option value="static"></option>
</field>
</fieldset>
</fields>
</config>
</extension>
Passo 3: O Provedor de Serviços services/provider.php
Este arquivo atua como o ponto de entrada (Entry Point) moderno. Ele instrui o Container DI do Joomla sobre como instanciar o seu Dispatcher e o seu Helper, dispensando o uso de require_once.
<?php
/**
* @package Joomla.Site
* @subpackage mod_hello
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
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;
/**
* The article latest module service provider.
*
* @since 4.2.0
*/
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->registerServiceProvider(new ModuleDispatcherFactory('\\Joomla\\Module\\Hello'));
$container->registerServiceProvider(new HelperFactory('\\Joomla\\Module\\Hello\\Site\\Helper'));
$container->registerServiceProvider(new Module());
}
};
Passo 4: O Dispatcher src/Dispatcher/Dispatcher.php
O Dispatcher é o Controlador. Invocado automaticamente pelo Joomla, ele usa a interface e trait HelperFactoryAware para instanciar o Helper de forma isolada, extraindo a lista de dados e mesclando-a ao array $data disponibilizado para a View.
<?php
/**
* @package Joomla.Site
* @subpackage mod_hello
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Module\Hello\Site\Dispatcher;
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
use Joomla\CMS\Helper\HelperFactoryAwareInterface;
use Joomla\CMS\Helper\HelperFactoryAwareTrait;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Dispatcher class for mod_hello
*
* @since 4.2.0
*/
class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareInterface
{
use HelperFactoryAwareTrait;
/**
* Returns the layout data.
*
* @return array
*
* @since 4.2.0
*/
protected function getLayoutData()
{
$data = parent::getLayoutData();
$data['list'] = $this->getHelperFactory()->getHelper('HelloHelper')->getItems($data['params'], $this->getApplication());
return $data;
}
}
Passo 5: O Helper src/Helper/HelloHelper.php
O Helper assume o papel de Model (camada de dados). É aqui que o módulo conecta-se ao banco de dados do Joomla utilizando DatabaseAwareTrait para realizar uma consulta customizada e segura (neste caso, buscando módulos na tabela #__modules limitados pelo parâmetro count).
<?php
/**
* @package Joomla.Site
* @subpackage mod_hello
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Module\Hello\Site\Helper;
use Joomla\CMS\Access\Access;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\Component\Content\Site\Model\ArticlesModel;
use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Helper for mod_hello
*
* @since 1.6
*/
class HelloHelper implements DatabaseAwareInterface
{
use DatabaseAwareTrait;
/**
* Retrieve a list of article
*
* @param Registry $params The module parameters.
* @param ArticlesModel $model The model.
*
* @return mixed
*
* @since 4.2.0
*/
public function getItems(Registry $params, SiteApplication $app)
{
$db = $this->getDatabase();
$user = $app->getIdentity();
$query = $db->getQuery(true)
->select('a.id, a.title')
->from('#__modules AS a')
->order('a.title ASC')
->setLimit($params->get('count', 5));
$db->setQuery($query);
$items = $db->loadObjectList();
return $items;
}
public static function getList(Registry $params)
{
return (new self())->getItems($params, Factory::getApplication());
}
}
Passo 6: O Layout de Exibição tmpl/default.php
O arquivo de layout mantém-se livre de lógicas complexas. Ele utiliza o Web Asset Manager para acoplar CSS e JS estáticos do módulo, e foca estritamente em renderizar a configuração de saudação (greeting) seguida pelo loop dos dados.
<?php
/**
* @package Joomla.Site
* @subpackage mod_hello
*
* @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;
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$wa->registerAndUseStyle('mod_hello_css', 'media/mod_hello/css/style.css');
$wa->registerAndUseScript('mod_hello_script', 'media/mod_hello/js/script.js');
if (!$list) {
return;
}
?>
<div class="hello">
<h3>
<?php echo $params->get('greeting'); ?>
</h3>
<ul>
<?php foreach ($list as $item) : ?>
<li>
<?php echo $item->title; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
🚀 Módulo Concluído!
Compacte a pasta mod_hello em um arquivo ZIP e acesse a administração do seu Joomla (Sistema → Extensões) para instalá-lo. Você acabou de implementar a estrutura moderna completa exigida pelo Joomla 5 e 6.