<?php
namespace NewsletterSendinblue\Subscriber;
use NewsletterSendinblue\Service\Cart\CartEventProducer;
use NewsletterSendinblue\Service\Cart\SendinblueCartProcessorService;
use NewsletterSendinblue\Service\ConfigService;
use NewsletterSendinblue\Service\Customer\CustomerProducer;
use NewsletterSendinblue\Service\NewsletterRecipient\NewsletterRecipientProducer;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
use Shopware\Core\Checkout\Customer\Event\CustomerRegisterEvent;
use Shopware\Core\Checkout\Customer\Event\GuestCustomerRegisterEvent;
use Shopware\Core\Content\MailTemplate\Service\Event\MailBeforeValidateEvent;
use Shopware\Core\Content\Newsletter\Event\NewsletterConfirmEvent;
use Shopware\Core\Content\Newsletter\Event\NewsletterRegisterEvent;
use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Context;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class NewsletterRecipientSubscriber implements EventSubscriberInterface
{
/**
* @var NewsletterRecipientProducer
*/
private $newsletterRecipientProducer;
/**
* @var CustomerProducer
*/
private $customerProducer;
/**
* @var ConfigService
*/
private $configService;
/**
* @var SystemConfigService
*/
private $systemConfigService;
/**
* @var EntityRepositoryInterface
*/
private $mailTemplateRepository;
/**
* @var EntityRepositoryInterface
*/
private $customerRepository;
/**
* @var RequestStack;
*/
private $requestStack;
/**
* @var SendinblueCartProcessorService
*/
private $cartProcessorService;
/**
* @var CartEventProducer
*/
private $cartEventProducer;
/**
* @var CartService
*/
private $cartService;
/**
* @param NewsletterRecipientProducer $newsletterRecipientProducer
* @param CustomerProducer $customerProducer
* @param ConfigService $configService
* @param SystemConfigService $systemConfigService
* @param EntityRepositoryInterface $mailTemplateRepository
* @param EntityRepositoryInterface $customerRepository
* @param RequestStack $requestStack
* @param SendinblueCartProcessorService $cartProcessorService
* @param CartEventProducer $cartEventProducer
* @param CartService $cartService
*/
public function __construct(
NewsletterRecipientProducer $newsletterRecipientProducer,
CustomerProducer $customerProducer,
ConfigService $configService,
SystemConfigService $systemConfigService,
EntityRepositoryInterface $mailTemplateRepository,
EntityRepositoryInterface $customerRepository,
RequestStack $requestStack,
SendinblueCartProcessorService $cartProcessorService,
CartEventProducer $cartEventProducer,
CartService $cartService
)
{
$this->newsletterRecipientProducer = $newsletterRecipientProducer;
$this->customerProducer = $customerProducer;
$this->configService = $configService;
$this->systemConfigService = $systemConfigService;
$this->mailTemplateRepository = $mailTemplateRepository;
$this->customerRepository = $customerRepository;
$this->requestStack = $requestStack;
$this->cartProcessorService = $cartProcessorService;
$this->cartEventProducer = $cartEventProducer;
$this->cartService = $cartService;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
NewsletterConfirmEvent::class => 'onNewsletterConfirmEvent',
NewsletterRegisterEvent::class => 'onNewsletterRegisterEvent',
EntityWrittenContainerEvent::class => 'onEntityWrittenContainerEvent',
MailBeforeValidateEvent::class => 'onMailBeforeValidateEvent',
CustomerRegisterEvent::class => 'onCustomerRegisterEvent',
GuestCustomerRegisterEvent::class => 'onCustomerRegisterEvent',
CustomerLoginEvent::class => 'onCustomerLoginEvent',
];
}
/**
* @param CustomerLoginEvent $event
* @return void
*/
public function onCustomerLoginEvent(CustomerLoginEvent $event)
{
if (method_exists($event, 'getSalesChannelContext') && $event->getSalesChannelContext() instanceof SalesChannelContext) {
$salesChannelId = $event->getSalesChannelContext()->getSalesChannelId();
} else if (method_exists($event, 'getContext') && $event->getContext() instanceof SalesChannelContext) {
$salesChannelId = $event->getContext()->getSalesChannelId();
} else {
return;
}
$this->configService->setSalesChannelId($salesChannelId);
if ($this->configService->isAbandonedCartTrackingEnabled()) {
$this->cartProcessorService->setShouldCollectData(true);
}
}
/**
* @param CustomerRegisterEvent $event
*/
public function onCustomerRegisterEvent(CustomerRegisterEvent $event): void
{
$this->configService->setSalesChannelId($event->getSalesChannelId());
if ($this->configService->isFullCustomerSyncEnabled()) {
$customer = $event->getCustomer();
$this->customerRepository->update([[
'id' => $customer->getId(),
'customFields' => [
CustomerProducer::FULL_SYNC_CUSTOMER_REGISTERED => true
]
]], $event->getContext());
$this->customerProducer->confirmContact($customer, $event->getContext(), $event->getSalesChannelId());
}
if ($this->configService->isAbandonedCartTrackingEnabled()) {
$salesChannelContext = $event->getSalesChannelContext();
$cart = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
if ($cart->getLineItems()->count() > 0) {
$this->cartEventProducer->processCart($cart, $salesChannelContext);
}
}
}
/**
* @param NewsletterConfirmEvent $event
*/
public function onNewsletterConfirmEvent(NewsletterConfirmEvent $event): void
{
$this->newsletterRecipientProducer->confirmContact($event->getNewsletterRecipient(), $event->getContext(), $event->getSalesChannelId());
$this->updateCoreConfigDoubleOptIn();
}
/**
* @param NewsletterRegisterEvent $event
*/
public function onNewsletterRegisterEvent(NewsletterRegisterEvent $event): void
{
$this->configService->setSalesChannelId($event->getSalesChannelId());
if ($this->configService->getSubscriptionMailing()) {
$this->newsletterRecipientProducer->confirmContact($event->getNewsletterRecipient(), $event->getContext(), $event->getSalesChannelId());
$this->updateCoreConfigDoubleOptIn();
}
}
/**
* @param EntityWrittenContainerEvent $event
*/
public function onEntityWrittenContainerEvent(EntityWrittenContainerEvent $event): void
{
foreach ($event->getEvents()->getElements() as $eventElement) {
if ($eventElement->getName() === 'newsletter_recipient.written') {
foreach ($eventElement->getWriteResults() as $writeResult) {
if ($writeResult->getOperation() === 'update') {
$payload = $writeResult->getPayload();
$newsletterRecipientId = $writeResult->getPrimaryKey();
$salesChannelId = $payload['salesChannelId'] ?? null;
if (empty($salesChannelId)) {
if ($event->getContext()->getSource() instanceof SalesChannelApiSource) {
$salesChannelId = $event->getContext()->getSource()->getSalesChannelId();
}
}
if (isset($payload['status']) && $payload['status'] == 'optOut') {
$this->newsletterRecipientProducer->unsubscribeContact($newsletterRecipientId, $event->getContext(), $salesChannelId);
} else {
$this->newsletterRecipientProducer->updateContact($newsletterRecipientId, $event->getContext(), $salesChannelId);
}
}
}
}
if ($eventElement->getName() === 'customer.written') {
foreach ($eventElement->getWriteResults() as $writeResult) {
if ($writeResult->getOperation() === 'update') {
$payload = $writeResult->getPayload();
// Skip if customer is created - not call updateContact
if (array_key_exists('customFields', $payload)
&& array_key_exists(CustomerProducer::FULL_SYNC_CUSTOMER_REGISTERED, $payload['customFields'])
&& count($payload['customFields']) == 1
) {
continue;
}
if (array_key_exists('lastLogin', $payload)
|| array_key_exists('remoteAddress', $payload)
) {
continue;
}
$customerId = $writeResult->getPrimaryKey();
$salesChannelId = $payload['salesChannelId'] ?? null;
if (empty($salesChannelId)) {
if ($event->getContext()->getSource() instanceof SalesChannelApiSource) {
$salesChannelId = $event->getContext()->getSource()->getSalesChannelId();
}
}
$this->customerProducer->updateContact($customerId, $event->getContext(), $salesChannelId);
}
}
}
}
}
/**
* @param MailBeforeValidateEvent $event
*/
public function onMailBeforeValidateEvent(MailBeforeValidateEvent $event): void
{
$data = $event->getData();
$templateData = $event->getTemplateData();
$context = $event->getContext();
if (!isset($templateData['newsletterRecipient']) || !isset($data['templateId'])) {
return;
}
if (!$this->isNewsletterMailTemplate($data['templateId'], $context)) {
return;
}
$salesChannelId = null;
if (!empty($data['salesChannelId'])) {
$salesChannelId = $data['salesChannelId'];
}
if (empty($salesChannelId) && method_exists($context->getSource(), 'getSalesChannelId')) {
$salesChannelId = $context->getSource()->getSalesChannelId();
}
if (!empty($salesChannelId)) {
$this->configService->setSalesChannelId($salesChannelId);
}
if (!$this->configService->getSubscriptionMailing()) {
return;
}
$event->stopPropagation();
}
/**
* @param string $templateId
* @param Context $context
* @return bool
*/
private function isNewsletterMailTemplate(string $templateId, Context $context)
{
$criteria = new Criteria([$templateId]);
$criteria->addAssociation('mailTemplateType');
$mailTemplate = $this->mailTemplateRepository->search($criteria, $context)->first();
if (empty($mailTemplate)) {
return false;
}
return in_array($mailTemplate->getMailTemplateType()->getTechnicalName(), ['newsletterDoubleOptIn', 'newsletterRegister']);
}
/**
* @return void
*/
private function updateCoreConfigDoubleOptIn(): void
{
if ($this->requestStack
&& $this->requestStack->getCurrentRequest()
&& $this->requestStack->getCurrentRequest()->hasSession()
) {
$session = $this->requestStack->getSession();
if ($session->has('sbCoreDoubleOptIn')) {
$this->systemConfigService->set('core.newsletter.doubleOptIn', $session->get('sbCoreDoubleOptIn'));
$session->remove('sbCoreDoubleOptIn');
}
}
}
}