---
title: "Создание плагинов с учётом новой структуры Joomla 4 - WebTolk"
description: "Статья мануал по созданию и обновлению плагинов Joomla до новой архитектуры Joomla 4, чтобы они работали и на Joomla 5 и старше."
url: "https://web-tolk.ru/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4"
date: "2023-09-16T13:03:56+00:00"
language: "ru-RU"
---

# Создание плагинов с учётом новой структуры Joomla 4

 Автор: Сергей Толкачев Создано: 16 сентября 2023 Обновлено: 01 сентября 2025 Просмотров: 2170    ![Создание плагинов с учётом новой структуры Joomla 4](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/header-1080p.webp)

С момента выхода Joomla 4 прошло уже почти 2 года, поэтому слово "новой" будем понимать в контексте сравнения с Joomla 3. В Joomla 4 "под капотом" произошло немало изменений. Кодовая база движка постепенно отбрасывает legacy (старый код), встретившись с которым мы могли бы как на машине времени вернуться в середину 2000-х или начало 2010-х 😀. Статья первоначально опубликована на Хабре. Копирую к себе.

 ![](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/1.webp)Собеседник в последний раз видел Joomla лет 12-15 назад.

Неизбежно, внедрение стандартов, новых технологий приводит к тому, что времена "Дикого Запада" уходят и нужно приспосабливать свои расширения к изменениям в структуре компонентов, модулей и плагинов.

В данной статье пойдёт речь о том, как создать плагин (или обновить старый) для Joomla 4 с новой структурой файлов и классов.

## Отступление

Я предполагаю, что часть читателей имеет опыт работы с Joomla, но не имеет опыта создания плагинов, поэтому постараюсь описать создание плагина как можно подробнее. Статья имеет сугубо прикладной характер, без погружения в теорию ООП и его реализацию в Joomla. Основная цель - подсказать что "делать руками", когда поставлена определённая задача.

Также я предполагаю, что часть разработчиков могут встретиться с Joomla впервые. В терминологии Joomla "плагин" - это расширение, которое предоставляет функции, которые связаны с [событиями](https://docs.joomla.org/Plugin/Events) (Event Dispatching). Когда происходит вызов конкретного события, то происходит последовательное выполнение всех функций подключаемых плагинов, связанных с этим событием. События могут вызывать как ядро Joomla, так и компоненты, да и в принципе любые расширения Joomla. Для сравнения, в терминологии WordPress "плагин" - это то, что в Joomla называется "компонент".

Плагины могут выполнять самый разный функционал в зависимости от времени и места вызываемого события, по которому они срабатывают: можно добавить кнопку like к материалу, а можно отправить данные заказа по API в CRM систему, "прочесать" весь готовый HTML-код страницы регуляркой или добавить новые команды в Joomla CLI интерфейс.

Поскольку в Joomla начиная с версий 3.2 начинают вводится `namespaces` , а в Joomla 4 добавляется паттерн Service locator ([подробнее о DI и Service locator в Joomla](https://www.dionysopoulos.me/book/concepts-container.html)) - в примерах кода неизбежно будут встречаться специфичные для конкретного расширения имена классов, названия файлов. При создании своего плагина, естественно, нужно изменить их на свои.

## Файловая структура плагина Joomla 3

 ![файловая структура плагина SEF Joomla 3](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/2.webp)Старая файловая структура плагина Joomla 3

Для создания плагина в Joomla 3 было необходимо минимум 2 файла:

- **xml-манифест плагина** - описание плагина для установщика расширений Joomla (системное имя, дата, версия, сайт разработчика и т.д.), параметры конфигурации плагина, сервер обновлений и т.д.
- **файл класса плагина**- "точка входа" в плагин. С этого файла начиналась работа Вашего кода.
 ![3](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/3.webp)Файловая структура плагина инсталлера Joomla

Плагины, которые работают с HTML-кодом, могут иметь собственные макеты вывода. Такие макеты могут располагаться в папках `tmpl` и/или `layouts`, в зависимости от назначения этого макета (макет таба в табсете, макет для рендера изображения, макет ссылки, кнопки и любой другой запчасти - чаще всего `layout`). Также на это влияет выбранный разработчиком метод работы с HTML в коде - использует ли он классы для работы макетами `Joomla\CMS\Layout\FileLayout` (ex. JFileLayout) или `Joomla\CMS\Layout\LayoutHelper` (ex.JLayoutHelper). Порой плагин и просто `echo $any_text;` делает. Тогда у него может не быть макета вывода.

 ![Файловая структура плагина для интеграции Joomla 3 с Битрикс 24](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/4.webp)Файловая структура плагина для интеграции Joomla 3 с Битрикс 24

Некоторые плагины по функционалу приближаются к компонентам. В них сложнее файловая структура, они могут нуждаться в сторонних библиотеках для своей работы. Немало разработчиков, в попытке создать standalone-решение помещали всё необходимое в директорию с плагином. Для таких случаев в Joomla есть отдельный тип расширения - пакет (package), но это тема для отдельной статьи.

## Файловая структура плагина в Joomla 4

Общие положения, как для модулей так и для плагинов Joomla 4: все необходимые для работы логики плагина файла мы помещаем в папку `src` - файл плагина, дополнительные библиотеки, файлы классов полей и т.д.. Макеты по прежнему находятся в корне плагина в `tmpl` и `layouts`.

 ![Файловая структура плагина в Joomla 4](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/5.webp)Файловая структура плагина в Joomla 4

### Файл XML-манифеста плагина

Этот файл содержит описание плагина для установщика расширений Joomla (системное имя, группу плагина, дата создания, версия, сайт разработчика и т.д.), параметры конфигурации, сервер обновлений, **а также** **задаёт Namespace плагина и директории для автозагрузки классов**. Регистрация `Namespace` плагина в реестре происходит при установке расширения. На данный момент (Joomla 4.3.1) список классов для автозагрузки находится в файле `administrator/cache/autoload_psr4.php`. Он обновляется после каждой установки или обновления расширения.

 ![Namespace плагина Joomla 4 в xml-манифесте на примере плагина для Radical Form  и AmoCRM](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/6.webp)Namespace плагина Joomla 4 в xml-манифесте на примере плагина для Radical Form и AmoCRM

**На заметку:** Список соответствий названий старых классов Joomla и новых (алиасы) находится в `libraries/classmap.php`.

Обратите внимание, что `namespace` плагина должен содержать в себе группу плагина: *Joomla\Plugin\****System****\Yourclass*, *Joomla\Plugin\****Content****\Yourclass* и так далее.

 ![Пример xml-манифеста плагина Joomla 4](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/7.webp)Пример xml-манифеста плагина Joomla 4

Также стоит иметь в виду, что перевод расширений ядра Joomla на новую структуру на момент написания статьи ещё идёт и ожидается, что будет полностью завершён в Joomla 4.4. Например, для плагинов кнопки редактора (группа `editors-xtd`) переход на новую структуру до версии Joomla 4.4 не имеет смысла, так как ядро 4.0-4.3 ещё не поддерживает этот `namespace`.

Шаблон xml-манифеста для плагина Joomla 4.

```
<?xml version="1.0"?>
<extension type="plugin" method="upgrade" group="system">
    <name>PLG_WT_AMOCRM_RADICALFORM</name>
    <author>Sergey Tolkachyov</author>
    <authorEmail>info@web-tolk.ru</authorEmail>
    <authorUrl>https://web-tolk.ru/</authorUrl>
	<creationDate>06/12/2022</creationDate>
    <copyright>Sergey Tolkachyov</copyright>
    <license>GNU General Public License v3.0</license>
    <version>1.0.1</version>
    <description>PLG_WT_AMOCRM_RADICALFORM_DESC</description>
    <namespace path="src">Joomla\Plugin\System\Wt_amocrm_radicalform</namespace>
    <media folder="media" destination="plg_system_wt_amocrm_radicalform">
        <folder>js</folder>
    </media>
	<files>
        <folder plugin="wt_amocrm_radicalform">src</folder>
		<folder>services</folder>
        <filename>wt_amocrm_radicalform.xml</filename>
    </files>
    <languages folder="language" client="administrator">
        <language tag="en-GB">en-GB/plg_system_wt_amocrm_radicalform.ini</language>
        <language tag="ru-RU">ru-RU/plg_system_wt_amocrm_radicalform.ini</language>
        <language tag="en-GB">en-GB/plg_system_wt_amocrm_radicalform.sys.ini</language>
        <language tag="ru-RU">ru-RU/plg_system_wt_amocrm_radicalform.sys.ini</language>
    </languages>
    <config>
        <fields name="params">
            <fieldset name="basic">
              <!-- ЗДЕСЬ ПАРАМЕТРЫ ВАШЕГО ПЛАГИНА -->
              <!-- ТИПЫ ПОЛЕЙ JOOMLA ЗДЕСЬ -->
              <!-- https://docs.joomla.org/Standard_form_field_types -->
            </fieldset>
        </fields>
    </config>
</extension>
```

**Также обратите внимание**, что для корректной установки и работы плагина нужно указывать атрибут `plugin="wt_amocrm_radicalform"` в xml-манифесте. Если в Joomla 3 этот атрибут указывался для файла "точки входа" (`<filename plugin="wt_amocrm_radicalform">wt_amocrm_radicalform.php</filename>`), то сейчас он указывается для папки `src` плагина - `<folder plugin="wt_amocrm_radicalform">src</folder>` .

### Языковые файлы (файлы локализации)

Ещё одно **нововведение связано с языковыми файлами**: теперь в именах файлов не обязательно дублировать префикс языка - "ru-RU.plg_system_wt_amocrm_radicalform.ini". Достаточно того, что файл лежит в папке "ru-RU". Однако, по-прежнему в названии файла локализации должен быть указан тип расширения `plg`, группа плагина (в данном случае `system`) и системное имя - из атрибута `plugin="wt_amocrm_radicalform"` - в сумме получается `plg_system_wt_amocrm_radicalform`.

Также помним, что файлы с языковыми константами (файлы локализации) должны отправляться в папку `administrator/language`, так как один и тот же плагин может работать и "внутри" и "снаружи". Для этого указываем атрибут `client="administrator"`:

```
<languages folder="language" client="administrator">
```

### Файл services/provider.php

Минимально необходимый набор для плагина увеличился на 1 файл. Аналогично, как и для модулей (статья [Создание модулей с учётом новой структуры Joomla 4](https://habr.com/ru/articles/684534/)), плагину нужен файл сервис-провайдер Вашего плагина. Файл `provider.php` позволяет регистрировать плагин в DI-контейнере Joomla и даёт возможность обращаться к методам плагина извне с помощью `MVCFactory`.

```
<?php
/**
 *  @package   WT AmoCRM Radical Form
 *  @copyright Copyright Sergey Tolkachyov
 *  @license   GNU General Public License version 3, or later
 */

defined('_JEXEC') || die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\System\Wt_amocrm_radicalform\Extension\Wt_amocrm_radicalform;

return new class () implements ServiceProviderInterface {
	/**
	 * Registers the service provider with a DI container.
	 *
	 * @param   Container  $container  The DI container.
	 *
	 * @return  void
	 *
	 * @since   4.0.0
	 */
	public function register(Container $container)
	{
		$container->set(
			PluginInterface::class,
			function (Container $container) {
				$subject = $container->get(DispatcherInterface::class);
				$config  = (array) PluginHelper::getPlugin('system', 'wt_amocrm_radicalform');
				return new Wt_amocrm_radicalform($subject, $config);
			}
		);
	}
};
```

Поскольку плагины по-прежнему расширяют класс `CMSPlugin` (ex. `JPlugin`), Вы можете установить объект приложения (`Factory::getApplication()`) плагину прямо в сервис-провайдере. И затем в самом плагине Вы сможете обращаться к объекту приложения с помощью `$app = $this->getApplication();` Аналогично для удобства можно один раз указать объект для работы с базой данных: `$plugin->set Database($container->get(DatabaseInterface::class));`. И получить его в плагине `$db = $this->getDatabase();`.

```
<?php
/**
* Пример взят из книги греческого разработчика
* Николаса Дионисопулоса, Akeeba Ltd.
* @link https://www.dionysopoulos.me/book/plg.html
*/
defined('_JEXEC') || die;

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 Joomla\Database\DatabaseInterface;
use Acme\Plugin\System\Example\Extension\Example;

return new class implements ServiceProviderInterface {
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container)
            {
                $config  = (array)PluginHelper::getPlugin('system', 'example');
                $subject = $container->get(DispatcherInterface::class);

                $app = Factory::getApplication();

                /** @var \Joomla\CMS\Plugin\CMSPlugin $plugin */
                $plugin = new Example($subject, $config);
                $plugin->setApplication($app);
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
```

Также обратите внимание на изменения в Joomla 4.4, в них классы модулей и плагинов становятся `final`, добавляется использование `trait`. В целом на работу Вашего кода это не должно влиять.

![Скриншот кода Joomla со страницы гитхаба](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/8.webp)

### Файл класса плагина

Это файл, в котором содержится основной рабочий код Вашего плагина. Он должен находится в папке `src/Extension`. Регистр названия имеет значение: `Extension` и `extension` - 2 разных папки.

Если Вы адаптируете старый плагин Joomla для работы в Joomla 4, то нужно сделать:

- перенести php-файл плагина в папку `src/Extension`
- изменить имя файла так, чтобы оно начиналось с заглавной буквы - `Wt_amocrm_radicalform`,
- изменить имя класса с `PlgSystemWt_amocrm_radicalform` на `Wt_amocrm_radicalform`. Вся строка будет вида `class Wt_amocrm_radicalform extends CMSPlugin`
- в самом начале файла указать его `namespace` - `namespace Joomla\Plugin\System\Wt_amocrm_radicalform\Extension;`
- проверить, чтобы все названия событий (функций в плагине) начинались с `"on"` - `onBeforeCompileHead`, `onAfterRender` и т.д.

#### Пример кода класса плагина Joomla 4

Если Ваш плагин был написан "по технологиям" Joomla 3.x, с использованием `namespaces`, то в большинстве случаев он будет работать на Joomla 4. Если же плагин использовал код, работавший ещё в 1.5-1.6 и бывший устаревшим уже в Joomla 3, то, скорее всего, плагин нужно будет пересобрать.

```
<?php
/**
 * @package     WT Amocrm - Radical From
 * @version     1.0.0
 * @Author      Sergey Tolkachyov, https://web-tolk.ru
 * @copyright   Copyright (C) 2022 Sergey Tolkachyov
 * @license     GNU/GPL3
 * @since       1.0
 */

// No direct access

namespace Joomla\Plugin\System\Wt_amocrm_radicalform\Extension;

defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Factory;
use Joomla\CMS\Date\Date;
use Webtolk\Amocrm\Amocrm;

class Wt_amocrm_radicalform extends CMSPlugin
{

	/**
	 * Добавляем js-скрпиты на HTML-фронт
	 *
	 * @throws \Exception
	 * @since 1.0.0
	 */
	function onAfterDispatch()
	{
		// We are not work in Joomla API or CLI ar Admin area
		if (!Factory::getApplication()->isClient('site')) return;

		$doc = Factory::getApplication()->getDocument();
		// We are work only in HTML, not JSON, RSS etc.
		if (!($doc instanceof \Joomla\CMS\Document\HtmlDocument))
		{
			return;
		}

		$wa = $doc->getWebAssetManager();
		// Show plugin version in browser console from js-script for UTM
		$wt_amocrm_radicalform_plugin_info = simplexml_load_file(JPATH_SITE . "/plugins/system/wt_amocrm_radicalform/wt_amocrm_radicalform.xml");
		$doc->addScriptOptions('plg_system_wt_amocrm_radicalform_version', (string) $wt_amocrm_radicalform_plugin_info->version);
		$wa->registerAndUseScript('plg_system_wt_amocrm_radicalform.wt_amocrm_radicalform_utm', 'plg_system_wt_amocrm_radicalform/wt_amocrm_radicalform_utm.js', array('version' => 'auto', 'relative' => true));

	}
  }
```

## Новая система событий для плагинов в Joomla 4

**Кратко:** в Joomla 4 реализована новая система событий, основанная на `SubscriberInterface` .Класс плагина реализует `\Joomla\Event\SubscriberInterface`, Вы устанавливаете свойство `protected $this->allowLegacyListeners = false;`. Это изменяет способ, которым Joomla регистрирует обработчики событий. В этом режиме Joomla не будет использовать рефлексию и искать публичные методы, чьи имена начинаются с `on`. Вместо этого она вызовет публичный статический метод `getSubscribedEvents`, который определен в интерфейсе и должен быть реализован в классе вашего плагина. Этот класс вернёт массив с маппингом событий и методов Вашего плагина. При этом внутри Вашего плагина методы уже не обязаны начинаться с `on` и вообще могут иметь любое (в разумных пределах) название. Отсутствие необходимости проходить через [рефлексию](https://www.php.net/manual/ru/book.reflection.php)экономит *много* времени при загрузке каждой страницы сайта. Кроме того, события представляют собой автономные объекты, которые передаются, что снижает накладные расходы на вызов каждого обработчика событий. К ним добавляются десятки и сотни плагинов и обработчиков событий, работающих на типичном сайте Joomla, что экономит от нескольких десятков до нескольких сотен миллисекунд времени загрузки страницы. Это значительное улучшение производительности сайта.

**Подробно:** это потребует отдельной статьи 😀

Пример кода плагина, использующего SubscriberInterface в Joomla 4.

```
<?php
namespace Joomla\Plugin\Quickicon\Joomlaupdate\Extension;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\Module\Quickicon\Administrator\Event\QuickIconsEvent;

class Joomlaupdate extends CMSPlugin implements SubscriberInterface
{

    /**
     * Returns an array of events this subscriber will listen to.
     *
     * @return  array
     *
     * @since   4.0.0
     */
    public static function getSubscribedEvents(): array
    {
        return [
            'onGetIcons' => 'getCoreUpdateNotification',
        ];
    }

    /**
     * This method is called when the Quick Icons module is constructing its set
     * of icons. You can return an array which defines a single icon and it will
     * be rendered right after the stock Quick Icons.
     *
     * @param   QuickIconsEvent  $event  The event object
     *
     * @return  void
     *
     * @since   4.0.0
     */
    public function getCoreUpdateNotification(QuickIconsEvent $event)
    {

    }
}
```

Это несколько сокращённый код плагина, который добавляет иконку с уведомлениями о новых версиях Joomla, код можно посмотреть здесь: `plugins/quickicon/joomlaupdate/src/Extension/Joomlaupdate.php`.

**Что здесь происходит?** Плагин реализует `SubscriberInterface` и имеет статический публичный метод `getSubscribedEvents`, который возвращает массив сопоставления событий и методов плагина. Обратите внимание, что по событию `onGetIcons` будет вызван не метод плагина `onGetIcons` (его там и нет вообще), а метод `getCoreUpdateNotification`.

Ещё один пример кода ([источник](https://www.dionysopoulos.me/book/plg.html#plg-forms-j4-subscriberinterface)):

```
<?php
public static function getSubscribedEvents(): array
{
    return [
        'onSomething'     => 'doSomething',
        'onSomethingElse' => ['doSomething', \Joomla\Event\Priority::HIGH],
    ];
}
```

`onSomething` - некое событие, вызывающее метод нашего плагина `doSomething`. `onSomethingElse` - другое событие. которое вызывает метод нашего плагина `doSomething` и устанавливает исполнению этого метода приоритет.

Да-да, теперь можно влиять на порядок выполнения не только путём перетаскивания плагинов мышкой в админке, но и **устанавливать приоритет выполнения** прямо в коде. Подробнее читаем пока что в вышеуказанном источнике.

#### Небольшое "но"...

Ядро Joomla 4 (4.3.1 на момент написания статьи) до сих пор ещё содержит старые вызовы плагинов. Поэтому, перед тем, как переписать плагин на новую структуру - убедитесь, что событие плагина вызывается по-новому. Это будет выглядеть в коде примерно так:

```
<?php
$event = \Joomla\CMS\Event\AbstractEvent::create('onSomething', [$param1 => $value1, $param2 => $value2]);
$this->getDispatcher()->dispatch($event->getName(), $event);
$results = $event->getArgument('result', []);
```

Для сравнения, старый способ вызова событий выглядел примерно таким образом: `$app->triggerEvent('onSomething', [$param1, $param2])`.

Самый простой способ выяснить как образом вызывается событие для плагина - выполнить поиск по текстовому содержимому движка (если Вы работаете без IDE), а затем найти вызов в файле

 ![Поиск по текстовому содержимому файлов php](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/9.webp)Поиск по названию события в коде Joomla 4. ![Поиск вызова метода плагина в коде файла.](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/10.webp)Поиск вызова метода плагина в коде файла.

Для примера, я решил перевести один из своих плагинов для JoomShopping на новую структуру Joomla 4. Немного почитав код ядра и документацию стал пробовать новый метод, но на JoomShopping он не завёлся, так как JoomShopping - это не ядро Joomla, а сторонний компонент и вызовы событий в нём реализуются пока что старыми методами. В этом плане, Вы как разработчик зависите от компонента, под который Вы разрабатываете плагин. Будьте внимательны и осторожны 😀.

**UPD. 16.09.2023:** Возможно использовать в плагине новую систему вызовов и получать в качестве аргумента объект `Event $event`, но если при этом если событие плагина вызывается по-старому, то обращение к параметрам будет идти по числовому индексу:

```
<?php
$param1 = $event->getArgument(0);
$param2 = $event->getArgument(1);
```

**Upd. 29.03.2024.** Возврат значений:

**Вариант 1.**

Мы должны получить массив с результатами работы плагинов, добавить туда свой результат и положить всё обратно.

```
<?php
// Пример из плагина QuickIcon админки

// Add the icon to the result array
  $result = $event->getArgument('result', []);

  $result[] = [
      [
          'link'  => $privacy . '&view=requests&filter[status]=1&list[fullordering]=a.requested_at ASC',
          'image' => 'icon-users',
          'icon'  => '',
          'text'  => $this->getApplication()->getLanguage()->_('PLG_QUICKICON_PRIVACYCHECK_CHECKING'),
          'id'    => 'plg_quickicon_privacycheck',
          'group' => 'MOD_QUICKICON_USERS',
      ],
  ];

  $event->setArgument('result', $result);
```

Нужно помнить, что этот способ помечен в коде Joomla 4.x как устаревший (deprecated) и будет удалён в Joomla 6. Речь идёт именно о *setArgument(****'result'****, $result).*

**Вариант 2. Назначение аргументов по индексу.**

Как мы их получали, так и возвращаем.

```
<?php

$param1 = $event->getArgument(0);
$param2 = $event->getArgument(1);

// Что-то с ними делаем....

$event->setArgument(0, $param1);
$event->setArgument(1, $param1);
```

**Вариант 3. Использование метода $event->addResult().**

Этот метод был добавлен в Joomla 4.2. Он берёт на себя работу, описанную выше.

```
<?php

// результат работы плагина
$result = $this->onCustomFieldsGetTypes();

if ($result) {
    $event->addResult($result);
}
```

Сами методы плагина (кроме `getSubscribedEvents()`) должны возвращать пустоту - `void`.

## Работа с Ajax в плагинах Joomla 4

В целом, в этом плане ничего не изменилось - среди методов плагина должен быть метод с префиксом `onAjax`, например `onAjaxDebug`. В случае, если Вы используете новый `SubscriberInterface`, то в маппинг нужно добавить событие и корреспондирующий ему метод в плагине:

 ![Пример метода для получения данных из плагина по ajax в Joomla 4](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/11.webp)Пример метода для получения данных из плагина по ajax в Joomla 4

Пример ajax-запроса, реализованного нативными средствами Joomla ([статья-мануал](https://web-tolk.ru/blog/index.php?option=com_content&view=article&id=46:ajax-zaprosy-nativnymi-sredstvami-joomla-frontend&catid=10:blog&lang=ru-RU&Itemid=114)).

```
Joomla.request({
				url: window.location.origin + "/index.php?option=com_ajax&plugin=yourplugin&group=system&format=raw",
				onSuccess: function (response, xhr){
                     if (response !== ""){
    					// Здесь делаем то, что нужно
                     }
				}
			});

		}
```

### Свои типы полей Joomla для плагина

Поля Joomla Form указываются в XML-манифесте и представляют собой удобный и быстрый конструктор интерфейса. Как и в Joomla 3, в Joomla 4, если Вам не хватает [стандартных типов полей](https://docs.joomla.org/Standard_form_field_types), у Вас есть возможность создавать свои типы полей. Это могут быть нестандартные выборки из базы данных, получение значений списка из сторонних сервисов по API и т.д.

Возможность создавать свои пользовательские типы полей открывает широкие возможности Joomla. Наглядный пример про Битрикс 24 приводил [в статье про написание модулей по структуре Joomla 4](https://habr.com/ru/articles/684534/) (хотя пример как раз из плагина... )).

#### Joomla 3

В Joomla 3 Вам надо было указать свой тип поля и назначить атрибут `addfieldpath` родительскому `<fieldset>` или напрямую `<field>`. Например

```
<field addfieldpath="plugins/system/wt_amocrm_radicalform/fields" type="plugininfo" name="plugininfo"/>
```

Php-файл поля находился в папке с плагином `plugins/system/wt_amocrm_radicalform/fields`.

#### Joomla 4

В Joomla 4 атрибут `addfieldpath` не работает. Вместо него используется атрибут `addfieldprefix`, в котором нужно указать **namespace для пользовательских полей плагина**.

 ![Собственные типы полей в Joomla 4](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/12.webp)Собственные типы полей в Joomla 4

Поля мы складываем в `src/Fields`. У файлов полей должен быть namespace `namespace Joomla\Plugin\System\Wt_amocrm_radicalform\Fields`. Я использую собственный тип поля, расширяющий тип поля spacer (пробел), для вывода своего логотипа, версии плагина, ссылки на сайт и иногда дополнительной информации.

К слову сказать, классы дополнительных типов полей могут находиться и не в папке с плагином, а, например, быть "в комплекте" с библиотекой и физически находиться в папке `libraries`. Но, указав нужный `namespace` в атрибуте `addfieldprefix` мы можем легко их использовать.

 ![В этом примере первое поле field загружается из папки с плагином, а второе поле - из библиотеки WT AmoCRM.](https://web-tolk.ru/blog/images/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4/13.webp)В этом примере первое поле <field> загружается из папки с плагином, а второе поле - из библиотеки WT AmoCRM.

### Полезные дополнения

#### Об использовании \Joomla\CMS\Factory

Из статьи [Распространенные ошибки при написании плагинов Joomla 4](https://habr.com/ru/post/677262/)

Вы должны использовать ТОЛЬКО ДВА метода `\Joomla\CMS\Factory` в Joomla 4:

- `getContainer()` - возвращает контейнер внедрения зависимостей Joomla (DI Container, иногда сокращенно DIC).
- `getApplication()` - возвращает текущий объект приложения Joomla, обрабатывающий запрос.

Всё. Больше ничего другого использовать не нужно! Всё остальное предоставляется либо через DI-контейнер, либо через сам объект приложения.

Чтобы получить документ приложения используйте `\Joomla\CMS\Factory::getApplication()->getDocument()`.

#### Правильное подключение CSS и JS в Joomla 4

1. Статья [Как правильно подключать JavaScript и CSS в Joomla 4](https://jpath.ru/docs/output/js-css/kak-pravilno-podklyuchat-javascript-i-css-v-joomla-4)
2. Статья на хабре [Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина](https://habr.com/ru/post/672020/). Мы помним, что все CSS и JS файлы должны лежать в папке `media`. Подробнее в статьях.
3. Статья на хабре [Создание модулей с учётом новой структуры Joomla 4](https://habr.com/ru/articles/684534/)

#### Замена для популярных, но устаревших методов

Многие из этих методов работали ещё со времен Joomla 1.5 (с 2008 года!).

- `JRequest::getUri()` заменяем на $uri = `Joomla\CMS\Uri::getInstance()` и читаем документацию к нему.
- методы `JRequest::getCmd` и аналогичные перекочевали в `Joomla\Input\Input` или (что проще) `$app->getInput()`. Пока что поддерживается устаревший синтаксис `$app->input`, но в Joomla 5 (выйдет осенью 2023 года) он может быть удалён ([план выпуска релизов и принципы удаления устаревшего кода в Joomla](https://habr.com/ru/news/t/686224/)).
- `$app->isAdmin()` и `$app->isSite()` стали `$app->isClient('Site')` и `$app->isClient('Administrator')`.
- **Подключение к базе данных:** вместо `JFactory::getDbo()` (или `Joomla\CMS\Factory::getDbo`) используем `$app->getContainer()->get('DatabaseDriver')` ('DatabaseDriver' регистрозависимый). Или же `use Joomla\Database\DatabaseInterface;` в начале файла в секции с `use`. И `Factory::getContainer()->get(DatabaseInterface::class)`.
- **Получение объекта пользователя:** вместо `JFactory::getUser()` (или `Joomla\CMS\Factory::getUser()`*)*используем `$app->getIdentity()`
- **Получение языка:** вместо `Factory::getLanguage()` используем `Factory::getApplication()->getLanguage();`
- **Список соответствий названий старых классов Joomla и новых** (алиасы) находится в `libraries/classmap.php`.

- [Первоначальная публикация статьи в моём блоге на Хабре](https://habr.com/ru/articles/736412/)

## Об авторе

![Толкачев Сергей Юрьевич](https://web-tolk.ru/images/uslugi/sergey-tolkachyov-apr-2023.webp)

### Толкачев Сергей Юрьевич

Joomla-разработчик. [Контрибьютер ядра Joomla](https://github.com/joomla/joomla-cms/pulls?q=is%3Apr+author%3Asergeytolkachyov+). Один из ведущих Telegram-канала русскоязычного Joomla-сообщества [JoomlaFeed](https://t.me/joomlafeed), один из модераторов [чата русскоязычного Joomla-сообщества](https://t.me/joomlaru). Мои расширения в официальном маркетплейсе расширений Joomla - [Joomla Extensions Directory](https://extensions.joomla.org/profile/profile/details/528051/). Имею публикации в [официальном журнале международного Joomla-сообщества - Joomla Community Magazine](https://magazine.joomla.org/authors/sergeytolkachyov) и на [официальном сайте русскоязычного Joomla-сообщества](https://joomlaportal.ru/users/sergey-tolkachyov).

Муж. Отец 3 детей.

Россия, Саратов.

## JSON-LD Schema

```json
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "@id": "https://web-tolk.ru/#/schema/BreadcrumbList/17",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": 1,
            "item": {
                "@id": "https://web-tolk.ru/",
                "name": "Главная"
            }
        },
        {
            "@type": "ListItem",
            "position": 2,
            "item": {
                "@id": "https://web-tolk.ru/blog",
                "name": "Блог"
            }
        },
        {
            "@type": "ListItem",
            "position": 3,
            "item": {
                "name": "Создание плагинов с учётом новой структуры Joomla 4"
            }
        }
    ]
}
```

```json
{
    "@context": "https://schema.org",
    "@graph": [
        {
            "@type": "Organization",
            "@id": "https://web-tolk.ru/#/schema/Organization/base",
            "name": "WebTolk",
            "url": "https://web-tolk.ru/",
            "logo": {
                "@type": "ImageObject",
                "@id": "https://web-tolk.ru/#/schema/ImageObject/logo",
                "url": "images/webtolk-1080p.jpg",
                "contentUrl": "images/webtolk-1080p.jpg",
                "width": 1920,
                "height": 1080
            },
            "image": {
                "@id": "https://web-tolk.ru/#/schema/ImageObject/logo"
            },
            "sameAs": [
                "https://github.com/WebTolk",
                "https://github.com/sergeytolkachyov",
                "https://vk.com/web_tolk",
                "https://vk.com/webtolkru",
                "https://tenchat.ru/sergeytolkachyov",
                "https://t.me/sergeytolkachyov",
                "https://t.me/webtolkru"
            ]
        },
        {
            "@type": "WebSite",
            "@id": "https://web-tolk.ru/#/schema/WebSite/base",
            "url": "https://web-tolk.ru/",
            "name": "WebTolk",
            "publisher": {
                "@id": "https://web-tolk.ru/#/schema/Organization/base"
            }
        },
        {
            "@type": "WebPage",
            "@id": "https://web-tolk.ru/#/schema/WebPage/base",
            "url": "https://web-tolk.ru/blog/sozdanie-plaginov-s-uchjotom-novoj-struktury-joomla-4",
            "name": "Создание плагинов с учётом новой структуры Joomla 4 - WebTolk",
            "description": "Статья мануал по созданию и обновлению плагинов Joomla до новой архитектуры Joomla 4, чтобы они работали и на Joomla 5 и старше.",
            "isPartOf": {
                "@id": "https://web-tolk.ru/#/schema/WebSite/base"
            },
            "about": {
                "@id": "https://web-tolk.ru/#/schema/Organization/base"
            },
            "inLanguage": "ru-RU",
            "breadcrumb": {
                "@id": "https://web-tolk.ru/#/schema/BreadcrumbList/17"
            }
        },
        {
            "@type": "Article",
            "@id": "https://web-tolk.ru/#/schema/com_content/article/80",
            "isPartOf": {
                "@id": "https://web-tolk.ru/#/schema/WebPage/base"
            }
        }
    ]
}
```
