<?php
namespace Virgin\CheckoutPlugin\Storefront\Subscriber;
use Doctrine\DBAL\Connection;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\Event\CustomerBeforeLoginEvent;
use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
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 Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\EventDispatcher\Event;
use Shopware\Core\Checkout\Cart\CartRuleLoader;
use Virgin\ProductModelExtension\Custom\ProductType\ProductTypeEntity;
use Virgin\SystemIntegration\Services\RestApiClient;
class CustomerLoginSubscriber implements EventSubscriberInterface
{
/**
* ...
*
* @var EntityRepository
*/
protected $customerRepository;
/**
* ...
*
* @var CartRuleLoader
*/
protected $cartLoader;
/**
* ...
*
* @var CartService
*/
private $cartService;
/**
* ...
*
* @var Connection
*/
private $db;
/**
* ...
*
* @var string
*/
static private $token;
private $restApiClient;
/** @var Session */
private $session;
/**
* ...
*
* @param EntityRepository $customerRepository
* @param CartRuleLoader $cartLoader
* @param CartService $cartService
* @param Connection $db
* @param RestApiClient $restApiClient
*/
public function __construct(
EntityRepository $customerRepository,
CartRuleLoader $cartLoader,
CartService $cartService,
Connection $db,
RestApiClient $restApiClient,
SessionInterface $session
) {
// set params
$this->customerRepository = $customerRepository;
$this->cartLoader = $cartLoader;
$this->cartService = $cartService;
$this->db = $db;
$this->restApiClient = $restApiClient;
$this->session = $session;
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents(): array
{
return [
// CustomerBeforeLoginEvent::class => 'onBeforeCustomerLogin',
// CustomerLoginEvent::class => 'onCustomerLogin',
CustomerLogoutEvent::class => 'onCustomerLogout'
];
}
/**
* ...
*
* @param CustomerBeforeLoginEvent $event
*/
public function onBeforeCustomerLogin(CustomerBeforeLoginEvent $event)
{
// get the customer by email.
// if the validation fails, we wont call the next event which would actually load the cart
$customer = $this->getCustomerByEmail(
$event->getEmail(),
$event->getSalesChannelContext()
);
// even valid?
if (!$customer instanceof CustomerEntity) {
// error will be cought by shopware
return;
}
// get the old cart token by customer id
$token = $this->getCustomerCartToken(
$customer,
$event->getSalesChannelContext()
);
// none found?
if (empty($token)) {
// done
return;
}
// save it
self::$token = $token;
}
/**
* ...
*
* @param CustomerLoginEvent $event
*/
public function onCustomerLogin(CustomerLoginEvent $event)
{
if ($this->session->get('warningSnippet')) {
$this->session->remove('warningSnippet');
}
// get parameters
$customer = $event->getCustomer();
$salesChannelContext = $event->getSalesChannelContext();
$token = $event->getContextToken();
// when we have a cart as a guest (with products) and we log in after
// then the cart item doesnt get an update for the customer id.
// so we make sure that our current cart has the customer id even before
// we try to load an old cart - which may not exist.
$this->db->update(
'cart',
['customer_id' => Uuid::fromHexToBytes($customer->getId())],
['token' => $token]
);
// no token found in pre-login?
if (empty(self::$token)) {
// ignore
return;
}
// get the current cart
$cart = $this->cartService->getCart(
$token,
$salesChannelContext
);
// load the old cart by the saved token
$oldCart = $this->cartLoader->loadByToken(
$salesChannelContext,
self::$token
);
// if current cart has a subscription or a revolution_digitale
$newLineItem = null;
foreach ($cart->getLineItems() as $lineItem) {
$lineItemProductType = $lineItem->getPayloadValue('product_type');
if ($lineItemProductType == ProductTypeEntity::SUBSCRIPTION ||
$lineItemProductType == ProductTypeEntity::REVOLUTION_DIGITALE) {
$newLineItem = $lineItem;
}
}
// loop every line item from the old cart
foreach ($oldCart->getCart()->getLineItems() as $lineItem) {
// and add it to the current cart
$lineItemProductType = $lineItem->getPayloadValue('product_type');
if ($lineItem->isStackable()) {
if (($lineItemProductType == ProductTypeEntity::SUBSCRIPTION ||
$lineItemProductType == ProductTypeEntity::REVOLUTION_DIGITALE) &&
$newLineItem != null
) {
$lineItem = $newLineItem;
}
$cart = $this->cartService->add(
$cart,
$lineItem,
$event->getSalesChannelContext()
);
}
}
// remove customer id from old cart
$this->db->update(
'cart',
['customer_id' => null],
['customer_id' => Uuid::fromHexToBytes($customer->getId())]
);
// force the customer id in the current cart
$this->db->update(
'cart',
['customer_id' => Uuid::fromHexToBytes($customer->getId())],
['token' => $token]
);
if (!empty($_SESSION['utm_params'])) {
$this->restApiClient->insertUtmParams($_SESSION['utm_params'], $customer->getEmail(), $customer->getFirstName(), $customer->getLastName(), 'LOGIN');
unset($_SESSION['utm_params']);
}
}
/**
* ...
*
* @param Event $event
*/
public function onCustomerLogout(CustomerLogoutEvent $event)
{
}
/**
* ...
*
* @param string $email
* @param SalesChannelContext $salesChannelContext
*
* @return CustomerEntity|null
*/
private function getCustomerByEmail(string $email, SalesChannelContext $salesChannelContext)
{
// create criteria
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('customer.email', $email));
$criteria->addFilter(new EqualsFilter('customer.guest', 0));
// search it
$result = $this->customerRepository->search($criteria, $salesChannelContext->getContext());
// return the first customer
return $result->first();
}
/**
* ...
*
* @param CustomerEntity $customer
* @param SalesChannelContext $salesChannelContext
*
* @return string|null
*/
private function getCustomerCartToken(CustomerEntity $customer, SalesChannelContext $salesChannelContext)
{
return (string) $this->db->createQueryBuilder()
->select('token')
->from('cart')
->where('customer_id = :id')
->setParameter('id', Uuid::fromHexToBytes($customer->getId()))
->execute()
->fetchColumn();
}
}