[spoiler]
Начнем с того, почему в обработчиках нет старых данных. Это редкий (по сравнению с обычным изменением) кейс, который, будучи реализован штатно, минимум в два раза увеличит число запросов на каждое обновление.
Так как же решить такую задачу? В случае использования D7 - тривиально, в случае старого api - несколько сложнее, хотя подход одинаков. Почему проще на D7? Набор имеющихся событий и место их вызова стандартизированы, равно как и данные, доступные в обработчиках. На старом api не всегда есть обработчики либо есть только один (обычно вызываемый после обновления).
Итак, сначала на D7. Приведу реальный пример, взятый из api купонов правил корзины. В случае перепривязки купона (смены правила корзины, к которому он относится), необходимо обновить флаг существования купонов как у старого, так и у нового правила. Класс \Bitrix\Sale\Internals\DiscountCouponTable:
public static function onUpdate(Main\Entity\Event $event) { if (!self::isCheckedCouponsUse()) return; $data = $event->getParameter('fields'); if (isset($data['DISCOUNT_ID'])) { $data['DISCOUNT_ID'] = (int)$data['DISCOUNT_ID']; $id = $event->getParameter('id'); $couponIterator = self::getList(array( 'select' => array('ID', 'DISCOUNT_ID'), 'filter' => array('=ID' => $id) )); if ($coupon = $couponIterator->fetch()) { $coupon['DISCOUNT_ID'] = (int)$coupon['DISCOUNT_ID']; if ($coupon['DISCOUNT_ID'] !== $data['DISCOUNT_ID']) { self::$discountCheckList[$data['DISCOUNT_ID']] = $data['DISCOUNT_ID']; self::$discountCheckList[$coupon['DISCOUNT_ID']] = $coupon['DISCOUNT_ID']; } } unset($coupon, $couponIterator); } } public static function onAfterUpdate(Main\Entity\Event $event) { self::updateUseCoupons(); } |
$eventManager = \Bitrix\Main\EventManager::getInstance(); $eventManager->registerEventHandler('модуль', 'событие', 'Ваш_модуль', 'Ваш_класс', 'метод_класса'); /* событие - \Bitrix\Main\Entity\DataManager::EVENT_ON_UPDATE или \Bitrix\Main\Entity\DataManager::EVENT_ON_AFTER_UPDATE */ |
1.
if (!self::isCheckedCouponsUse()) return; |
2.
$data = $event->getParameter('fields'); if (isset($data['DISCOUNT_ID'] ) ) { $data['DISCOUNT_ID'] = (int)$data['DISCOUNT_ID']; $id = $event->getParameter('id'); $couponIterator = self::getList(array( 'select' => array('ID', 'DISCOUNT_ID'), 'filter' => array('=ID' => $id) )); if ($coupon = $couponIterator->fetch()) { $coupon['DISCOUNT_ID'] = (int)$coupon['DISCOUNT_ID']; if ($coupon['DISCOUNT_ID'] !== $data['DISCOUNT_ID'] ) { self::$discountCheckList[$data['DISCOUNT_ID']] = $data['DISCOUNT_ID']; self::$discountCheckList[$coupon['DISCOUNT_ID']] = $coupon['DISCOUNT_ID']; } } unset($coupon, $couponIterator); } |
3. Собственно, остался вызов onAfterUpdate, внутри которого проверяется, что переменная с данными не пустаи идет обновление флага существования купонов для старого и нового правил корзины.
Еще раз подчеркну. Этот подход требует следующего:
- Оба обработчика относятся к одному классу.
- Оба статические
- Данные хранятся в статической переменной класса.
В случае api старого ядра задача усложняется - может не оказаться необходимых событий. Единственный выход - либо просить их добавить, либо искать обходные пути. Но в рамках задачи предположим, что они есть. Небольшой недостаток в том, что в старом ядре может выполниться событие Before и не выполниться After. Рассмотрим на примере CCatalogProduct. У метода Update есть два события - OnBeforeProductUpdate и OnProductUpdate. Создаем класс обработчиков:
class MyChanger { protected static $oldWeight = array(); public static function OnBeforeUpdate($id, &$fields) { $id = (int)$id; if ($id <= 0) return true; if (isset($fields['WEIGHT'] ) ) { $productRes = CCatalogProduct::GetList(array(), array('ID' => $id), false, false, array('WEIGHT' ); $product = $productRes->Fetch(); if (!empty($product)) { if ($product['WEIGHT'] != $fields['WEIGHT'] ) self::$oldWeight[$id] = $product['WEIGHT']; } } return true; } public static function OnUpdate($id, $fields) { if (isset($fields['WEIGHT'] ) && isset(self::$oldWeight[$id] ) ) { /* * необходимые действия */ unset(self::$oldWeight[$id] ); } } } |
Метод MyChanger :: OnBeforeUpdate вешается на событие catalog'а OnBeforeProductUpdate. Если в массиве обновляемых данных есть поле WEIGHT - из базы подымается старое значение и если оно отличается - старое заносится в статическую переменную.
Метод MyChanger :: OnUpdate должен содержать требуемую логику (логирование, отправка писем, etc). Он должен быть зарегистрирован на событие catalog'а OnProductUpdate.
Собственно, это все. Естественно, перечень параметров обработчика должен соответствовать параметрам, передаваемым в обработчик из метода.
Удачи в использовании.
1) почему используется self:: вместо static::, возможность наследования класса принципиально не предполагается?
2) немного не по теме, но связанно с вопросом #1, зачем создаются final классы без возможности наследования
Но как теоретический пример, предположим, что у нас купоны заинтегрированны на внешнюю купонную систему, тогда при активации купона было бы здорово переопределить метод "updateUseCoupons" или "isCheckedCouponsUse", но self добавит работки по переопределеню методов.