Битрикс: объединение корзины после авторизации

В битриксе есть встроенное поведение, когда корзина одного пользователя (неавторизованного) объединяется с корзиной другого пользователя - в момент авторизации пользователя на сайте. По традиции официальная документация про этот функционал умалчивает.

Порядок вызовов функций в ядре битрикса тут такой:

1) срабатывает событие главного модуля (main) OnUserLogin. На это событие подписан обработчик события авторизации из модуля интернет магазина (sale) - \CSaleUser::OnUserLogin(). Этот обработчик модуля интернет-магазина внутри только вызывает одну функцию - см. следующий пункт.

2) вызывается обработчик \Bitrix\Sale\Fuser::handlerOnUserLogin() - внутри него происходит определение старого и нового FUSER_ID. Это пользователь интернет-магазина до авторизации и пользователь после авторизации.

3) далее если звёзды сошлись, запускается метод \CSaleBasket::TransferBasket(), который выполняет объединение корзин для найденных FUSER_ID.

И всё бы нечего, если бы не возникала иногда необходимость вмешаться в процесс объединения корзин. Битрикс объединяет позиции корзины по таким правилам:

1) одинаковые значения параметров MODULE и PRODUCT_ID у элементов корзин

2) одинаковый состав и значения свойств элементов корзин

Иногда получается так, что в итоговой корзине (после объединения корзин) после авторизации находятся два одинаковых товара двумя позициями. Т.е. какие-то свойства элементов корзин не совпали и битрикс не смог (не стал) объединить товары в одну строку. Иногда требуется это не допускать: чтобы один и тот же товар не повторялся бы в корзине дважды.

Битрикс не оставляет никакой возможности повлиять на этот процесс объединения корзин. Нет никаких событий или параметров, которые бы помогли в этом. Единственный выход, который я для себя нашёл - допустить образование дублей, а затем сразу же это определять и устранять.

Битрикс делает объединение корзин в момент авторизации и далее в процессе авторизации делается очистка буфера и редирект. Нужно подобрать какие-то события, чтобы: 1) определить, что происходит процесс авторизации; 2) вставить после битриксового объединения корзин свой код, который перепроверит итоговую корзину и исправит её при необходимости.

Это получается сделать через подписывание на два такие события:

1) На событие OnAfterUserAuthorize главного модуля битрикса:


/**
 * Обработчик события будет вызван из метода CUser::Authorize после авторизации пользователя,
 * передавая в параметре user_fields массив всех полей авторизованного пользователя.
 */
$eventManager->addEventHandler(
    'main',
    'OnAfterUserAuthorize',
    [User::class, 'onAfterUserAuthorizeHandler']
);

2) На событие OnBeforeRestartBuffer главного модуля битрикса:


/**
 * Событие "OnBeforeRestartBuffer" вызывается перед сбросом буфера контента.
 * После авторизации, клиенту подтягивается его корзина и объединяется с корзиной,
 * которая была у неавторизованного пользователя.
 * Тут проверяем на дубли товаров в корзине.
 */
$eventManager->addEventHandler(
    'main',
    'OnBeforeRestartBuffer',
    [User::class, 'checkCartAfterAuth']
);

В обработчике первого события ставим пометку, что сейчас произошла авторизация. Например, через параметр в своём каком-то классе-синглтоне или ещё как-то. Во втором обработчике проверяем пометку: если в событие попали после авторизации, значит надо проверять и чинить корзину.

Как именно проверяем и чиним позиции корзины - зависит от конкретных бизнес задач. Не буду полностью приводить свой код. Оставлю тут только SQL запрос, которым можно определить, есть ли дубли товаров в корзине и какие именно товары задвоенны:


SELECT `sb1`.`ID`, `sb1`.`FUSER_ID`
FROM `b_sale_basket` `sb1`, `b_sale_basket` `sb2`
WHERE ' . $addSql . '`sb1`.`ID` < `sb2`.`ID` AND `sb1`.`FUSER_ID`=`sb2`.`FUSER_ID`
    AND `sb1`.`ORDER_ID` IS NULL AND `sb2`.`ORDER_ID` IS NULL
    AND `sb1`.`PRODUCT_ID`=`sb2`.`PRODUCT_ID` AND `sb1`.`FUSER_ID`=' . $fuserId . '
ORDER BY `ID` DESC

Комментарии

Популярные сообщения из этого блога

Пропорциональное распределение суммы

Битрикс: своя геолокация

Битрикс: два способа отправить файл