Битрикс: объединение корзины после авторизации
В битриксе есть встроенное поведение, когда корзина одного пользователя (неавторизованного) объединяется с корзиной другого пользователя - в момент авторизации пользователя на сайте. По традиции официальная документация про этот функционал умалчивает.
Порядок вызовов функций в ядре битрикса тут такой:
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
Комментарии
Отправить комментарий