Триггеры ядра Joomla при CRUD-операциях
CRUD — аббревиатура основных операций с данными: создание (Create), чтение (Read), изменение (Update) и удаление (Delete). Практически все действия на сайте как в админке, так и пользовательской части можно описать этими действиями. Разработчикам при создании функционала часто в какие-то моменты этих действий нужно проделать некие действия с участием данных или без них. Для этого в частности существует механизм вызова событий (Event Dispatching) и плагинов, которые на эти события «откликаются». В названии событий есть определенная логика и здесь я упомяну статью Виталия Некрасова (@VitaliyNekrasov) на Хабре Как событие Joomla назовёшь, так оно и триггернётся?.
Самый простой пример (один момент в процессе Read данных) — триггер onContentPrepare
— вызывается перед отображением какого-либо контента «снаружи» сайта. Обычно плагины, срабатывающие на этом событии заменяют какие-нибудь шорт-коды на какое-нибудь содержимое.
Карта событий плагинов в моделях Joomla
Обработка, проверка и сохранение данных в Joomla происходит с помощью моделей (MVC — Model), которые обычно расширяют родительскую \Joomla\CMS\MVC\Model\AdminModel
(libraries/src/MVC/Model/AdminModel.php). Именно она описывает CRUD-процессы, а модели конкретных компонентов дополняют родительские методы. В AdminModel
есть «карта событий» для плагинов, которая сопоставляет собственно момент времени с названием триггера, а так же говорит какую группу плагинов вызывать.
event_after_delete = $config['event_after_delete'];
} elseif (empty($this->event_after_delete)) {
$this->event_after_delete = 'onContentAfterDelete';
}
// ....
$config['events_map'] = $config['events_map'] ?? [];
// Указываем какая группа плагинов, кроме системных
// будет реагировать на триггер.
$this->events_map = array_merge(
[
'delete' => 'content',
'save' => 'content',
'change_state' => 'content',
'validate' => 'content',
'batch' => 'content',
],
$config['events_map']
);
// ....
}
}
По умолчанию предполагается вызывать на все события плагины группы content
, однако это можно переопределить в конструкторе модели конкретного компонента.
Имена триггеров Joomla по умолчанию, если не переопределены конкретным компонентом:
onContentBeforeDelete
— перед удалением сущностиonContentAfterDelete
— после удаленияonContentBeforeSave
— перед сохранением данных сущностиonContentAfterSave
— после сохраненияonContentBeforeChangeState
— перед изменением состояния (опубликовано, не опубликовано, в корзине…)onContentChangeState
— после изменения состоянияonBeforeBatch
— перед пакетной обработкой нескольких сущностей
По умолчанию это триггеры, содержащие слово «Content» в названии. Но если мы посмотрим в конструкторы моделей некоторых компонентов ядра, то увидим, что они вызывают собственные более узко-специализированные триггеры. Например, компоненты модулей (com_modules
) и шаблонов (com_templates
) вызывают собственные события onExtensionBeforeDelete
, onExtensionAfterDelete
, onExtensionBeforeSave
, onExtensionAfterSave
и группа плагинов — extension
. Или же модель \Joomla\Component\Users\Administrator\Model\UserModel
(administrator/components/com_users/src/Model/UserModel.php) определяет события onUserAfterDelete
, onUserAfterSave
, onUserBeforeDelete
, onUserBeforeSave
в группе плагинов user
.
Таким образом, работая с компонентами Joomla всегда можно посмотреть какое именно событие и какой именно группы плагинов можно использовать. Другое дело, что на практике нередко лень создавать несколько плагинов под разные случаи и множество событий помещают в один системный плагин, чтобы всё было в одном месте. Недостаток такого подхода в том, что системные плагины срабатывают на каждый чих в Joomla и поэтому велик риск сломать что-то абсолютно далёкое от зоны ответственности вашего плагина.
Данные для использования при срабатывании плагина
Какие данные приходят и как с ними работать? Здесь предварительно упомяну ещё одну статью Виталия Некрасова — Каждому событию Joomla — свой класс. Также я кратко (но подробнее, чем тут) писал об этом в статье Программное создание материалов с пользовательскими полями в Joomla 5+. В Joomla 5 для разных типов событий стали создавать кастомные классы этих событий для того, чтобы можно было использовать методы работы с данными, характерными для этих только событий. Для контентных плагинов — $event->getContext()
вместо получения $event->getArgument(0);
или $event->updateEventResult($data)
для AjaxEvent
для и т.д.
Получение данных события плагина Joomla
Вариант 1. По числовому индексу аргумента. Устаревший, но в Joomla 5 ещё работающий. Тут нужно знать порядок аргументов для данного события, исторически сложившийся в Joomla.
// public function onContentAfterDisplay($context, &$row, &$params, $page = 0)
$context = $event->getArgument(0);
Вариант 2. Методом getArguments ().
Здесь тоже нужно знать порядок аргументов. Но если вы его знаете, почему бы и нет? Этот способ можно использовать и тогда, когда компонент, с которым вы работаете не предоставляет собственные классы для событий.
/**
* Пример из плагина для интернет-магазина JoomShopping
* @var object $order JoomShopping order object
* @var object $cart JoomShopping cart object
*/
[$order, $cart] = $event->getArguments();
Вариант 3. Методами класса события (Joomla 5+).
Метод класса события предлагает и геттеры и сеттеры — получение и сохранение данных.
public function onContentBeforeDisplay(BeforeDisplayEvent $event):void
{
$context = $event->getContext();
// Тут возвращаем результаты
$event->addResult($this->showLikeButton($context, $event->getItem(), $event->getParams(), $event->getPage()));
}
Контекст вызова плагина Joomla
Событие одно, а компонентов — много. CRUD-ы разные. Чтобы плагин отличал их в событие передаётся контекст вызова. Обычно это строка вида
. Например:
com_menus.item
при сохранении пункта менюcom_contact.contact
при сохранении контактаcom_media.file
при сохранении загруженного файла в медиа менеджереcom_content.article
при сохранении материалаи т.д.
Проверяем нужный нам контекст и выполняем необходимую работу.
if($event->getContext() !== 'com_content.article')
{
return;
}
// Тут работаем с данными
Получение данных
Обычно для этого используют метод getItem()
класса плагина. Что именно за итем может оказаться — зависит от компонента и контекста. Это может оказаться объект Table
удаляемого элемента, или объект материала (com_content
), контакта (com_contact
) или категории (com_categories
).
В некоторых случаях метода getItem()
не существует. Например, при вызове триггера onContentChangeState
метода getItems()
нет, но есть методы getPks()
(получение списка id
сущностей, которые удаляют в корзину или снимают с публикации) и getValue()
— устанавливаемый статус. IDE подсказывает какие методы есть у класса события.
Исключения из правил
Когда мы работаем с Joomla, то далеко не всегда встречаемся именно с компонентами ядра, но используем расширения сторонних разработчиков. Кто-то из них следует «Joomla way» и использует функционал ядра, а кто-то наоборот идёт своим путём и в таком случае нужно использовать те события для плагинов, которые предоставляет сам компонент, так как большая часть событий (или вообще все) ядра Joomla не будут работать. В качестве примера приведу компонент-интернет магазина JoomShopping, модели которого наследуют не штатную AdminModel
, а абстрактную BaseDatabaseModel
, которая находится выше по уровню иерархии и в ней не описаны стандартные CRUD-операции, а разработчики JoomShopping сделали их по-своему.
Список всех событий ядра Joomla 4.2+
Его можно найти в трейте \Joomla\CMS\Event\CoreEventAware
(libraries/src/Event/CoreEventAware.php). Если посмотреть на ползунок файла, то будет видно, что событий не мало. Просто информация по теме статьи, полезная тем, у кого ещё нет IDE для работы с Joomla.
Полезные ресурсы
Ресурсы сообщества:
Telegram:
Мой личный Telegram-канал — WebTolkRu.