پلاگین Ajax

مقدمه

فرض کنید یک سایت جوملا دارید که رزرو رویدادها را مدیریت می‌کند. در روز رویداد، افرادی که قرار است صندلی‌ها را بچینند می‌خواهند بدانند چند نفر شرکت می‌کنند. لذا آدرسی را در مرورگر وارد می‌کنند که روی سایت جوملای شما یک عملیات اجرا کرده و تعداد شرکت‌کنندگان را نمایش می‌دهد.

این کار به خوبی با استفاده از یک پلاگین Ajax انجام می‌شود. با وجود نام آن، پلاگین Ajax فقط محدود به درخواست‌های Ajax توسط جاوااسکریپت نیست و می‌توانید آن را برای پاسخ به درخواست‌های معمولی GET در مرورگر نیز استفاده کنید.

برای چنین کارهای فوری و پراکنده، استفاده از پلاگین Ajax بهتر از نوشتن اسکریپت سفارشی است، چون این قابلیت در خود جوملا پشتیبانی شده و معمولاً در بروزرسانی‌های جوملا نیازی به تغییرات زیادی ندارد. ضمن اینکه می‌توانید همه کارهای خود را در یک فایل PHP متمرکز کنید.

این مثال پلاگین Ajax یک ابزار ساده برای نمایش تعداد افزونه‌های نصب شده در سایت جوملا، دسته‌بندی شده بر اساس نوع افزونه است. کافی است آدرسی مانند این را در مرورگر وارد کنید:

https://example.com/index.php?option=com_ajax&format=raw&plugin=getExtensionTotals

به عنوان پاسخ تعداد افزونه‌ها بر اساس نوع آن‌ها نمایش داده خواهد شد، مثلاً:

component:45

file:3

language:9

…

نحوه نوشتن پلاگین Ajax

برای نوشتن پلاگینی که درخواست URL زیر را پاسخ دهد:

https://example.com/index.php?option=com_ajax&format=raw&plugin=getExtensionTotals

فقط کافی است یک پلاگین استاندارد بسازید که به ایونت(رویداد) `onAjaxGetExtensionTotals` گوش دهد و نتیجه را به صورت رشته برگرداند. کد شبه به شکل زیر است:

 
public function onAjaxGetExtensionTotals(Event $event)
{
    // اجرای کوئری روی دیتابیس
    // بازگرداندن نتیجه
}

پارامترهای query که باید در URL مشخص کنید عبارتند از:

- `option=com_ajax` 

- `format=<نوع قالب>` (می‌توانید format=json قرار دهید تا خروجی JSON باشد، در غیر این صورت خروجی فقط رشته معمولی خواهد بود) 

- `plugin=<نام کار>` که باعث فعال شدن event به شکل `onAjax<نام کار>` می‌شود (توجه به حروف بزرگ مهم است). در مثال ما `plugin=getExtensionTotals` باعث فراخوانی `onAjaxGetExtensionTotals` می‌شود.

می‌توانید پارامترهای اضافی هم ارسال کنید که در کد با استفاده از Input قابل دریافت و پردازش هستند.

درباره محیط سایت، مدیریت و کاربران وارد شده

کد مربوط به site و administrator در `com_ajax` کاملاً یکی است، پس می‌توانید آدرس زیر را نیز برای اجرا استفاده کنید:

https://example.com/administrator/index.php?option=com_ajax&format=raw&plugin=getExtensionTotals

اما در حالت اول، کد روی SiteApplication اجرا می‌شود و در حالت دوم روی AdministratorApplication که روی توابعی مثل ساخت مسیرهای SEF یا دریافت لیست منوها تأثیرگذار است. 

همچنین اگر کاربر وارد شده باشد، وضعیت ورود در بین قسمت front-end و back-end (در صورت فعال بودن گزینه Shared Sessions در تنظیمات سیستم) به اشتراک گذاشته می‌شود.

اگر در کد خودتان برای ورود کاربر به صورت هاردکد شده استفاده کنید، حتماً قبل از خروج از کد او را خارج کنید و تمام عملکردها را داخل بلوک try/catch قرار دهید تا خطاها مدیریت شوند؛ وگرنه وضعیت ورود در سشن ادامه خواهد داشت.

کد کامل پلاگین Ajax

در ادامه کد کامل پلاگین Ajax ارائه شده است (می‌توانید دستی کپی کرده یا به صورت فایل زیپ دانلود کنید). اگر به صورت دستی می‌نویسید، فایل‌ها را در پوشه‌ای مثل plg_ajax_jobs قرار دهید.

 

فایل Manifest (تعریف پلاگین)

یک فایل مانیفست استاندارد برای پلاگین:

`plg_ajax_jobs/jobs.xml`

 
<?xml version="1.0" encoding="utf-8"?>
<extension method="upgrade" type="plugin" group="ajax">
    <name>Ajax Jobs</name>
    <version>1.0</version>
    <description>نمونه پلاگین Ajax برای انجام کارهای فوری</description>
    <author>Me</author>
    <creationDate>Today</creationDate>
    <namespace path="src">My\Plugin\Ajax\AjaxJobs</namespace>
    <files>
        <folder plugin="jobs">services</folder>
        <folder>src</folder>
    </files>
</extension>

 

فایل Service Provider

یک فایل سرویس پرووایدر استاندارد برای راه‌اندازی پلاگین از طریق کانتینر تزریق وابستگی (Dependency Injection Container):

`plg_ajax_jobs/services/provider.php`

 
<?php

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use My\Plugin\Ajax\AjaxJobs\Extension\Jobs;

return new class() implements ServiceProviderInterface
{
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {

                // دریافت تنظیمات پلاگین 'ajax', 'jobs'
                $config = (array) PluginHelper::getPlugin('ajax', 'jobs');

                // گرفتن Dispatcher برای رویدادها
                $subject = $container->get(DispatcherInterface::class);

                // دریافت آبجکت برنامه (Application)
                $app = Factory::getApplication();

                // ساخت نمونه پلاگین Jobs با پارامترهای لازم
                $plugin = new Jobs($subject, $config);

                // تعیین آبجکت برنامه برای پلاگین
                $plugin->setApplication($app);

                return $plugin;
            }
        );
    }
};

 

فایل Jobs (کد اصلی پلاگین)

در اینجا کد مربوط به کارهای فوری خود را می‌نویسید. خروجی مطابق با روش‌های جوملا ۴ و ۵ بازگردانده می‌شود.

`plg_ajax_jobs/src/Extension/Jobs.php`

 
<?php
namespace My\Plugin\Ajax\AjaxJobs\Extension;

// جلوگیری از دسترسی مستقیم
defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Event\Result\ResultAwareInterface;
use Joomla\Database\DatabaseInterface;
   
class Jobs extends CMSPlugin implements SubscriberInterface
{
    // ثبت رویدادهایی که این پلاگین به آن‌ها گوش می‌دهد
    public static function getSubscribedEvents(): array
    {
        return [
            'onAjaxGetExtensionTotals' => 'onAjaxGetExtensionTotals',
        ];
    }
    
    // متد مدیریت رویداد onAjaxGetExtensionTotals
    public function onAjaxGetExtensionTotals(Event $event)
    {
        // گرفتن شی دیتابیس از container 
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        
        // ساخت کوئری برای شمارش تعداد افزونه‌ها بر اساس نوع
        $query = $db->getQuery(true)
                    ->select('type, count(*) as count')
                    ->from($db->quoteName('#__extensions'))
                    ->group('type');
        $db->setQuery($query);

        // اجرای کوئری و بارگذاری نتیجه به صورت آرایه‌ای [type => count]
        $counts = $db->loadAssocList('type', 'count');
        $output = "";
        foreach ($counts as $extension => $count) {
            $output .= "{$extension}:{$count}<br>";
        }

        // اگر رویداد از نوع پشتیبانی‌کننده نتایج باشد، نتیجه را اضافه می‌کنیم
        if ($event instanceof ResultAwareInterface) {
            $event->addResult($output);
        } else {
            // در غیر این صورت، نتیجه را در آرگومان 'result' قرار می‌دهیم
            $result = $event->getArgument('result') ?? [];
            $result[] = $output;
            $event->setArgument('result', $result);
        }
    }
}