Система событий в Битрикс является одним из краеугольных камней платформы, позволяющим расширять функциональность без вмешательства в ядро. Правильное использование событий позволяет создавать гибкие, расширяемые веб-сайты, которые легко адаптировать под изменяющиеся требования бизнеса. В этой статье мы рассмотрим типовые события Битрикс, методы подписки на них, создание собственных событий и лучшие практики для организации эффективной event-driven архитектуры на вашем веб-сайте.
Основные группы событий:
1. Модуль Main (главный модуль) https://dev.1c-bitrix.ru/api_help/main/events/index.php
- События, связанные с пользователями (OnBeforeUserAdd, OnAfterUserAdd, OnBeforeUserUpdate, и т.д.)
- События авторизации (OnBeforeUserLogin, OnAfterUserLogin)
- События работы с файлами (OnFileSave, OnFileDelete)
- События кеширования (OnBeforeEndBufferContent, OnEndBufferContent)
2. Модуль Iblock (информационные блоки) https://dev.1c-bitrix.ru/api_help/iblock/events/index.php
- События элементов (OnBeforeIBlockElementAdd, OnAfterIBlockElementAdd, и т.д.)
- События разделов (OnBeforeIBlockSectionAdd, OnAfterIBlockSectionAdd, и т.д.)
3. Модуль Sale (интернет-магазин)
https://dev.1c-bitrix.ru/api_d7/bitrix/sale/events/order_saved.php
https://dev.1c-bitrix.ru/api_d7/bitrix/sale/events/delivery_events.php
- События заказов (OnSaleOrderSaved, OnSaleOrderCanceled, OnSaleOrderBeforeSaved и т.д.)
- События корзины (OnSaleBasketBeforeSaved, OnSaleBasketSaved, OnSaleBasketItemRefreshData)
- События доставки (onSaleDeliveryServiceCalculate)
4. Модуль Catalog (Торговый каталог)
https://dev.1c-bitrix.ru/api_help/catalog/events/index.php
- События товаров (OnProductAdd, OnProductUpdate)
- События цен (OnPriceAdd, OnPriceUpdate)
Как найти список всех событий
Для поиска всех зарегистрированных событий в системе можно использовать таблицу b_module_to_module в базе данных. Эта таблица содержит информацию о всех зависимостях между модулями, включая зарегистрированные обработчики событий.
SQL-запрос для получения списка всех событий и их обработчиков:
SELECT
FROM_MODULE_ID as "Модуль источник",
MESSAGE_ID as "Событие",
TO_MODULE_ID as "Модуль обработчик",
TO_CLASS as "Класс",
TO_METHOD as "Метод",
SORT as "Сортировка"
FROM
b_module_to_module
ORDER BY
FROM_MODULE_ID, MESSAGE_ID, SORT
Также можно посмотреть список событий по ссылке вашего проекта:
{домен}/bitrix/admin/perfmon_table.php?lang=ru&table_name=b_module_to_module
Подписка на события в Битрикс
В Битрикс существует два способа подписки на события:
1. Классический способ (через RegisterModuleDependences)
Этот метод используется в старом API Битрикс:
// В файле регистрации обработчиков событий
RegisterModuleDependences(
"main", // Модуль, генерирующий событие
"OnBeforeUserAdd", // Имя события
"local.handlers", // Идентификатор модуля обработчика (произвольное имя)
"UserHandlers", // Имя класса обработчика
"OnBeforeUserAdd" // Имя метода обработчика
);
2. Современный способ (через EventManager в D7)
Этот метод используется в новом API Битрикс (D7):
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
"main", // Модуль, генерирующий событие
"OnBeforeUserAdd", // Имя события
["\Local\Handlers\UserHandlers", "onBeforeUserAdd"] // Обработчик (класс и метод)
);
Правильная структура для организации обработчиков
Даже для небольших проектов рекомендуется использовать структурированный подход к организации обработчиков событий. Файл init.php должен оставаться чистым и содержать только необходимые подключения:
<?php
// /local/php_interface/init.php
// Подключаем автозагрузчик классов
require_once __DIR__ . '/autoload.php';
// Подключаем файл регистрации обработчиков событий
require_once __DIR__ . '/events.php';
Пример организации файловой структуры:
/local/
/php_interface/
/init.php - Минимальный код с подключениями
/events.php - Регистрация всех обработчиков событий
/autoload.php - Автозагрузчик классов (если не используется composer)
/include/
/handlers/ - Классы обработчиков событий
/Main/
UserHandlers.php
/Iblock/
ElementHandlers.php
Для средних и крупных проектов:
Для более сложных проектов рекомендуется использовать более детальную структуру:
/local/
/php_interface/
/init.php - Минимальный код с подключениями
/events/ - Регистрация обработчиков
/main.php - События главного модуля
/iblock.php - События инфоблоков
/sale.php - События модуля продаж
/include/
/handlers/ - Классы обработчиков
/Main/
UserHandlers.php
/Iblock/
ElementHandlers.php
/Sale/
OrderHandlers.php
В файле init.php подключаем директорию с регистрацией событий:
<?php
// Подключаем автозагрузчик классов
require_once __DIR__ . '/autoload.php';
// Подключаем файлы с регистрацией обработчиков
$eventFiles = glob(__DIR__ . '/events/*.php');
foreach ($eventFiles as $eventFile) {
require_once($eventFile);
}
Приоритеты обработчиков событий
При регистрации нескольких обработчиков на одно событие, можно управлять порядком их выполнения с помощью параметра сортировки:
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\Main\UserHandlers", "onBeforeUserAdd"],
100 // Высокий приоритет (выполнится раньше)
);
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\Main\UserHandlers", "anotherHandler"],
200 // Более низкий приоритет (выполнится позже)
);
Чем меньше значение, тем выше приоритет (обработчик выполнится раньше). По умолчанию приоритет равен 100.
Создание собственных событий
Для создания собственных событий в Битрикс можно использовать как классический, так и новый D7 подход:
Классический подход:
// Генерация события
$rsEvents = GetModuleEvents("custom", "OnBeforeNewsShow");
while ($arEvent = $rsEvents->Fetch()) {
$result = ExecuteModuleEventEx($arEvent, [$newsID, &$arNews]);
if ($result === false) {
// Если обработчик вернул false, прекращаем выполнение
return false;
}
}
// Отображение новости
// ...
Подход D7:
use Bitrix\Main\Event;
use Bitrix\Main\EventResult;
// Создаем объект события
$event = new Event("custom", "OnBeforeNewsShow", [
"NEWS_ID" => $newsID,
"NEWS_DATA" => $arNews
]);
// Отправляем событие
$event->send();
// Обрабатываем результаты
$results = $event->getResults();
$modified = false;
foreach ($results as $result) {
if ($result->getType() == EventResult::ERROR) {
// Обработка ошибки
return false;
} elseif ($result->getType() == EventResult::SUCCESS) {
// Получение модифицированных данных
$modified = true;
$modifiedParams = $result->getParameters();
if (isset($modifiedParams["NEWS_DATA"])) {
$arNews = $modifiedParams["NEWS_DATA"];
}
}
}
// Отображение новости
// ...
Примеры использования событий в типовых задачах сайта
Пример 1: Добавление дополнительной информации в карточку товара
// Файл: /local/php_interface/events/catalog.php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
"catalog",
"OnGetOptimalPrice",
["\Local\Handlers\Catalog\PriceHandlers", "onGetOptimalPrice"]
);
// Файл: /local/php_interface/include/handlers/Catalog/PriceHandlers.php
namespace Local\Handlers\Catalog;
use Bitrix\Main\EventResult;
use Bitrix\Main\Event;
class PriceHandlers
{
public static function onGetOptimalPrice ($productID, $quantity, $arUserGroups, $renewal, $arPrices, $siteID, $arDiscountCoupons)
{
$arInfoProduct = CCatalogProduct::GetByIDEx($productID);
// Изменяем цену, если она больше 1000
if ($arinfo && $arinfo["PRICES"]) {
foreach ($arinfo["PRICES"] as $arPrice) {
if ($arPrice["PRICE"] > 1000) {
return array(
'PRICE' => array(
"ID" => $productID,
'CATALOG_GROUP_ID' => 1,
'PRICE' => 790,
'CURRENCY' => "RUB",
'ELEMENT_IBLOCK_ID' => $productID,
'VAT_INCLUDED' => "Y",
),
);
}
}
}
}
}
Пример 2: Отправка уведомления при изменении статуса заказа
// Файл: /local/php_interface/events/sale.php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
"sale",
"OnSaleStatusOrderChange",
["\Local\Handlers\Sale\OrderHandlers", "onStatusChange"]
);
// Файл: /local/php_interface/include/handlers/Sale/OrderHandlers.php
namespace Local\Handlers\Sale;
use Bitrix\Main\Event;
class OrderHandlers
{
public static function onStatusChange(Event $event)
{
$order = $event->getParameter("ENTITY");
$newStatus = $order->getField("STATUS_ID");
// Отправка SMS при отправке заказа
if ($newStatus == "S") { // "Отправлен"
$phone = $order->getPropertyCollection()->getPhone()->getValue();
$orderNumber = $order->getField("ACCOUNT_NUMBER");
if ($phone) {
self::sendSMS(
$phone,
"Ваш заказ №{$orderNumber} отправлен. Отслеживайте статус в личном кабинете."
);
}
}
}
private static function sendSMS($phone, $message)
{
// Код отправки SMS через выбранный сервис
// ...
}
}
Пример 3: Модификация результатов поиска
// Файл: /local/php_interface/events/search.php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
"search",
"BeforeIndex",
["\Local\Handlers\Search\SearchHandlers", "beforeIndex"]
);
// Файл: /local/php_interface/include/handlers/Search/SearchHandlers.php
namespace Local\Handlers\Search;
class SearchHandlers
{
public static function beforeIndex($arFields)
{
// Исключаем из индекса страницы с определенным тегом
if ($arFields["MODULE_ID"] == "iblock") {
$elementId = $arFields["ITEM_ID"];
// Проверяем, есть ли тег "noindex" у элемента
$tags = \CIBlockElement::GetPropertyValues(
$arFields["PARAM2"],
["ID" => $elementId],
false,
["CODE" => "TAGS"]
)->Fetch();
if ($tags && strpos($tags["TAGS"], "noindex") !== false) {
return false; // Исключаем из индекса
}
}
return $arFields;
}
}
Лучшие практики использования событий в Битрикс
1. Документируйте события и их параметры
Создайте документацию для всех созданных вами событий:
/**
* Событие генерируется перед отображением новости.
*
* @event OnBeforeNewsShow
* @param int $newsId ID новости
* @param array &$arNews Массив с данными новости, может быть изменен обработчиком
* @return mixed Если обработчик вернет false, отображение новости будет отменено
*/
$rsEvents = GetModuleEvents("custom", "OnBeforeNewsShow");
2. Используйте структурированные имена событий
Следуйте общепринятым соглашениям об именовании:
- Префикс On указывает на событие
- Следующая часть Before/After указывает на момент генерации события
- Далее идет название действия: Add, Update, Delete, Save, и т.д.
- В конце указывается объект: Element, Section, User, Order, и т.д.
Примеры:
- OnBeforeUserAdd
- OnAfterIBlockElementUpdate
- OnSaleOrderSaved
3. Правильно обрабатывайте результаты событий
При генерации собственных событий, всегда проверяйте и обрабатывайте их результаты:
// Генерация события
$event = new Event("custom", "OnBeforeNewsShow", ["NEWS_ID" => $newsID]);
$event->send();
// Обработка результатов
$continue = true;
foreach ($event->getResults() as $result) {
if ($result->getType() == EventResult::ERROR) {
$continue = false;
break;
}
}
if (!$continue) {
// Прекращаем выполнение
return;
}
4. Группируйте логически связанные обработчики в отдельные классы
Создавайте отдельные классы для обработчиков по функциональным группам:
/local/php_interface/include/handlers/
/User/
UserRegistrationHandlers.php - Обработчики, связанные с регистрацией
UserProfileHandlers.php - Обработчики, связанные с профилем
/Catalog/
PriceHandlers.php - Обработчики, связанные с ценами
StockHandlers.php - Обработчики, связанные с наличием
5. Используйте приоритеты для контроля порядка выполнения
Если несколько обработчиков должны выполняться в определенном порядке, используйте приоритеты:
// Обработчик валидации данных (выполняется первым)
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\User\UserValidationHandlers", "validate"],
10 // Высокий приоритет
);
// Обработчик обогащения данных (выполняется вторым)
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\User\UserEnrichmentHandlers", "enrich"],
20 // Средний приоритет
);
// Обработчик логирования (выполняется последним)
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\User\UserLoggingHandlers", "log"],
30 // Низкий приоритет
);
6. Обрабатывайте исключения в обработчиках
Предотвратите прерывание цепочки обработчиков из-за исключений:
public static function onBeforeUserAdd(Event $event)
{
try {
// Логика обработчика
return new EventResult(EventResult::SUCCESS);
} catch (\Exception $e) {
// Логирование ошибки
\CEventLog::Add([
"SEVERITY" => "ERROR",
"AUDIT_TYPE_ID" => "EVENT_HANDLER_EXCEPTION",
"MODULE_ID" => "main",
"DESCRIPTION" => "Ошибка в обработчике OnBeforeUserAdd: " . $e->getMessage()
]);
// Продолжаем цепочку обработчиков
return new EventResult(EventResult::SUCCESS);
}
}
7. Отладка событий
Для отладки событий и их обработчиков можно использовать встроенные инструменты:
// Временно включаем вывод всех событий в лог
define("BX_SECURITY_SHOW_MESSAGE", true);
// В файле .settings.php
return [
"exception_handling" => [
"value" => [
"debug" => true,
"log" => [
"settings" => [
"file" => "/logs/bitrix-events.log",
"log_size" => 1000000
]
]
],
"readonly" => false
]
];
Для более детальной отладки можно создать специальный класс логирования:
// Файл: /local/php_interface/include/handlers/Debug/EventLogger.php
namespace Local\Handlers\Debug;
use Bitrix\Main\Event;
class EventLogger
{
public static function logUserEvents(Event $event)
{
$params = $event->getParameters();
file_put_contents(
$_SERVER["DOCUMENT_ROOT"]."/log/user_events.log",
date("Y-m-d H:i:s")."\n".
"Событие: ".$event->getEventType()."\n".
"Параметры: ".print_r($params, true)."\n\n",
FILE_APPEND
);
return null;
}
}
// Регистрация в файле /local/php_interface/events/debug.php
use Bitrix\Main\EventManager;
// Только в режиме разработки
if (file_exists($_SERVER["DOCUMENT_ROOT"]."/.debug")) {
EventManager::getInstance()->addEventHandler(
"main",
"OnBeforeUserAdd",
["\Local\Handlers\Debug\EventLogger", "logUserEvents"],
1 // Самый высокий приоритет
);
}
Ключевые моменты:
- Используйте структурированный подход к организации кода, даже для небольших проектов
- Следуйте соглашениям по именованию при создании собственных событий
- Используйте приоритеты для контроля порядка выполнения обработчиков
- Обрабатывайте ошибки и исключения в обработчиках
- Документируйте созданные вами события и их параметры
Это позволит не только повысить качество кода, но и упростит работу с проектом для всей команды разработки.
Система событий в Битрикс предоставляет мощный инструмент для расширения функциональности сайта без вмешательства в ядро платформы. Правильное использование событий и обработчиков позволяет создавать гибкие, расширяемые веб-сайты, которые легко адаптировать под изменяющиеся требования бизнеса. Следуя рекомендациям из этой статьи, вы сможете эффективно организовать event-driven архитектуру на своем сайте, что сделает его более модульным, простым в поддержке и расширении.