<?php declare(strict_types = 1);
namespace Zeobv\AbandonedCart\Storefront\Subscriber;
use Shopware\Storefront\Page\Account\Overview\AccountOverviewPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoadedEvent;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemAddedEvent;
use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoadedEvent;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemRemovedEvent;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemQuantityChangedEvent;
use Zeobv\AbandonedCart\Pagelet\AbandonedCartReminder\Account\AbandonedCartReminderAccountPageletLoader;
use Zeobv\AbandonedCart\Pagelet\AbandonedCartReminder\Account\AbandonedCartReminderPagelet;
use Zeobv\AbandonedCart\Service\AbandonedCartService;
use Zeobv\AbandonedCart\Service\CartService;
/**
* Class PageSubscriber
*
* @package Zeobv\AbandonedCart\Subscriber
*/
class PageSubscriber implements EventSubscriberInterface
{
/**
* @var EntityRepositoryInterface
*/
protected $abandonedCartRepository;
/**
* @var AbandonedCartService
*/
protected $abandonedCartService;
/**
* @var EntityRepositoryInterface
*/
protected $salesChannelDomainRepository;
/**
* @var Request|null
*/
protected $currentRequest;
/**
* @var CartService
*/
protected $cartService;
protected AbandonedCartReminderAccountPageletLoader $abandonedCartReminderAccountPageletLoader;
/**
* @param RequestStack $requestStack
* @param EntityRepositoryInterface $abandonedCartRepository
* @param EntityRepositoryInterface $salesChannelDomainRepository
* @param AbandonedCartService $abandonedCartService
* @param CartService $cartService
* @param AbandonedCartReminderAccountPageletLoader $abandonedCartReminderAccountPageletLoader
*/
public function __construct(
RequestStack $requestStack,
EntityRepositoryInterface $abandonedCartRepository,
EntityRepositoryInterface $salesChannelDomainRepository,
AbandonedCartService $abandonedCartService,
CartService $cartService,
AbandonedCartReminderAccountPageletLoader $abandonedCartReminderAccountPageletLoader
)
{
$this->currentRequest = $requestStack->getCurrentRequest();
$this->abandonedCartRepository = $abandonedCartRepository;
$this->abandonedCartService = $abandonedCartService;
$this->cartService = $cartService;
$this->salesChannelDomainRepository = $salesChannelDomainRepository;
$this->abandonedCartReminderAccountPageletLoader = $abandonedCartReminderAccountPageletLoader;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
AccountOverviewPageLoadedEvent::class => 'onAccountOverviewLoaded',
AfterLineItemAddedEvent::class => 'onLineItemAdded',
AfterLineItemQuantityChangedEvent::class => 'onLineItemQtyChanged',
AfterLineItemRemovedEvent::class => 'onLineItemRemoved',
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoaded',
CheckoutRegisterPageLoadedEvent::class => 'onCheckoutRegisterPageLoaded',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
// CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoaded',
];
}
/**
* @param AccountOverviewPageLoadedEvent $event
*/
public function onAccountOverviewLoaded(AccountOverviewPageLoadedEvent $event): void
{
$event->getPage()->addExtension(
AbandonedCartReminderPagelet::EXTENSION_NAME,
$this->abandonedCartReminderAccountPageletLoader->load(
$event->getPage()->getCustomer()
)
);
}
/**
* @param AfterLineItemAddedEvent $event
*/
public function onLineItemAdded(AfterLineItemAddedEvent $event): void
{
$cart = $event->getCart();
$salesChannelContext = $event->getSalesChannelContext();
if ($this->currentRequest->attributes->get('sw-domain-id')) {
$this->upsertAbandonedCart($cart, $salesChannelContext, true);
}
}
/**
* @param AfterLineItemRemovedEvent $event
*/
public function onLineItemRemoved(AfterLineItemRemovedEvent $event): void
{
$cart = $event->getCart();
$salesChannelContext = $event->getSalesChannelContext();
if ($this->currentRequest->attributes->get('sw-domain-id')) {
$this->upsertAbandonedCart($cart, $salesChannelContext, true);
}
}
/**
* @param AfterLineItemQuantityChangedEvent $event
*/
public function onLineItemQtyChanged(AfterLineItemQuantityChangedEvent $event): void
{
$cart = $event->getCart();
$salesChannelContext = $event->getSalesChannelContext();
if ($this->currentRequest->attributes->get('sw-domain-id')) {
$this->upsertAbandonedCart($cart, $salesChannelContext, true);
}
}
/**
* @param OffcanvasCartPageLoadedEvent $event
*/
public function onOffcanvasCartPageLoaded(OffcanvasCartPageLoadedEvent $event): void
{
$cart = $event->getPage()->getCart();
$this->abandonedCartService->saveCart($cart, $event->getSalesChannelContext());
$this->upsertAbandonedCart($cart, $event->getSalesChannelContext());
}
/**
* @param CheckoutConfirmPageLoadedEvent $event
*/
public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void
{
$cart = $event->getPage()->getCart();
$this->abandonedCartService->saveCart($cart, $event->getSalesChannelContext());
$this->upsertAbandonedCart($cart, $event->getSalesChannelContext());
}
/**
* @param CheckoutRegisterPageLoadedEvent $event
*/
public function onCheckoutRegisterPageLoaded(CheckoutRegisterPageLoadedEvent $event): void
{
$cart = $event->getPage()->getCart();
$this->abandonedCartService->saveCart($cart, $event->getSalesChannelContext());
$this->upsertAbandonedCart($cart, $event->getSalesChannelContext());
}
/** @deprecated see OrderSubscriber::onOrderWritten()
* @param CheckoutFinishPageLoadedEvent $event
*/
public function onCheckoutFinishPageLoaded(CheckoutFinishPageLoadedEvent $event): void
{
$orderCustomer = $event->getPage()->getOrder()->getOrderCustomer();
if ($orderCustomer === null) {
return;
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('email', $orderCustomer->getEmail()));
$abandonedCart = $this->abandonedCartRepository->search($criteria, $event->getContext())->first();
if (is_null($abandonedCart)) {
return;
}
$this->abandonedCartService->resolveAbandonedCart($abandonedCart, $event->getContext());
}
/**
* @param Cart $cart
* @param SalesChannelContext $context
* @param bool $cartShouldExist
*/
protected function upsertAbandonedCart(Cart $cart, SalesChannelContext $context, bool $cartShouldExist = false): void
{
$customer = $context->getCustomer();
if (is_null($customer)) {
return;
}
$salesChannelDomainId = $this->currentRequest->attributes->get('sw-domain-id');
if (is_null($salesChannelDomainId)) {
return;
}
$criteria = new Criteria();
# Get abandoned cart by customer email to avoid abandoned cart duplication.
$criteria->addFilter(new EqualsFilter('email', $customer->getEmail()));
$cartResult = $this->abandonedCartRepository->search($criteria, $context->getContext());
if ($cartShouldExist && $cartResult->count() <= 0) {
return;
}
$lineItems = json_encode($cart->getLineItems()->getElements());
if ($lineItems === false) {
return;
}
$lineItems = json_decode($lineItems, true);
if (empty($lineItems)) {
$id = $cartResult->first() ? $cartResult->first()->getId() : null;
if ($id !== null) {
$this->abandonedCartRepository->delete([[
'id' => $id
]], $context->getContext());
}
return;
}
$data = [
'id' => $cartResult->first() ? $cartResult->first()->getId() : Uuid::randomHex(),
'cartToken' => $cart->getToken(),
'lineItems' => $lineItems,
'currencyId' => $context->getCurrency()->getId(),
'paymentMethodId' => $context->getPaymentMethod()->getId(),
'shippingMethodId' => $context->getShippingMethod()->getId(),
'countryId' => $context->getShippingLocation()->getCountry()->getId(),
'salesChannelId' => $context->getSalesChannel()->getId(),
'salesChannelDomainId' => $salesChannelDomainId,
'customerId' => $customer->getId(),
'email' => $customer->getEmail(),
];
$this->abandonedCartRepository->upsert([$data], $context->getContext());
}
}