События — механизм, позволяющий вмешаться в работу 1С-Битрикс, не трогая код ядра. Нужно проверять данные перед сохранением элемента, отправлять уведомление после создания заказа, дописывать поля пользователю при регистрации — всё это делается через обработчики событий.
Разберём, как устроен механизм, чем отличаются события «до» и «после», как отменить
действие из обработчика и почему AddEventHandler постепенно уступает
место подходу из D7.
Как это работает
В ключевых точках ядро вызывает все зарегистрированные обработчики события и передаёт им данные. Обработчик может их прочитать, изменить, а в некоторых случаях — прервать выполнение операции.
Регистрация обработчика выглядит так:
<?php // /local/php_interface/init.php
AddEventHandler(
'main', // модуль, который вызывает событие
'OnBeforeUserAdd', // имя события
'myUserCheck' // функция-обработчик
);
function myUserCheck(&$arFields)
{
// ...
}
Регистрировать обработчики нужно в init.php — файле, который подключается
на каждом хите до формирования страницы. Подробно он разобран в статье
init.php в Битрикс.
Обработчик в классе
Функции в глобальной области быстро засоряют проект. Лучше использовать методы класса:
<?php
AddEventHandler('iblock', 'OnBeforeIBlockElementAdd', ['CatalogHandlers', 'onBeforeAdd']);
class CatalogHandlers
{
public static function onBeforeAdd(&$arFields)
{
// ...
}
}
События «до» и «после»
Большинство событий существует в двух вариантах, и разница между ними принципиальная.
| OnBefore* | OnAfter* | |
|---|---|---|
| Когда срабатывает | До выполнения операции | После выполнения |
| Можно изменить данные | Да, через ссылку | Нет, операция завершена |
| Можно отменить | Да | Нет |
| Известен ID записи | Нет при добавлении | Да |
| Типичное применение | Валидация, подстановка значений | Уведомления, синхронизация |
Отсюда простое правило: меняете данные — берите OnBefore,
реагируете на факт — берите OnAfter. Попытка отправить письмо
из OnBefore приведёт к тому, что письмо уйдёт даже если сохранение
в итоге не состоится.
Нужна помощь с Битрикс?
Изменение данных
Чтобы правки в обработчике подействовали, параметр принимается по ссылке — через амперсанд. Без него функция получит копию, изменит её, и ничего не произойдёт:
<?php
AddEventHandler('iblock', 'OnBeforeIBlockElementAdd', 'setElementCode');
function setElementCode(&$arFields)
{
// автоматически заполняем символьный код, если он пуст
if (empty($arFields['CODE']) && !empty($arFields['NAME'])) {
$arFields['CODE'] = CUtil::translit(
$arFields['NAME'],
'ru',
['replace_space' => '-', 'replace_other' => '-']
);
}
}
Забытый амперсанд — самая частая причина «обработчик срабатывает, но ничего не меняется». Ошибки при этом не возникает, что делает проблему особенно неприятной.
Отмена операции
Событие OnBefore* можно прервать. Механизм выглядит непривычно:
нужно вернуть false и записать текст ошибки в глобальный объект приложения.
<?php
AddEventHandler('iblock', 'OnBeforeIBlockElementAdd', 'checkElementPrice');
function checkElementPrice(&$arFields)
{
global $APPLICATION;
if (isset($arFields['PROPERTY_VALUES']['PRICE'])
&& $arFields['PROPERTY_VALUES']['PRICE'] < 0) {
$APPLICATION->throwException('Цена не может быть отрицательной');
return false;
}
return true;
}
Без вызова throwException операция всё равно отменится, но пользователь
увидит пустое сообщение об ошибке и не поймёт, что произошло.
Часто используемые события
| Модуль | Событие | Когда срабатывает |
|---|---|---|
| main | OnBeforeUserAdd | Перед добавлением пользователя |
| main | OnAfterUserRegister | После регистрации на сайте |
| main | OnBeforeUserLogin | Перед авторизацией |
| main | OnBeforeEventAdd | Перед постановкой письма в очередь |
| main | OnEpilog | В конце формирования страницы |
| iblock | OnBeforeIBlockElementAdd | Перед добавлением элемента |
| iblock | OnAfterIBlockElementUpdate | После изменения элемента |
| iblock | OnBeforeIBlockElementDelete | Перед удалением элемента |
| sale | OnSaleOrderSaved | После сохранения заказа |
| catalog | OnPriceAdd | После добавления цены |
Полный список событий модуля всегда можно посмотреть в документации разработчика — он зависит от версии продукта и набора установленных модулей.
Подход D7
В новом ядре тот же механизм реализован через EventManager.
Синтаксис отличается, возможностей больше:
<?php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'iblock',
'OnBeforeIBlockElementAdd',
['CatalogHandlers', 'onBeforeAdd']
);
Ключевые отличия от старого способа: обработчик можно снять, события своих модулей
регистрируются штатно, а параметры передаются объектом Event
вместо набора аргументов. Подробный разбор — в статье
EventManager в Битрикс.
Важный практический момент: старые события ядра остаются старыми.
OnBeforeIBlockElementAdd вызывается в формате старого ядра независимо
от того, каким способом вы его зарегистрировали. Регистрация через
EventManager не меняет сигнатуру обработчика.
Отладка обработчиков
Обработчики выполняются в глубине ядра, где echo либо ничего не выводит,
либо ломает страницу. Отлаживать нужно логом:
<?php
function myHandler(&$arFields)
{
\Bitrix\Main\Diag\Debug::writeToFile(
$arFields,
'OnBeforeIBlockElementAdd',
'/local/logs/events.log'
);
}
Порядок проверки, если обработчик не работает:
- Убедитесь, что
init.phpвообще подключается — запишите в лог факт подключения. - Проверьте написание имени события: опечатка не вызывает ошибки.
- Проверьте, что модуль указан верно —
iblock, а неmain. - Убедитесь, что параметр принимается по ссылке, если вы меняете данные.
- Проверьте, что метод класса объявлен как
public static.
Частые ошибки
- Забыт амперсанд. Данные меняются в копии, оригинал остаётся прежним.
-
Тяжёлая логика в обработчике. Событие вроде
OnAfterIBlockElementUpdateсрабатывает при каждом сохранении, в том числе при массовом импорте — и импорт растягивается на часы. - Обработчик вызывает сам себя. Изменение элемента внутри обработчика события изменения элемента даёт бесконечную рекурсию. Нужен флаг-предохранитель.
-
Отправка письма из
OnBefore. Уведомление уходит, даже если операция в итоге отменена. - Обработчики зарегистрированы в шаблоне сайта. В административной части они не подключатся, и поведение будет разным.
-
Отмена без
throwException. Пользователь видит пустую ошибку и не понимает, что сделал не так.
Итог
События — штатный способ расширить поведение 1С-Битрикс без правок ядра.
Обработчики регистрируются в init.php: AddEventHandler
в старом стиле или EventManager в стиле D7.
Три вещи, которые определяют, будет ли обработчик работать: правильный выбор между
OnBefore и OnAfter, передача параметров по ссылке
при изменении данных и вызов throwException при отмене операции,
чтобы пользователь увидел причину.
