
[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 добавит работки по переопределеню методов.