joomla 5

فصل هشتم: توسعه پلاگین جوملا

(زمان تقریبی مطالعه: 19 - 37 دقیقه)

بیان خلاصه ای از بحث توسط هوش مصنوعی:

افزونه های جوملا ابزاری قدرتمند برای تغییر صفحه هر سایتی هستند. در واقع، حتی نصب اولیه جوملا همراه با چندین پلاگین است که قابلیت های یک سایت را گسترش می دهد.

در این فصل، یاد خواهیم گرفت که این پلاگین ها، ابزار قدرتمندی برای توسعه افزونه ی ما هستند. خواهیم دید که چگونه پلاگین ها جوملا برای سازماندهی بهتر به خانواده ها و انواع مختلفی دسته بندی می شوند. ما یاد خواهیم گرفت که رویدادهای پلاگین چیست و چگونه می توانیم آنها را به کامپوننت خود اضافه کنیم. ما همچنین ساختار فایل اصلی هر پلاگین را درک خواهیم کرد و فایل مانیفست را برای ثبت پلاگین های خود در جوملا ایجاد کرده و برخی از پارامترهای پیکربندی را اضافه کنید. در نهایت، یک پلاگین محتوا برای کامپوننت خود ایجاد می کنیم تا اطلاعات پروژه خود را در یک مقاله نشان دهیم.

در پایان این فصل، نحوه عملکرد پلاگین ها در جوملا را خواهید فهمید و شما قادر خواهید بود پلاگین های جوملای خود را برای گسترش قابلیت های جوملا یا افزودن ویژگی های بیشتر به کامپوننت خودتان بسازید.

پلاگین ها به شما این امکان را می دهند که ویژگی های ممتازی برای کامپوننت خود ایجاد کنید یا با سایر بخش های سایت جوملا بدون تغییر کد موجود، تعامل داشته باشید. اغلب، پلاگین های جوملا، نیازی به چیدمان یا نمایش تصویری ندارند، اما می توانند خروجی جوملا را به طور کامل تغییر دهند.

به عنوان مثال، می توانید از آنها برای مخفی کردن آدرس های ایمیل نشان داده شده در سایت خود استفاده کنید تا از استفاده ربات ها جلوگیری کنید. تغییر رنگ کلمات خاص؛ یا حتی متن مورد نظر را به طور کامل جایگزین کنید. بنابراین ایده خوبی است که از پلاگین ها به عنوان اولین گزینه برای یک پروژه استفاده کنید زیرا آنها راه های موثرتری برای یافتن راه حل ها در اختیار شما قرار می دهند.

اینها موضوعات اصلی هستند که در این فصل به آنها خواهیم پرداخت:

  • پلاگین در جوملا چیست؟
  • چه خانواده هایی از پلاگین ها در جوملا وجود دارد ؟
  • آشنایی با ساختار فایل پلاگین جوملا
  • ایجاد فایل مانیفست برای پلاگین های ما
  • ایجاد یک پلاگین محتوا

الزامات فنی

در این فصل، ما روی بهبود افزونه Simple Project Manager خود کار خواهیم کرد، بنابراین شما به موارد زیر نیاز دارید :

  • Visual Studio Code (یا ویرایشگر کد دلخواه شما)
  • سایت جوملا که در فصل های قبل نصب کردیم

تمام کدهای استفاده شده در این فصل را در GitHub خواهید یافت:

 https://github.com/PacktPublishing/Developing-Extensions-for-Joomla-5/tree/chapter08

پلاگین در جوملا چیست؟

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

پلاگین ها در سیستم ACL جوملا ادغام می شوند و می توانید مجوزهای مختلفی را در هر سطح نمایش جوملا تنظیم کنید. این یک سیستم منعطف ایجاد می کند که امکانات زیادی را برای مدیران وب سایت فراهم می کند.

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

گروه های پلاگینی که می توانیم در نصب جوملا پیدا کنیم، به شرح زیر است:

  • API Authentication
  • Authentication
  • Behavior
  • Captcha
  • Content
  • Editors
  • Editors-xtd
  • Extensions
  • Fields
  • Filesystem
  • Finder
  • Installer
  • Media Action
  • Privacy
  • Quick Icons
  • Sampledata
  • System
  • User
  • Web Services
  • Workflow

 در فصل 6 ، پلاگینی که متعلق به نوع وب سرویس است تا نقطه پایانی router را برای API ما ایجاد کنید. در قسمت های بعدی قصد داریم یک پلاگین محتوا و یک پلاگین Finder توسعه دهیم.

پلاگین  ها نیز مانند ماژول  ها یک صفحه پیکربندی (configuration) خارج از جعبه (اصطلاح Out of box یعنی پس از نصب قابل اجرا خواهد بود)  در جوملا دارند. ما همچنین این صفحه را در فایل مانیفست پلاگین ایجاد خواهیم کرد، همانطور که با ماژول ها انجام دادیم.

قبل از شروع توسعه پلاگین، اجازه دهید در بخش بعدی ببینیم که چگونه می توانیم اجرای پلاگین را از یک کامپوننت راه اندازی کنیم.

فراخوانی پلاگین های جوملا از کامپوننت ما

پلاگین ها در جوملا زمانی اجرا میشوند که رویدادی که به آن متصل شده اند، راه اندازی شود. در کامپوننت خود، می توانیم این رویدادها را فراخوانی کنیم و بنابراین از پلاگین ها برای بهبود نتایج خود استفاده کنیم.

در واقع، از آنجایی که ما از الگوی MVC جوملا در توسعه خود پیروی می کنیم، اتفاقاتی رخ می دهد که ما حتی متوجه آنها نشده بودیم. در فصل 2 ، مدل های خود را با گسترش کلاس جدول جوملا توسعه دادیم. اگر این کلاس را بررسی کنیم، می توانیم کد زیر را در متد store پیدا کنیم:

 
$this->getDispatcher()->dispatch('onTableBeforeStore',$event); 

این کد راه اندازی رویداد را به توزیع کننده فعلی واگذار می کند. رویدادی که در این کد فراخوانی می کنیم رویداد onTableBeforeStore  است که به ما اجازه می دهد تا قبل از ذخیره آن در پایگاه داده، تغییراتی را روی داده های ردیف جدول انجام دهیم.

این کدی است که برای ارسال یک رویداد پلاگین در کد خود به آن نیاز دارید. متد dispatch دو آرگومان می گیرد:

  • نام رویدادی که می خواهیم راه  اندازی کنیم
  • یک آبجکتEvent  با داده های رویداد شما

آبجکتEvent  حاوی تمام اطلاعاتی است که پلاگین برای مدیریت رویداد نیاز دارد. این آبجکت را می توان به راحتی با استفاده از کد زیر ایجاد کرد، جایی که همه ی آرگومان های رویداد خود را در متغیر دوم به عنوان کلیدهای آرایه اضافه می کنیم:

 
$event = AbstractEvent::create('NAME OF THE EVENT', 
['argument1' => 'Value'] 

فراخوانی پلاگین های جوملا از کامپوننت ما ترفند خوبی است که می توانیم برای سفارشی سازی بیشتر کامپوننت های خود از آن استفاده کنیم. برای مشاهده عملی این موضوع، اجازه دهید رویداد onContentPrepare  را در کامپوننت خود فراخوانی کنیم تا به کاربر اجازه دهیم توضیحات پروژه را با پلاگین سفارشی کند.

ما شروع به ویرایش توسط فایل src/component/site/src/View/Project/HtmlView.php  می کنیم و ابتدا فضای نام AbstractEvent  را اضافه می کنیم. اعلان فضای نام به صورت زیر است:

 
<?php 
namespace Piedpiper\Component\Spm\Site\View\Project; 
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; 
use Joomla\CMS\MVC\View\GenericDataException; 
use Joomla\CMS\Factory; 
use Joomla\CMS\Event\AbstractEvent; 
use Joomla\CMS\Plugin\PluginHelper; 
class HtmlView extends BaseHtmlView 
{ 
... 
} 

خطوط برجسته شده در بلوک کد قبلی، خطوط جدید هستند.

پس از این، می توانیم با افزودن کد برجسته شده زیر، راه انداز (trigger) خود را برای onContentPrepare  در متد display  قرار دهیم:

 
public function display($tpl=null): void 
{ 
$this->item = $this->get('Item'); 
$article = new \stdClass(); 
$article->text = $this->item->description; 
$event = AbstractEvent::create( 
'onContentPrepare', 
[ 
'context' => 'com_spm.project', 
'article' => $article 
]; 
); 
PluginHelper::getPlugin('content', 'projectlink') 
Factory::getApplication()->getDispatcher()-> 
dispatch('onContentPrepare', $event); 
$this->item->description = $article->text; 
if (count($errors = $this->get('Errors'))) 
{ 
throw new GenericDataException(implode("\n", 
$errors), 500); 
} 
parent::display($tpl); 
} 

در این مثال خاص، از آنجایی که رویداد onContentPrepare  قرار است برای مقالات جوملا استفاده شود، باید متغیرهای خود را با آنچه پلاگین های محتوا برای این روش انتظار دارند، تطبیق دهیم. بنابراین در فراخوانی خود، وقتی آرایه را با متغیرهایی که می توانیم استفاده کنیم، تعریف می کنیم، آبجکت$article  را با ویژگی text  حاوی متنی که می خواهیم محتوا را بررسی کنیم، ارسال می کنیم.

ما متد PluginHelper::getPlugin را فراخوانی می کنیم تا پلاگین محتوای خود را در رویدادهای جوملا به اشتراک بگذاریم. این به پلاگین محتوای ما اجازه می دهد تا دو خط بعد با رویدار onContentPrepare  فراخوانی شود.

 

-          یک راه ساده تر برای راه اندازی  onContentPrepare

راه انداز رویداد onContentPrepare  بسیار رایج است و جوملا کمک هایی برای کاهش مقدار کد در پلاگین های شما ارائه می دهد. بنابراین، برای راه اندازی این رویداد، میتوانیم به جای آن از روش زیر استفاده کنیم که ما را از ایجاد آبجکت $article بی نیاز میکند:

Joomla\CMS\HTML\Helpers\Content::prepare(this->item->description)

در نهایت، پلاگین های محتوا، متن اصلاح شده را در داخل ویژگیtext  آبجکت $article  ما بر می گرداند، بنابراین ما آن را دریافت می کنیم و ویژگی $this->item->description  خود را به روز می کنیم.

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

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

آشنایی با ساختار فایل پلاگین جوملا

از زمان جوملا 4، پلاگین ها از همان معماری ماژول ها و کامپوننت ها پیروی می کنند. نتیجه فوری این است که جوملا 4 بسیار سریعتر از نسخه های قبلی جوملا بارگذاری می شود. نقطه ضعف این است که ما از یک ساختار فایل واحد به ساختار فایل و پوشه ای (در فصل 2)، که هنگام راه اندازی کامپوننت خود، و هنگام نوشتن ماژول ها (در فصل 7) دیدیم، حرکت می کنیم.

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

  •  services : حاوی فایل provider.php  است. این کپی پلاگین ظرف تزریق وابستگی (dependency injection container) را ثبت می کند. این بدان معنی است که هر سرویسی که با پلاگین ما تزریق شود، به طور خودکار برای سایر افزونه های در دسترس نخواهد بود (مگر اینکه آنها آن را تزریق کنند). این به هدف جداسازی پلاگین از سایر بخش های چرخه اجرا جوملا و اطمینان از عدم تأثیر آن بر سایر قسمت های چارچوب اصلی است.
  • src :  این پوشه شامل کلاس هایی است که ما در پلاگین خود استفاده می کنیم و در پوشه های مختلف سازماندهی شده اند. می تواند حاوی تعداد پوشه هایی باشد که فضاهای نام ما نیاز دارند، اما حداقل باید حاوی پوشه Extension  با فایل پلاگین اصلی باشد.
  • src/Extension :  این زیر پوشه حاوی فایل اصلی پلاگین ما به همراه متدهای مختلف برای مدیریت رویدادها است. فایل اصلی از همان پوششی که در فضای نام تعریف شده است، استفاده خواهد کرد.
  • tmpl :  وقتی پلاگین ما مقداری کد HTML را خروجی می دهد، ما از این پوشه tmpl  برای ذخیره چیدمان آن کد، استفاده می کنیم. به این ترتیب کاربر به راحتی می تواند طرح را نادیده بگیرد و override کند.

علاوه بر این، ما یک فایل manifest.xml  با تعریف و تنظیمات پلاگین خود داریم، همانطور که در بخش بعدی خواهیم دید.

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

هر افزونه ای که می خواهیم در جوملا نصب کنیم، باید یک فایل مانیفست مناسب داشته باشید که ویژگی های آن را اعلام می کند. ما قبلاً یک فایل مانیفست برای کامپوننت خود در فصل 2 ایجاد کردیم و یک فایل دیگر برای ماژول خود در فصل 7 ایجاد کردیم. فایل مانیفست پلاگین ها در جوملا، بسیار شبیه به چیزی است که ما برای ماژول ها ایجاد کردیم، به این معنا که نه تنها جزئیات فنی برای افزونه، بلکه پارامترهای پیکربندی (در صورت وجود) برای پلاگین را نیز شامل می شود.

یک فایل مانیفست اولیه به صورت زیر است:

 
<?xml version="1.0" encoding="UTF-8"?> 
<extension type="plugin" group="PluginType" 
method="upgrade"> 
<name>plg_PluginType_PluginName</name> 
<author>Carlos Cámara</author> 
<creationDate>2023-05</creationDate> 
<copyright>(C) 2023 Piedpiper, Inc.</copyright> 
<license>GNU General Public License version 2 or later; 
see LICENSE.txt</license> 
<authorEmail>این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید</authorEmail> 
<authorUrl>https://extensions.hepta.es</authorUrl> 
<version>1.0.0</version> 
<description>PLG_PLUGINTYPE_PLUGINNAME_XML_DESCRIPTION 
</description> 
<namespace path="src">Piedpiper\Plugin\PluginType 
\PluginName</namespace> 
<files> 
<folder plugin="PluginName">services</folder> 
<folder>src</folder> 
<folder>tmpl</folder> 
</files> 
<languages> 
<language tag="en-GB">language/en-GB/ 
plg_PluginType_PluginName.ini</language> 
<language tag="en-GB">language/en-GB/ 
plg_PluginType_PluginName.sys.ini</language> 
</languages> 
<config> 
<fields name="params"> 
<fieldset name="basic"> 
</fieldset> 
</fields> 
</config> 
</extension> 

در کد قبلی، باید مقدار ویژگی group  را با گروه پلاگین خود (Content, Finder, ) جایگزین کنیم. در واقع در این کد هر زمان که PluginType  ظاهر شد باید آن را جایگزین کنیم و همچنین PluginName  را با نام پلاگین خود جایگزین کنیم.

اینmanifest  به ما این امکان را می دهد که به راحتی گروه های پلاگین خود را ایجاد کنیم. اگر از یک نوع پلاگین استفاده کنیم که در نصب جوملا فعلی ما وجود ندارد، جوملا، پوشه خاصی را برای گروه ایجاد می کند و پلاگین شما را در آنجا نصب می کند.

در بخش files ، همانطور که با کامپوننت و ماژول خود انجام دادیم، فایل ها و پوشه های مختلفی که بخشی از پلاگین ما هستند را تعریف می کنیم. اما در این حالت ممکن است متوجه تگfolder  شوید که اعلام می کند پوشه services دارای ویژگی پلاگین است و مقدار ویژگی، نام پلاگین است. به این ترتیب به جوملا می گوییم که فایل ارائه دهنده پلاگین ما است که اولین فایل هست که جوملا هنگام جستجوی پلاگین ما بررسی می کند.

در این فایل مانیفست، فضای نام پلاگین خود را تعریف می کنیم. فضاهای نام از سینتکس زیر استفاده می کنند:

 \Company name\Plugin\Type of plugin\PluginName 

 بنابراین برای ایجاد یک پلاگین محتوا، از فضای نامی مانند زیر استفاده می کنیم:

\Piedpiper\Plugin\Content\ProjectLink

 با کد قبلی، فضای نام یک پلاگین محتوا به نام ProjectLink  را اعلام می کنیم که آن را در بخش بعدی توسعه خواهیم داد.

ایجاد یک پلاگین محتوا

یک مورد استفاده جالب پلاگین های محتوا، استفاده از آنها برای گنجاندن محتوای سایر کامپوننت های مقالات و حتی ماژول های داخل جوملا است. این به مدیر سایت جوملا انعطاف پذیری زیادی میدهد  و از دیدگاه توسعه دهنده به راحتی قابل پیاده سازی است. بنابراین، ما قصد داریم یک پلاگین محتوا ایجاد کنیم که به مدیر وب سایت اجازه می دهد این پروژه ها را در محتوای خود قرار دهد.

مرسوم است که این نوع افزونه از یک کد کوتاه (shortcode) برای نشان دادن پروژه ای که می خواهیم اضافه کنیم، استفاده می کند، بنابراین افزونه ما از کد کوتاه {projectlink 42}  برای جاسازی پیوند پروژه با شناسه 42  در مقاله جوملا استفاده می کند:

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

<?xml version="1.0" encoding="UTF-8"?> 
<extension type="plugin" group="content" 
method="upgrade"> 
<name>plg_content_projectlink</name> 
<author>Carlos Cámara</author> 
<creationDate>2023-05</creationDate> 
<copyright>(C) 2023 Piedpiper, Inc.</copyright> 
<license>GNU General Public License version 2 or 
later; see LICENSE.txt</license> 
<authorEmail>این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید</authorEmail> 
<authorUrl>https://extensions.hepta.es</authorUrl> 
<version>1.0.0</version> 
<description>PLG_CONTENT_PROJECTLINK_XML 
_DESCRIPTION</description> 
<namespace path="src">Piedpiper\Plugin\Content\ 
ProjectLink</namespace> 
<files> 
<folder plugin="projectlink">services</folder> 
<folder>src</folder> 
<folder>tmpl</folder> 
</files> 
<languages> 
<language tag="en-GB">language/en-GB/ 
plg_content_projectlink.ini</language> 
<language tag="en-GB">language/en-GB/ 
plg_content_projectlink.sys.ini</language> 
</languages> 
<config> 
<fields name="params"> 
<fieldset name="basic"> 
</fieldset> 
</fields> 
</config> 
</extension> 

در این کد، پلاگین خود را به عنوان یک افزونه محتوا با نام ProjectLink  تعریف می کنیم. برای مقدار پلاگین، از رشته plg_content_projectlink استفاده می کنیم.  هنگامی که فایل های .ini را ایجاد می کنیم، این رشته هنگام نصب با رشته های زبان ما ترجمه می شود. 

ما همچنین نام پلاگین خود را درج می کنیم، اما اکنون ما از projectlink استفاده می کنیم زیرا نام واقعی پلاگین ما است.

حالا بیایید پوشه language خود را با رشته های زبان خود ایجاد کنیم. فایل زیر را با محتوای زیر ایجاد کنید

src/plugins/content/projectlink/language/en-GB/plg_content_projectlink.sys.ini

 
PLG_CONTENT_PROJECTLINK="Project Link" 
PLG_CONTENT_PROJECTLINK_XML_DESCRIPTION="Content 
plugin to include project details in your content. To 
include the link to a project in your articles you 
need to add the shortcode: {projectlink ID} Where ID 
is the id of the project." 

 این ترجمه رشته  هایی را که ما به فایل XML اضافه کرده ایم، فراهم می کند. در این مرحله، می توانیم این فایل را درsrc/plugins/content/projectlink/language/en-GB/plg_content_projectlink.ini  کپی کنیم.

سپس یک فایل به آدرس زیر با محتوای گفته شده، ایجاد کنید:

 src/plugins/content/projectlink/services/provider.php 

 
<?php 
use Joomla\CMS\Extension\PluginInterface; 
use Joomla\CMS\Plugin\PluginHelper; 
use Joomla\CMS\Factory; 
use Joomla\DI\Container; 
use Joomla\DI\ServiceProviderInterface; 
use Joomla\Event\DispatcherInterface; 
use Piedpiper\Plugin\Content\ProjectLink\ 
Extension\ProjectLink; 
return new class implements ServiceProviderInterface 
{ 
public function register(Container $container) 
{ 
$container->set( 
PluginInterface::class, 
function (Container $container) { 
$dispatcher = $container->get 
(DispatcherInterface::class); 
$plugin = new ProjectLink( 
$dispatcher, 
(array) PluginHelper::getPlugin 
('content', 'projectlink') 
); 
$plugin->setApplication 
(Factory::getApplication()); 
return $plugin; 
} 
); 
} 
}; 

 این کد کوچکترین ارائه دهنده ای هست که می توانید برای یک پلاگین داشته باشید. یک کپی از ظرف تزریق وابستگی جوملا به شما می دهد و کلاس پلاگین شما را با $dispatcher  مقداردهی می کند.

اکنون باید کلاس اصلی را برای افزونه خود ایجاد کنیم.

برای انجام این کار، فایل src/plugins/content/projectlink/src/Extension/ProjectLink.php  را با کد زیر ایجاد کنید:

 
<?php 
namespace Piedpiper\Plugin\Content\ 
ProjectLink\Extension; 
use Joomla\Event\SubscriberInterface; 
use Joomla\CMS\Plugin\CMSPlugin; 
use Joomla\Event\Event; 
class ProjectLink extends CMSPlugin implements 
SubscriberInterface 
{ 
protected $autoloadLanguage = true; 
public static function getSubscribedEvents(): 
array 
{ 
return [ 
'onContentPrepare' => 'getProjectLink' 
]; 
} 
public function getProjectLink(Event $event) 
{ 
$context = $event->getArgument('context'); 
 
if ($context != 'com_content.article') { 
return; 
} 
} 
} 

در کلاس Plugin ، کلاس CMSPlugin  را گسترش می دهیم. با استفاده از این کلاس، تمام تنظیمات لازم برای یک پلاگین، مانند ثبت رویدادها، انجام می شود. این پایگاه داده را در ویژگی $db  و سایر موارد  Bootstrap فراهم می کند.

کلاس پلاگین، دستور SubscriberInterface  را پیاده سازی می کند، به این معنی که ما از مدرن ترین و سریع ترین متد برای بارگیری پلاگین ها در جوملا استفاده می کنیم. با استفاده از SubscriberInterface ، پلاگین  های ما می توانند در صورت نیاز اجرای رویداد را متوقف کنند و تمام پارامترها در داخل آبجکت $event  محصور می شوند، که کار با آن، آسان تر از تکنیک قدیمی برای مدیریت رویدادها در جوملا هست، که چیزی بیش از یک فراخوان ساده PHP  نبود.

در کلاس پلاگین خود، ویژگی $autoloadLanguage را تعریف می کنیم. وقتی این ویژگی روی true تنظیم شود، جوملا، فایل های زبان را در ایجاد کلاس پلاگین اضافه میکند. فقط زمانی از آن استفاده کنید که رشته های زبانی دارید که باید هنگام اجرای پلاگین نشان داده شوند.

همانطور که پلاگین ما SubscriberInterface  را پیاده سازی می کند ، باید به آن بگوییم که به چه رویداد هایی گوش دهد. ما این کار را با استفاده از متد getSubscribedEvents    انجام می دهیم، جایی که آرایه  ای را با رویدادی که به آن گوش می دهیم، به  عنوان کلید آرایه و با متدی که آن را به عنوان مقدار مرتبط مدیریت می کند، بر می  گردانیم.

در نهایت، متد getProjectLink   را تعریف می کنیم که رویداد onContentPrepare  را مدیریت می کند. این متد فقط آبجکت $event  ارسال شده توسط جوملا را دریافت می کند و ما هر چیزی را که باید از این آبجکت بدانیم را بدست می آوریم.

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

می توانیم متد   getProjectLink خود را با کد زیر جایگزین کنیم تا عملکرد بهتری را شاهد باشیم:

 
public function getProjectLink(Event $event) 
{ 
$context = $event->getArgument('0'); 
if (strpos($context, 'com_content.article') === 
false) { 
return; 
} 
$pattern = '/{projectlink\s([1-9[0-9]*)}/i'; 
$article = $event->getArgument('1'); 
if (str_contains($article->text, '{projectlink ')) { 
preg_match_all($pattern, $article->text, 
$matches, PREG_SET_ORDER); 
if ($matches) { 
foreach ($matches as $projectId) { 
$id = trim($projectId[1]); 
$projectLink = LinkHelper 
::formatProjectLink($id); 
$shortcodeStart = strpos($article-> 
text, $projectId[0]); 
$article->text = substr_replace 
($article->text, $projectLink, 
$shortcodeStart, strlen 
($projectId[0])); 
} 
} 
$event->stopPropagation(); 
} 
} 

هنگامی که onContentPrepare  راه اندازی می شود، در متد getSubscribedEvents  تعریف کرده ایم که توسط متد getProjectLink    مدیریت می شود. بنابراین، در کد قبلی، رویداد را از جوملا دریافت می کنیم و ابتدا زمینه را بررسی می کنیم. ما فقط می خواهیم لینک پروژه را در نمای مقاله خود قرار دهیم، بنابراین زمینه های مجاز را به 'com_content.article'  محدود می کنیم. اگر می خواهید این ویژگی را در زمینه های دیگر نصب جوملای خود نیز مجاز کنید، می توانید منطق را بر اساس نیاز خود تغییر دهید.

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

در نهایت، ما یک جستجوی عبارت منظم انجام می دهیم تا شناسه پروژه را از کد کوتاه خود دریافت کنیم. زمانی که شناسه پروژه ای را که باید جستجو کنیم را دانستیم، باید آن را با لینکی به پروژه خود جایگزین کنیم.

برای جایگزینی لینک، ما قصد داریم یک کلاس Helper ایجاد کنیم که کار را انجام دهد.

بنابراین قبل از اضافه کردن $projectLink = LinkHelper::formatProjectLink($id);  ، ما باید چند مرحله اضافی را انجام دهیم. اول از همه، ما فایل  src/plugins/content/projectlink/src/Helper/LinkHelper.php را با حداقل محتوای زیر ایجاد می کنیم:

 
<?php 
namespace Piedpiper\Plugin\Content\ProjectLink\Helper; 
class LinkHelper 
{ 
} 

اینجاست که کلاس helper خود را تعریف کرده و فضای نام مناسبی را برای آن تنظیم می کنیم. همانطور که فضای نام را ایجاد کرده ایم، می توانیم اعلان آن را در بالای فایل زیر اضافه کنید:

src/plugins/content/projectlink/src/Extension/ProjectLink.php

 
use Piedpiper\Plugin\Content\ProjectLink\Helper 
\LinkHelper; 

پس از این، می توانیم به کلاس LinkHelper خود برگردیم و کد گذاری توابع آن را شروع کنیم.

 

اکنون متد formatProjectLink را به کلاس  اضافه می کنیم:

 
public static function formatProjectLink($id) 
{ 
$link = Text::_('PLG_CONTENT_PROJECTLINK_ 
PROJECT_NOT_FOUND'); 
$app = Factory::getApplication(); 
$model = $app 
->bootComponent('com_spm') 
->getMVCFactory() 
->createModel( 
'Project', 
'Site', 
['ignore_request' => true] 
); 
$item = $model->getItem($id); 
if ($item) { 
$item->url = Route::_( 
'index.php?option=com_spm&view= 
project&id=' . $item->id 
); 
$link = '<a href="' . $item->url . '">' . 
$item->name .'</a>'; 
} 
return $link; 
} 

این روش جایی است که ما بیشتر کارهای جایگزین را انجام می دهیم. ابتدا یک پیام عمومی برای موقعیت هایی که پروژه در سیستم ما یافت نمی شود، تعریف می کنیم. برای انجام این کار، از قابلیت های زبان جوملا که در فصل 5 بررسی کردیم، استفاده میکنیم. همانطور که از کلاس Text  جوملا استفاده می کنیم، باید استفاده از فضای نام آن را در بالای فایل خود اعلام کنیم. ما همچنین از کلاس Route  استفاده می کنیم، بنابراین باید فضای نام آن را نیز اعلام کنیم. در کنار اینها، ما از کلاس Factory  برای نمونه برنامه سایت جوملا استفاده می کنیم ، بنابراین بالاتر از کلاس ما موارد زیر را اضافه می کنیم:

 
use Joomla\CMS\Language\Text; 
use Joomla\CMS\Router\Route; 
use Joomla\CMS\Factory; 

برای استفاده از ProjectModel  که در فصل 2 ایجاد کردیم، مدل کامپوننت را بارگذاری می کنیم. ما از متدbootComponent  برای راه اندازی صحیح کامپوننت خود استفاده می کنیم و یک کپی از آبجکت مدل دریافت می کنیم تا در پلاگین خود استفاده کنیم. از این مدل می توانیم پروژه را با استفاده از متدgetItem    دریافت کنیم. این متد کامپوننت (در این مورد، کامپوننت SPM خودمان) را به گونه  ای مقداردهی می کند که گویی به هر یک از نماهای آن دسترسی داریم. به این ترتیب می توانیم از تمام قدرت آن در هر بخشی از جوملا بهره ببریم. در این صورت از آن برای دریافت مدل پروژه و عنوان و لینک پروژه خاص خود استفاده می کنیم.

هنگامی که پروژه خود را در متغیر $item قرار دادیم ، URL  آن را با استفاده از کلاس Route  دریافت می کنیم، کد پیوند را ایجاد می کنیم و آن را بر می گردانیم.

برای تکمیل افزونه خود، باید ترجمه کلید زبان را به فایل زبان اصلی خود اضافه کنیم:

src/plugins/content/projectlink/language/en-GB/plg_content_projectlink.ini:

 
PLG_CONTENT_PROJECTLINK_PROJECT_NOT_FOUND="Project Not found" 

و با آن، ما یک پلاگین محتوای کاربردی برای مدیر پروژه خود داریم. اما اگر مشتریان ما بخواهند پیوند را سفارشی کنند چه اتفاقی می افتد؟ این کار بدون ویرایش کد افزونه غیرممکن خواهد بود.

9.       برای اینکه امکان سفارشی سازی آسان نمایش پلاگین خود را فراهم کنیم، باید از چیدمان (layout) پلاگین استفاده کنیم.

 

برای انجام این کار، ما باید فایل src/plugins/content/projectlink/tmpl/link.php   را با محتوای زیر ایجاد کنیم:

 

 
<div id="spm-project-<?php echo $item->id;?>" 
class="spm-project"> 
<a href="/<?php echo $item->url; ?>"> 
<?php echo $item->name; ?> 
</a> 
</div> 

این کد HTMLکمی مفصل تر از پیوند ساده ای است که در پایان مرحله 6 داشتیم. در کد قبلی، یک شناسه عنصر منحصر به فرد برای کانتینر &lt;div&gt;  هر پیوند ایجاد می کنیم و حتی یک کلاس CSS اضافه می کنیم.

برای استفاده از این فایل، چیدمان link.php ، باید ساختار  if فایل Helper را با این کد جایگزین کنی:

 
if ($item) { 
$item->url = Route::_( 
'index.php?option=com_spm&view=project&id=' . 
$item->id 
); 
$layoutFile = PluginHelper::getLayoutPath( 
'content', 
'projectlink', 
'link' 
); 
ob_start(); 
include $layoutFile; 
$link = ob_get_clean(); 
} 

در اینجا، ما از متد PluginHelper::getLayoutPath  برای دریافت چیدمان خود استفاده می کنیم. نوع پلاگین خود محتوا (content) ، نام آن  پیوند پروژه (projectlink) و نام فایل چیدمان ما لینک (link) را ارسال میکنیم. که مسیر فایل layout را نشان می دهد و همچنین برای لغو این فایل در قالب فعلی را بررسی می کند.

در جوملا، ما یک تابع بومی برای نمایش چیدمان پلاگین نداریم، بنابراین به وسیله ی دستورات PHP از توابع  ob_startو  ob_get_clean برای گرفتن خروجی کد بین این دو متد استفاده می کنیم. ما از دستور include برای تولید کد HTML برای نمایش به کاربر استفاده می کنیم و خروجی را در متغیر $link  با متدob_get_clean  می گیریم.

از آنجایی که ما از کلاس PluginHelper جوملا استفاده می کنیم، همچنین باید آن را در بالای فضاهای نام خود برای Helper قرار دهیم، بنابراین عبارت زیر را درست در زیر سایر اعلان  های فضای نام، قرار می دهیم.:

 
use Joomla\CMS\Plugin\PluginHelper;  

و با آن کد، پلاگین خود را تکمیل کردیم. اکنون یک پلاگین محتوای کاربردی داریم که از مدل کامپوننت برای بازیابی یک پروژه خاص استفاده می کند و یک کد کوتاه را با URL صحیح بارگذاری میکند.

 

افزودن پلاگین به کامپوننت ما

در بخش های قبلی نحوه فراخوانی پلاگین های جوملا از کامپوننت را توضیح دادیم. ما همچنین پلاگین محتوای خود را ایجاد کردیم که به کامپوننت ما نگاه می کند و آن را در صورت نیاز فقط با استفاده از یک کد کوتاه نمایش می دهد. اگر این دو مورد را در کنار هم قرار دهیم، می توانیم مزیت دیگری از استفاده از پلاگین ها در پروژه هایمان به دست بیاوریم: توانایی گسترش ویژگی  هایمان بدون تغییر کد.

در کامپوننت Simple Project Manager خود، می توانیم هر تعداد رویداد پلاگین را که برای این کار نیاز داریم اضافه کنیم. برای مثال، می توانیم رویدادی را اضافه کنیم که قبل از اضافه شدن مشتری جدید به سیستم ما فراخوانی می شود. در این رویداد، می توانیم پوشش ایمیل مشتری خود را قبل از ذخیره آن در پایگاه داده، تعمیر کنیم.

اولین کاری که باید انجام دهیم این است که راه انداز (trigger) رویداد خود را ایجاد کنیم. این مرحله دقیقاً همان چیزی است که در فراخوانی پلاگین ها از کامپوننت در بخش قبلی دیدیم.

بنابراین، برای ادامه، فایل /src/component/admin/src/Model/CustomerModel.php  را ویرایش می کنیم و افزودن متد save برای اضافه کردن راه انداز (trigger) به پلاگین:

 
public function save($data) 
{ 
PluginHelper::importPlugin('spm'); 
$event = new GenericEvent( 
'onSpmCustomerBeforeSave', 
[ 
'data' => $data 
] 
); 
$this->getDispatcher()->dispatch 
('onSpmCustomerBeforeSave', $event); 
$data = $event->getArgument('data'); 
return parent::save($data); 
} 

این تابع override از متد save کلاس  AdminModel است ، بنابراین ما فقط باید پلاگین خود را وارد کنیم و سپس متد کلاس والد را فراخوانی کنیم.

ابتدا از متد PluginHelper::getPlugin برای اشتراک پلاگین خود در رویداد onSpmCustomerBeforeSave  استفاده می کنیم. این به جوملا می گوید که به دنبال رویداد onSpmCustomerBeforeSave  در همه پلاگین ها (از نوع spm) باشد.

پس از آن، می تواند رویداد onSpmCustomerBeforeSave خود را راه اندازی کند. ما رویداد را با استفاده از توزیع کننده (dispatcher) جوملا راه اندازی می کنیم و برای انتقال اطلاعات به رویداد خود، از یک آبجکت  GenericEventکه قبل از فراخوانی توزیع کننده، نمونه سازی کرده ایم، استفاده می کنیم.

برای نمونه سازی آبجکت GenericEvent ، به دو آرگومان نیاز داریم، نام رویداد و یک آرایه شاخص (indexed) با آرگومان هایی که می خواهیم منتقل کنیم. در این آرایه، کلید ها نشان دهنده نام آرگومان هایی هستند که برای بازیابی آنها استفاده خواهیم کرد. در این مثال، ما فقط داده هایی را که در مدل دریافت می کنیم، ارسال می کنیم.

ارسال رویداد، همه افزونه  ها از نوع spm  را در رویداد  onSpmCustomerBeforeSaveدر قلاب میگیرد، داده ها را دریافت می کند و برخی عملیات را با آن انجام می دهد. برخی از این افزونه  ها ممکن است داده  هایی را که ارسال می  کنیم، با تمام تغییرات ذخیره شده در آبجکت $event ، تغییر دهند. (در واقع، افزونه  ای که می خواهیم برای توضیح این مثال بنویسیم، این کار را انجام می دهد)

برای بازیابی داده های اصلاح شده، باید متد getArgument را با ارسال نام آرگومانی که می خواهیم بازیابی کنیم، فراخوانی کنیم.

پس از دریافت تغییرات، می توانیم داده های خود را ذخیره کنیم و از آنجایی که قصد نداریم تغییرات بیشتری ایجاد کنیم، متد والد save را فراخوانی می کنیم.

در نهایت، همانطور که از کلاس GenericEvent ارائه شده توسط جوملا استفاده می کنیم ، باید فضای نام آن را مشخص کنیم، بنابراین در بالای فایل، اعلان آن را اضافه می کنیم:

 
use Joomla\CMS\Event\GenericEvent; 

این اصلاح به ما امکان می دهد تا پلاگین  ها را به فرآیند ذخیره مشتری خود متصل کنیم، اما ما هنوز هیچ افزونه  ای نداریم که بتواند از این ویژگی استفاده کند، بنابراین در بخش بعدی افزونه  ای را توسعه خواهیم داد که تمام آدرس های ایمیل مشتری را برطرف خواهد کرد.

 

ایجاد افزونه مشتری ما

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

این یعنی، پلاگین های این نوع جدید spm  ، در پوشه خود، در هنگام نصب در سایت ذخیره می شوند. در واقع، ایجاد این نوع جدید در جوملا به سادگی نصب اولین پلاگین از این نوع است. همانطور که در ابتدای این فصل مشاهده شد، نوع پلاگین در فایل مانیفست پلاگین تعریف شده است، بنابراین بیایید با ایجاد فایلی به نام src/plugins/spm/customers/customers.xml  شروع کنیم که حاوی محتوای زیر است:

 
<?xml version="1.0" encoding="UTF-8"?> 
<extension type="plugin" group="spm" method="upgrade"> 
<name>plg_spm_customers</name> 
<author>Carlos Cámara</author> 
<creationDate>2023-05</creationDate> 
<copyright>(C) 2023 Piedpiper, Inc.</copyright> 
<license>GNU General Public License version 2 or later; see LICENSE.txt</license> 
<authorEmail>این آدرس ایمیل توسط spambots حفاظت می شود. برای دیدن شما نیاز به جاوا اسکریپت دارید</authorEmail> 
<authorUrl>https://extensions.hepta.es</authorUrl> 
<version>1.0.0</version> 
<description>PLG_SPM_CUSTOMERS_XML_DESCRIPTION </description> 
<namespace path="src">Piedpiper\Plugin\Spm\Customers </namespace> 
<files> 
<folder plugin="customers">services</folder> 
<folder>src</folder> 
<folder>services</folder> 
</files> 
<languages> 
<language tag="en-GB">language/en-GB/plg_spm_customers.ini</language> 
<language tag="en-GB">language/en-GB/plg_spm_customers.sys.ini</language> 
</languages> 
<config> 
<fields name="params"> 
<fieldset name="basic"> 
</fieldset> 
</fields> 
</config> 
</extension> 

این یک فایل مانیفست ساده است. می توانید ببینید که چگونه فقط با تعریف نوع پلاگین در ویژگی group ، پلاگین متعلق به این نوع افزونه خواهد بود.

 
<extension type="plugin" group="spm" method="upgrade"> 

البته در فضای نام نیز نوع پلاگین را در مسیر منعکس می کنیم و طبق معمول از نام گروه در نام فایل های زبان استفاده می کنیم.

نکته: این افزونه هیچ داده ای را به کاربر نمایش نمی دهد، بنابراین ما یک پوشهtmpl  اضافه نمی کنیم.

ما می توانیم با ایجاد فایل src/plugins/spm/customers/services/provider.php با محتوای زیر به ساخت بقیه ساختار فایل پلاگین ادامه دهیم:

 
<?php 
use Joomla\CMS\Factory; 
use Joomla\CMS\Extension\PluginInterface; 
use Joomla\CMS\Plugin\PluginHelper; 
use Joomla\DI\Container; 
use Joomla\DI\ServiceProviderInterface; 
use Joomla\Event\DispatcherInterface; 
use Piedpiper\Plugin\Spm\Customers\Extension\Customers; 
\defined('_JEXEC') or die; 
return new class implements ServiceProviderInterface 
{ 
public function register(Container $container) 
{ 
$container->set( 
PluginInterface::class, 
function (Container $container) { 
$dispatcher = $container->get 
(DispatcherInterface::class); 
$plugin = new Customers( 
$dispatcher, 
(array) PluginHelper::getPlugin('spm', 
'customers') 
); 
$plugin->setApplication 
(Factory::getApplication()); 
return $plugin; 
} 
); 
} 
}; 

این فایل provider.php  با همان اصولی که در بخش های قبلی توضیح داده شد، کار می کند. فقط توجه داشته باشید که ما کلاس  Customers  را از پوشه Extension  خودمان نمونه سازی می کنیم، بنابراین بیایید کلاس را در فایلsrc/plugins/spm/customers/src/Extension/Customers.php  با کد زیر ایجاد کنیم:

 
<?php 
namespace Piedpiper\Plugin\Spm\Customers\Extension; 
use Joomla\Event\SubscriberInterface; 
use Joomla\CMS\Plugin\CMSPlugin; 
use Joomla\Event\Event; 
\defined('_JEXEC') or die; 
class Customers extends CMSPlugin implements 
SubscriberInterface 
{ 
protected $autoloadLanguage = true; 
public static function getSubscribedEvents(): array 
{ 
return [ 
'onSpmCustomerBeforeSave' => 'fixCasing' 
]; 
} 
public function fixCasing(Event $event) 
{ 
$data = $event->getArgument('data'); 
$data['email'] = strtolower($data['email']); 
$event->setArgument('data', $data); 
return; 
} 
} 

این کلاس جایی است که همه ی جادو اتفاق می افتد. ابتدا باید رویداد هایی را که با استفاده از متد  getSubscribedEventsبه آنها متصل می کنیم، اعلام کنیم. در این روش، آرایه  ای را بر می  گردانیم که در آن کلید ها، نام رویدادی است که می خواهیم در پلاگین خود مشترک شویم و مقدار آن، نام متد است که رویداد را مدیریت می کند. در این مورد، متد fixCasing برای مدیریت رویداد onSpmCustomerBeforeSave  استفاده می شود.

متد fixCasing آبجکت$event  را که قبلاً به عنوان آرگومان ارسال کرده بودیم، هنگام ارسال رویداد در مدل دریافت می کند. بنابراین ما آرگومان را با نامش دریافت می کنیم و آن را بر اساس نیاز خود پردازش می کنیم.

پس از اتمام پردازش داده های دریافتی، داده های رویداد را با استفاده از متد setArgument به روز می کنیم، که توسط کلاس Event ارائه شده است.

در نهایت برای تکمیل پلاگین خود، فقط باید فایل های زبان را اضافه کنیم. بنابراین فایل های زیر را با محتوای یکسان ایجاد می کنیم:

 src/plugins/spm/customers/language/en-GB/plg_spm_customers.ini 

 src/plugins/spm/customers/language/en-GB/plg_spm_customers.sys.ini

 
PLG_SPM_CUSTOMERS="SPM Customers plugin" 
PLG_SPM_CUSTOMERS_XML_DESCRIPTION="This plugin fixes customer's email casing before saving it" 

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

منابع فصل

اسناد رسمی جوملا یک مقاله جالب در مورد پلاگین ها دارد:

فصل های دیگر کتاب

نوشتن دیدگاه

ارسال