مرحله ۱۰: استفاده از AbstractModuleDispatcher

اگر کد هر یک از ماژول‌های جوملا را بررسی کرده باشید، ممکن است متوجه شوید که کد Dispatcher آن‌ها بسیار متفاوت از کدی است که در این آموزش ایجاد کرده‌ایم. دلیل این تفاوت این است که آن‌ها از کلاس AbstractModuleDispatcher ارث‌بری می‌کنند.

در این مرحله، نگاهی به AbstractModuleDispatcher می‌اندازیم و بررسی می‌کنیم چگونه می‌توانیم با استفاده از آن کد خود را ساده کنیم.

کد منبع این مرحله در mod_hello مرحله ۱۰ موجود است

(https://github.com/joomla/manual-examples/tree/main/module-tutorial/step10_abstract_module_dispatcher)

قابلیت‌های AbstractModuleDispatcher

کد کلاس \Joomla\CMS\Dispatcher\AbstractModuleDispatcher در مسیر زیر قرار دارد

`libraries/src/Dispatcher/AbstractModuleDispatcher.php`

این کلاس خود از /Joomla\CMS\Dispatcher\Dispatcher در مسیر فایل `libraries/src/Dispatcher/Dispatcher.php` ارث می‌برد.

نکات مهم در مورد AbstractModuleDispatcher عبارتند از:

- در سازنده این کلاس، متغیرهای $module (ماژول)، $app (اپلیکیشن) و $input (ورودی) دریافت شده و در متغیرهای نمونه ذخیره می‌شوند.

- دارای تابع `loadLanguage` است که فایل زبان .ini مربوط به ماژول را بارگذاری می‌کند.

- تابعی به نام `getLayoutData` دارد که آرایه‌ای با ۵ متغیر (module, app, input, params و template) را برمی‌گرداند:

 
return [
    'module'   => $this->module,
    'app'      => $this->app,
    'input'    => $this->input,
    'params'   => new Registry($this->module->params),
    'template' => $this->app->getTemplate(),
];

(این ۵ متغیر در نسخه‌های پیشین جوملا ۴ هم به کد ماژول‌ها داده می‌شدند و برای سازگاری حفظ شده‌اند).

- تابع `dispatch` این کارها را انجام می‌دهد:

  - زبان را از طریق `loadLanguage` بارگذاری می‌کند.

  - `getLayoutData` را فراخوانی کرده و آرایه بازگشتی را به متغیر `$displayData` اختصاص می‌دهد.

  - با استفاده از `extract` در PHP عناصر آرایه `$displayData` را به متغیرها تبدیل می‌کند.

  - سپس فایل tmpl مورد نیاز را اجرا می‌کند:

 
require ModuleHelper::getLayoutPath($module->module, $params->get('layout', 'default'));

دو عملیات آخر در یک تابع جدا انجام می‌شوند تا محدوده (scope) تمیزی ایجاد کنند.

استفاده از AbstractModuleDispatcher

از آنجایی که Dispatcher ما (در mod_hello) خیلی شبیه AbstractModuleDispatcher است، می‌توانیم از این کلاس استفاده کنیم تا کارهای زیر را برای ما انجام دهد:

- ذخیره پارامترهای سازنده و در دسترس قرار دادن متغیرهای $module، $app و $input

- بارگذاری زبان

- بارگذاری فایل tmpl

تنها کاری که باید انجام دهیم این است که تابع `getLayoutData` را بازنویسی کنیم تا عنصر `'hello'` را به آرایه داده‌ها اضافه کنیم تا وقتی `extract` اجرا شود، تبدیل به متغیر `$hello` شود.

نسخه جدید فایل Dispatcher ما شامل حذف چند خط و تبدیل به کد زیر شده است:

 
<?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;
    }
}

نکته درباره فایل tmpl

در فایل tmpl فقط باید توجه داشته باشیم که به جای استفاده از `$this->app`، از `$app` استفاده کنیم. متغیر `$hello` هنگام انجام extract روی آرایه داده‌ها تعریف خواهد شد.

کد فایل tmpl به شکل زیر است:

 
<?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');

// ارسال افزونه به جاوااسکریپت
$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>

الگوی کلی برای ماژول‌ها

ماژول‌ها معمولاً از یک الگوی ساده پیروی می‌کنند:

- داده‌ای که می‌خواهید نمایش دهید را دریافت کنید (هر منطق پیچیده را در یک فایل helper بگذارید)

- داده‌ها را در یک بخش از HTML نمایش دهید

اگر ماژولی که می‌خواهید توسعه دهید این الگو را دنبال می‌کند، می‌توانید به صورت زیر عمل کنید:

- یک کلاس Dispatcher مانند mod_hello بنویسید.

- تابع `getLayoutData` را متناسب با نیازهای خود سفارشی کنید تا آرایه داده‌ها را تنظیم کنید. به یاد داشته باشید عناصری که در این آرایه قرار می‌گیرند با `extract` به متغیرهای PHP تبدیل شده و در فایل tmpl در دسترس خواهند بود.

- هر منطق پیچیده را در توابعی در داخل فایل helper جای دهید.

- با استفاده از داده‌هایی که تهیه کرده‌اید، در فایل tmpl خروجی HTML را تولید کنید.

اگر نیازهای شما پیچیده‌تر است، ممکن است لازم باشد چند قابلیت بیشتر را در فایل Dispatcher.php بازنویسی (override) کنید، اما می‌توانید از AbstractModuleDispatcher به عنوان کلاس پایه استفاده نمایید.

فایل مانیفست بروزشده 

فایل: `mod_hello/mod_hello.xml`

 
<?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>