Событие OnBeforeIBlockElementAdd — главная точка входа для валидации элементов инфоблока до их сохранения. Здесь можно отменить некорректное добавление, дополнить обязательные поля и нормализовать значения свойств, не трогая шаблоны и компоненты.
Когда срабатывает событие
Событие принадлежит модулю iblock и вызывается методом CIBlockElement::Add() непосредственно перед формированием SQL-запроса на вставку. К этому моменту массив полей уже прошёл первичную сборку, но запись в таблицу b_iblock_element ещё не произошла — это делает событие идеальным местом для проверки и правки данных.
Важно понимать порядок событий добавления элемента:
OnStartIBlockElementAdd— самое раннее, вызывается до проверки обязательных полей и прав;OnBeforeIBlockElementAdd— после валидации ядром, но до записи; можно отменить операцию;OnAfterIBlockElementAdd— после успешной записи, когда уже известен ID элемента.
OnStartIBlockElementUpdate, OnBeforeIBlockElementUpdate, OnAfterIBlockElementUpdate. Логику валидации обычно выносят в общий метод, чтобы не дублировать код.Регистрация обработчика
OnBeforeIBlockElementAdd — событие «старого» стиля: обработчик получает массив полей по ссылке. Регистрировать его удобнее через новый EventManager в /local/php_interface/init.php:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'iblock',
'OnBeforeIBlockElementAdd',
['\Vendor\Catalog\ElementValidator', 'onBeforeAdd']
);
Классический способ через RegisterModuleDependencies() тоже работает и записывает обработчик в таблицу зависимостей — его применяют внутри модулей, чтобы связь сохранялась между хитами без перерегистрации.
Сигнатура обработчика и структура полей
Обработчик принимает единственный аргумент — массив полей по ссылке. Возврат false отменяет добавление, любой другой результат (в том числе отсутствие return) считается разрешением.
public static function onBeforeAdd(&$arFields)
{
// $arFields['IBLOCK_ID'], ['NAME'], ['CODE'], ['ACTIVE']
// $arFields['PROPERTY_VALUES'][CODE|ID] — значения свойств
return true;
}
Ключевые узлы массива:
| Ключ | Назначение |
|---|---|
IBLOCK_ID | ID инфоблока — по нему фильтруют логику |
NAME, CODE | Название и символьный код элемента |
PROPERTY_VALUES | Значения свойств: ключ — ID или символьный код свойства |
ACTIVE, PREVIEW_TEXT | Флаг активности и анонс |
Обратите внимание: ключи в PROPERTY_VALUES зависят от того, как вызывался Add(). Если разработчик передал свойства по символьным кодам — читайте по ним, если по ID — по числовым идентификаторам.
Как отменить добавление и показать ошибку
Чтобы прервать операцию с осмысленным сообщением, задайте текст исключения через глобальный объект приложения и верните false:
global $APPLICATION;
if (empty($arFields['PROPERTY_VALUES']['ARTICLE'])) {
$APPLICATION->throwException('Не заполнен артикул товара');
return false;
}
Текст, переданный в throwException(), попадёт в CIBlockElement::Add() — вызывающий код прочитает его через $el->LAST_ERROR, а в админке он отобразится в форме редактирования элемента. Для нескольких ошибок соберите их в один текст с переносами строк.
false без throwException(), добавление тоже отменится, но пользователь увидит пустое или невнятное сообщение. Всегда сопровождайте отмену понятным текстом.Модификация полей до записи
Поскольку массив передан по ссылке, из обработчика можно не только проверять, но и дописывать данные. Частые сценарии — автогенерация символьного кода, нормализация цен, установка значений по умолчанию:
// Автотранслитерация CODE из NAME, если он пуст
if (empty($arFields['CODE']) && !empty($arFields['NAME'])) {
$arFields['CODE'] = \CUtil::translit(
$arFields['NAME'], 'ru',
['replace_space' => '-', 'replace_other' => '-']
);
}
// Обрезаем лишние пробелы в артикуле
$art = &$arFields['PROPERTY_VALUES']['ARTICLE'];
if (is_string($art)) { $art = trim($art); }
Такой подход надёжнее, чем правка данных в шаблоне или в импорте: событие срабатывает при любом способе создания элемента — через админку, компонент, API или обмен с 1С.
Фильтрация по инфоблоку и типичные ошибки
Обработчик вызывается для всех инфоблоков сайта. Первым делом отсекайте чужие данные по IBLOCK_ID — иначе валидация каталога сработает на элементах новостей и служебных инфоблоков.
- Определяйте нужный ID через константу или
CIBlock::GetList()по символьному коду и типу — не хардкодьте числа, которые различаются на dev и prod; - Не запускайте
CIBlockElement::GetList()в цикле внутри обработчика — событие в импорте может вызываться тысячи раз; - Помните, что при обновлении в
$arFieldsприходят только изменяемые поля — недостающие значения читайте отдельным запросом.
CIBlockElement::Update() для того же элемента, событие обновления сработает снова. Модифицируйте $arFields прямо в обработчике вместо повторной записи.Итог
OnBeforeIBlockElementAdd — надёжный контроль качества данных на уровне ядра: проверка обязательных свойств, нормализация значений и отмена некорректных записей работают одинаково для админки, компонентов, API и обменов. Ключевые правила — фильтровать по IBLOCK_ID, сопровождать отмену throwException() и избегать рекурсивных записей.
Когда валидация перерастает в сложные бизнес-правила, интеграции с 1С или проверки против внешних систем, мы в B2Bsite проектируем это как отдельный слой сервисов и автоматизаций, чтобы логика оставалась тестируемой и не расползалась по обработчикам.
Частые вопросы
Чем OnBeforeIBlockElementAdd отличается от OnAfterIBlockElementAdd?
Before срабатывает до записи и позволяет отменить операцию или изменить поля по ссылке. After вызывается после успешной вставки, когда уже есть ID элемента, и подходит для действий-последствий: индексация, уведомления, обмен.
Как показать пользователю сообщение об ошибке валидации?
Вызовите $APPLICATION->throwException('текст ошибки') и верните false. Текст попадёт в LAST_ERROR и отобразится в форме редактирования элемента в админке.
Можно ли из обработчика изменить значения свойств?
Да, массив $arFields передаётся по ссылке. Правьте $arFields['PROPERTY_VALUES'] напрямую, и изменения попадут в базу без повторного вызова Update.
Почему обработчик срабатывает на чужих инфоблоках?
Событие общее для модуля iblock и вызывается при добавлении любого элемента. Всегда фильтруйте логику по $arFields['IBLOCK_ID'] в начале обработчика.
Как читать свойства — по ID или по символьному коду?
Ключи в PROPERTY_VALUES зависят от того, как формировался массив при вызове Add. Если свойства передавались по кодам — читайте по кодам, если по ID — по числам. Надёжнее договориться об одном формате в проекте.
Где регистрировать обработчик?
Для проектной логики — в /local/php_interface/init.php через EventManager::getInstance()->addEventHandler. Внутри собственного модуля — через RegisterModuleDependencies в файле установки.
Событие срабатывает при импорте из 1С?
Да, обмен с 1С создаёт и обновляет элементы через API инфоблоков, поэтому события Before/After вызываются. Это удобно для валидации, но учитывайте нагрузку при массовых операциях.
Как не попасть в бесконечную рекурсию?
Не вызывайте Update того же элемента внутри обработчика добавления или обновления. Меняйте данные прямо в $arFields — этого достаточно, чтобы они записались корректно.
Поможем с настройкой и поддержкой Для разработчиков
Поможем с настройкой, доработкой и поддержкой Для разработчиков.