Контент-менеджер сохранил в списке только новое название товара, а характеристики исчезли. Типичная ошибка на практике: вчера в init.php повесили OnBeforeIBlockElementUpdate, в обработчике поправили свойство без isset — свойства пропали, умный фильтр не работает. Главная проблема не в Update, а в отсутствии isset: ядро восприняло это как полную перезапись PROPERTY_VALUES. Ниже — как использовать AddEventHandler в init.php, безопасно работать с &$arFields, отменить Update через ThrowException и return false, и получить предсказуемый результат при частичном сохранении.
OnBeforeIBlockElementUpdate - событие модуля iblock, вызываемое внутри CIBlockElement::Update до записи в БД. Обработчик получает &$arFields по ссылке: правки полей применяются, а отмена Update - через $APPLICATION->ThrowException() и return false. Критично: трогать PROPERTY_VALUES только если ключ передан в вызове Update, иначе свойства элемента будут очищены. Сегодня: повесьте обработчик в init.php, оберните свойства в isset, для одного свойства смотрите SetPropertyValuesEx в гайде по Update.
Речь про 1С-Битрикс: Управление сайтом, не про Bitrix24. Событие срабатывает при любом CIBlockElement::Update: админка, импорт, cron. Семантику Update и PROPERTY_VALUES разбираем в гайде по CIBlockElement::Update. Общую схему init.php и AddEventHandler - в статье про обработчики событий.
Что такое OnBeforeIBlockElementUpdate и когда он нужен
OnBeforeIBlockElementUpdate - проверка перед записью элемента инфоблока в базу. Представьте фильтр на входе: пока массив $arFields не прошел вашу логику, CIBlockElement::Update не запишет изменения. Обработчик может подправить поля или запретить сохранение целиком.
Нужен, когда при любом Update надо валидировать поля, нормализовать CODE, запретить пустой DETAIL_TEXT у активных товаров или проверить связку свойств каталога. Не нужен, если из cron меняете одно свойство - точечнее SetPropertyValuesEx (B40). Не путайте с общим AddEventHandler для sale или main: здесь модуль iblock, событие OnBeforeIBlockElementUpdate, сигнатура (&$arFields).
Зарегистрируйте обработчик в init.php через AddEventHandler
- Откройте /bitrix/php_interface/init.php на тестовой копии.
- Добавьте AddEventHandler('iblock', 'OnBeforeIBlockElementUpdate', 'MyOnBeforeElementUpdate').
- Объявите функцию с (&$arFields) - без ссылки правки не применятся.
- Отфильтруйте IBLOCK_ID в первых строках обработчика и проверьте сохранение в админке.
<?php
AddEventHandler('iblock', 'OnBeforeIBlockElementUpdate', 'MyOnBeforeElementUpdate');
function MyOnBeforeElementUpdate(&$arFields)
{
if ((int)$arFields['IBLOCK_ID'] !== 7) {
return true;
}
return true;
}
В модуле (D7): EventManager::getInstance()->addEventHandler('iblock', 'OnBeforeIBlockElementUpdate', ['\\Vendor\\Handlers', 'onBeforeUpdate']). Для addeventhandler bitrix в init.php хватает старого ядра. Сделайте: фильтр по инфоблоку сразу. Не делайте: копировать гист без понимания охвата.
Отмените Update через ThrowException и return false
Невалидные данные останавливают сохранение так: $APPLICATION->ThrowException('текст'), затем return false. Без return false Update может пройти.
function MyOnBeforeElementUpdate(&$arFields)
{
if (empty(trim($arFields['NAME']))) {
global $APPLICATION;
$APPLICATION->ThrowException('Название не может быть пустым.');
return false;
}
return true;
}
В админке редактор увидит текст ошибки. В скрипте — $el->LAST_ERROR. Сделайте: понятные сообщения на русском. Не делайте: отменять импорт без явной причины.
Обойдите ловушку PROPERTY_VALUES: проверяйте isset
Типичная ошибка на практике после копипаста гайда в прод: в реальном проекте разработчик повесил AddEventHandler, в обработчике написал $arFields['PROPERTY_VALUES'][$propId] = 'нормализованное значение'. Контент-менеджер сохранил элемент из списка только с новым NAME. Вызов Update не передал PROPERTY_VALUES, но обработчик создал ключ — ядро восприняло это как полную перезапись и очистило артикулы, бренды, фото-привязки.
Цепочка: частичный Update без свойств → обработчик без isset → wipe. Та же семантика, что при неполном ciblockelement update в скрипте импорта - подробно в B40. Комментарий Д. Турчина в официальной справке OnBefore прямо предупреждает про isset перед PROPERTY_VALUES.
if (isset($arFields['PROPERTY_VALUES'])) {
$arFields['PROPERTY_VALUES'][42] = trim($arFields['PROPERTY_VALUES'][42]);
}
Без isset($arFields['PROPERTY_VALUES']) свойства не трогайте. Для одного свойства из cron используйте SetPropertyValuesEx (ciblockelement setpropertyvalues), не OnBefore. Сделайте: isset перед любой записью. Не делайте: «нормализовать артикул», если Update без свойств.
Проверяйте isset для DETAIL_TEXT при частичном Update
В $arFields только то, что передал вызов Update. Из списка может не быть DETAIL_TEXT, PREVIEW_TEXT, SHOW_COUNTER. str_replace без isset обнуляет текст при смене раздела.
if (isset($arFields['DETAIL_TEXT']) && $arFields['DETAIL_TEXT'] !== '') {
$arFields['DETAIL_TEXT'] = str_replace('old.domain', 'new.domain', $arFields['DETAIL_TEXT']);
}
Для сравнения с базой — CIBlockElement::GetByID, не ждите полный снимок в событии. Сделайте: isset на каждое поле. Не делайте: полагаться на «все поля элемента» при bitrix update element property из админки.
Сравните OnBefore и OnAfterIBlockElementUpdate
Разработчики часто переносят код из OnBefore в OnAfter и удивляются, что "ничего не сохранилось". В OnAfterIBlockElementUpdate &$arFields по ссылке, но официальная справка прямо говорит: манипуляции с массивом не изменят данные в БД. OnAfter срабатывает даже при неудачном Update, поэтому всегда проверяйте $arFields['RESULT'] и $arFields['RESULT_MESSAGE'].
Схема событий Update:
OnStartIBlockElementUpdate → OnBeforeIBlockElementUpdate (правки, отмена) → запись в БД → OnAfterIBlockElementUpdate (лог, очередь при RESULT=true)
| Критерий | OnBeforeIBlockElementUpdate | OnAfterIBlockElementUpdate |
|---|---|---|
| Момент | До записи в БД | После попытки Update |
| Правка &$arFields | Применяется | Не меняет БД |
| Отмена | ThrowException + return false | Невозможна |
| Успех | return false | RESULT === true |
Итог: валидацию и правки полей - в OnBefore. OnAfter - лог и побочные действия при успешном RESULT. CCatalogProduct::Update в OnAfter часто не сохраняется - для цен ищите события catalog или OnBefore.
Прогоните чек-лист перед деплоем
- Бэкап базы и init.php.
- Тест на копии: карточка, список, импорт - свойства на месте.
- Журнал событий iblock без fatal error.
- Откат: removeEventHandler или комментарий регистрации.
На staging снимите обработчик через removeEventHandler('iblock', 'OnBeforeIBlockElementUpdate', 'MyOnBeforeElementUpdate') - ядро править не нужно. Если свойства пропали на проде - первым делом isset($arFields['PROPERTY_VALUES']) и контракт Update в B40. Нужен аудит цепочки событий и импорта на боевом проекте - напишите через контакты, разберем onbeforeiblockelementupdate и соседние обработчики.
Автор: Максим Мольков, разработчик 1С-Битрикс.
Источники: OnBeforeIBlockElementUpdate, OnAfterIBlockElementUpdate, CIBlockElement::Update.
Частые вопросы
Почему после Update пропали свойства из-за обработчика?
Update без PROPERTY_VALUES, а обработчик OnBeforeIBlockElementUpdate записал в $arFields['PROPERTY_VALUES'] без isset - ядро удалило остальные свойства. Оберните работу в isset($arFields['PROPERTY_VALUES']). Для одного свойства - SetPropertyValuesEx (B40).
Чем OnBeforeIBlockElementUpdate отличается от OnAfterIBlockElementUpdate?
OnBefore - до записи: правки &$arFields применяются, отмена через ThrowException и return false. OnAfter - после Update: &$arFields не сохраняется в элемент; только лог при RESULT === true. Валидацию не переносите в After.
Можно ли отменить обновление из обработчика?
Да, в OnBeforeIBlockElementUpdate: $APPLICATION->ThrowException('текст'); return false;. В OnAfter отменить запись нельзя.
Где регистрировать обработчик инфоблока?
В /bitrix/php_interface/init.php: AddEventHandler('iblock', 'OnBeforeIBlockElementUpdate', 'ИмяФункции'). Подробности init.php - B58. В модуле - EventManager::addEventHandler с модулем iblock.
Нужен ли OnBefore для одного свойства?
Обычно нет. Для "битрикс обновить свойство элемента" из cron безопаснее SetPropertyValuesEx. OnBefore - когда контролируете каждое сохранение из админки.
Почему в $arFields не все поля элемента?
В событие попадает только то, что передан в CIBlockElement::Update. Из списка может не быть DETAIL_TEXT. Проверяйте isset; для текущих значений - GetByID.
Как использовать onbeforeiblockelementupdate битрикс на staging?
Копия прода, AddEventHandler в init.php, три теста: карточка, список, импорт. Проверьте свойства. Откат - removeEventHandler. Логи - журнал событий iblock.