custom/plugins/NewsletterSendinblue/src/Subscriber/NewsletterRecipientSubscriber.php line 164

Open in your IDE?
  1. <?php
  2. namespace NewsletterSendinblue\Subscriber;
  3. use NewsletterSendinblue\NewsletterSendinblue;
  4. use NewsletterSendinblue\Service\Cart\CartEventProducer;
  5. use NewsletterSendinblue\Service\Cart\SendinblueCartProcessorService;
  6. use NewsletterSendinblue\Service\ConfigService;
  7. use NewsletterSendinblue\Service\Constant;
  8. use NewsletterSendinblue\Service\Customer\CustomerProducer;
  9. use NewsletterSendinblue\Service\NewsletterRecipient\NewsletterRecipientProducer;
  10. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  11. use Shopware\Core\Checkout\Customer\CustomerEntity;
  12. use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
  13. use Shopware\Core\Checkout\Customer\Event\CustomerRegisterEvent;
  14. use Shopware\Core\Checkout\Customer\Event\GuestCustomerRegisterEvent;
  15. use Shopware\Core\Content\MailTemplate\Service\Event\MailBeforeValidateEvent;
  16. use Shopware\Core\Content\Newsletter\Event\NewsletterConfirmEvent;
  17. use Shopware\Core\Content\Newsletter\Event\NewsletterRegisterEvent;
  18. use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
  19. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  22. use Shopware\Core\Framework\Context;
  23. use Shopware\Core\Framework\Struct\ArrayStruct;
  24. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  25. use Shopware\Core\System\SystemConfig\SystemConfigService;
  26. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  27. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  28. use Symfony\Component\HttpFoundation\RequestStack;
  29. class NewsletterRecipientSubscriber implements EventSubscriberInterface
  30. {
  31.     /**
  32.      * @var NewsletterRecipientProducer
  33.      */
  34.     private $newsletterRecipientProducer;
  35.     /**
  36.      * @var CustomerProducer
  37.      */
  38.     private $customerProducer;
  39.     /**
  40.      * @var ConfigService
  41.      */
  42.     private $configService;
  43.     /**
  44.      * @var SystemConfigService
  45.      */
  46.     private $systemConfigService;
  47.     /**
  48.      * @var EntityRepositoryInterface
  49.      */
  50.     private $mailTemplateRepository;
  51.     /**
  52.      * @var EntityRepositoryInterface
  53.      */
  54.     private $customerRepository;
  55.     /**
  56.      * @var RequestStack;
  57.      */
  58.     private $requestStack;
  59.     /**
  60.      * @var SendinblueCartProcessorService
  61.      */
  62.     private $cartProcessorService;
  63.     /**
  64.      * @var CartEventProducer
  65.      */
  66.     private $cartEventProducer;
  67.     /**
  68.      * @var CartService
  69.      */
  70.     private $cartService;
  71.     /**
  72.      * @var bool
  73.      */
  74.     private $isRecipientAlreadyUpdated false;
  75.     /**
  76.      * @param NewsletterRecipientProducer $newsletterRecipientProducer
  77.      * @param CustomerProducer $customerProducer
  78.      * @param ConfigService $configService
  79.      * @param SystemConfigService $systemConfigService
  80.      * @param EntityRepositoryInterface $mailTemplateRepository
  81.      * @param EntityRepositoryInterface $customerRepository
  82.      * @param RequestStack $requestStack
  83.      * @param SendinblueCartProcessorService $cartProcessorService
  84.      * @param CartEventProducer $cartEventProducer
  85.      * @param CartService $cartService
  86.      */
  87.     public function __construct(
  88.         NewsletterRecipientProducer    $newsletterRecipientProducer,
  89.         CustomerProducer               $customerProducer,
  90.         ConfigService                  $configService,
  91.         SystemConfigService            $systemConfigService,
  92.         EntityRepositoryInterface      $mailTemplateRepository,
  93.         EntityRepositoryInterface      $customerRepository,
  94.         RequestStack                   $requestStack,
  95.         SendinblueCartProcessorService $cartProcessorService,
  96.         CartEventProducer              $cartEventProducer,
  97.         CartService                    $cartService
  98.     )
  99.     {
  100.         $this->newsletterRecipientProducer $newsletterRecipientProducer;
  101.         $this->customerProducer $customerProducer;
  102.         $this->configService $configService;
  103.         $this->systemConfigService $systemConfigService;
  104.         $this->mailTemplateRepository $mailTemplateRepository;
  105.         $this->customerRepository $customerRepository;
  106.         $this->requestStack $requestStack;
  107.         $this->cartProcessorService $cartProcessorService;
  108.         $this->cartEventProducer $cartEventProducer;
  109.         $this->cartService $cartService;
  110.     }
  111.     /**
  112.      * @return array
  113.      */
  114.     public static function getSubscribedEvents(): array
  115.     {
  116.         return [
  117.             NewsletterConfirmEvent::class => 'onNewsletterConfirmEvent',
  118.             NewsletterRegisterEvent::class => 'onNewsletterRegisterEvent',
  119.             EntityWrittenContainerEvent::class => 'onEntityWrittenContainerEvent',
  120.             MailBeforeValidateEvent::class => 'onMailBeforeValidateEvent',
  121.             CustomerRegisterEvent::class => 'onCustomerRegisterEvent',
  122.             GuestCustomerRegisterEvent::class => 'onCustomerRegisterEvent',
  123.             CustomerLoginEvent::class => 'onCustomerLoginEvent',
  124.         ];
  125.     }
  126.     /**
  127.      * @param CustomerLoginEvent $event
  128.      * @return void
  129.      */
  130.     public function onCustomerLoginEvent(CustomerLoginEvent $event)
  131.     {
  132.         if (method_exists($event'getSalesChannelContext') && $event->getSalesChannelContext() instanceof SalesChannelContext) {
  133.             $salesChannelId $event->getSalesChannelContext()->getSalesChannelId();
  134.         } else if (method_exists($event'getContext') && $event->getContext() instanceof SalesChannelContext) {
  135.             $salesChannelId $event->getContext()->getSalesChannelId();
  136.         } else {
  137.             return;
  138.         }
  139.         $this->configService->setSalesChannelId($salesChannelId);
  140.         if ($this->configService->isAbandonedCartTrackingEnabled()) {
  141.             $this->cartProcessorService->setShouldCollectData(true);
  142.         }
  143.     }
  144.     /**
  145.      * @param CustomerRegisterEvent $event
  146.      */
  147.     public function onCustomerRegisterEvent(CustomerRegisterEvent $event): void
  148.     {
  149.         // Determine the location of register: signup or checkout
  150.         $subscriptionLocation CustomerProducer::SUBSCRIPTION_LOCATION_SIGNUP;
  151.         if ($this->requestStack
  152.             && $this->requestStack->getCurrentRequest()
  153.             && $this->requestStack->getCurrentRequest()->server->has('HTTP_REFERER')
  154.         ) {
  155.             $referrer $this->requestStack->getCurrentRequest()->server->get('HTTP_REFERER');
  156.             if ($referrer && strpos($referrer'/checkout') !== false) {
  157.                 $subscriptionLocation CustomerProducer::SUBSCRIPTION_LOCATION_ORDER_CHECKOUT;
  158.             }
  159.         }
  160.         $event->getContext()->addExtension(CustomerProducer::SUBSCRIPTION_LOCATION_EXT, new ArrayStruct([
  161.             'value' => $subscriptionLocation
  162.         ]));
  163.         $this->configService->setSalesChannelId($event->getSalesChannelId());
  164.         if ($this->configService->isFullCustomerSyncEnabled()) {
  165.             $customer $event->getCustomer();
  166.             $this->customerRepository->update([[
  167.                 'id' => $customer->getId(),
  168.                 'customFields' => [
  169.                     CustomerProducer::FULL_SYNC_CUSTOMER_REGISTERED => true
  170.                 ]
  171.             ]], $event->getContext());
  172.             $this->customerProducer->setAdditionalUpdateData([
  173.                 Constant::OPT_IN_ENABLED => 'false',
  174.                 Constant::OPT_IN_CHECKED => 'false'
  175.             ]);
  176.             $this->customerProducer->confirmContact($customer$event->getContext(), $event->getSalesChannelId());
  177.         }
  178.         if ($this->configService->isAbandonedCartTrackingEnabled()) {
  179.             $salesChannelContext $event->getSalesChannelContext();
  180.             $cart $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
  181.             if ($cart->getLineItems()->count() > 0) {
  182.                 $this->cartEventProducer->processCart($cart$salesChannelContext);
  183.             }
  184.         }
  185.     }
  186.     /**
  187.      * @param NewsletterConfirmEvent $event
  188.      */
  189.     public function onNewsletterConfirmEvent(NewsletterConfirmEvent $event): void
  190.     {
  191.         /**
  192.          * This method is triggered when:
  193.          * 1. DOI (Double Opt-In) is enabled, after the user confirms via email.
  194.          * 2. DOI is not enabled, immediately after newsletter registration.
  195.          */
  196.         if ($this->isRecipientAlreadyUpdated) {
  197.             $this->isRecipientAlreadyUpdated false;
  198.             return;
  199.         }
  200.         $this->newsletterRecipientProducer->setAdditionalUpdateData([
  201.             Constant::OPT_IN_ENABLED => 'true',
  202.             Constant::OPT_IN_CHECKED => 'true'
  203.         ]);
  204.         $this->newsletterRecipientProducer->confirmContact($event->getNewsletterRecipient(), $event->getContext(), $event->getSalesChannelId());
  205.     }
  206.     /**
  207.      * @param NewsletterRegisterEvent $event
  208.      */
  209.     public function onNewsletterRegisterEvent(NewsletterRegisterEvent $event): void
  210.     {
  211.         $this->configService->setSalesChannelId($event->getSalesChannelId());
  212.         if ($this->configService->getSubscriptionMailing()) {
  213.             $this->newsletterRecipientProducer->setAdditionalUpdateData([
  214.                 Constant::OPT_IN_ENABLED => 'true',
  215.                 Constant::OPT_IN_CHECKED => 'true'
  216.             ]);
  217.             $this->newsletterRecipientProducer->confirmContact($event->getNewsletterRecipient(), $event->getContext(), $event->getSalesChannelId());
  218.             if ($this->requestStack
  219.                 && $this->requestStack->getCurrentRequest()
  220.                 && $this->requestStack->getCurrentRequest()->hasSession()
  221.                 && $this->requestStack->getSession()->has('sbCoreDoubleOptIn')
  222.             ) {
  223.                 $session $this->requestStack->getSession();
  224.                 $this->systemConfigService->set('core.newsletter.doubleOptIn'$session->get('sbCoreDoubleOptIn'));
  225.                 $session->remove('sbCoreDoubleOptIn');
  226.             }
  227.         }
  228.     }
  229.     /**
  230.      * @param EntityWrittenContainerEvent $event
  231.      */
  232.     public function onEntityWrittenContainerEvent(EntityWrittenContainerEvent $event): void
  233.     {
  234.         // Stop sending calls to Brevo if they are coming from Brevo UI
  235.         if ($event->getContext()->hasExtension('brevoUiUnsubscribe')
  236.             || $event->getContext()->hasExtension('brevoUiSubscribe')
  237.             || $event->getContext()->hasExtension(NewsletterSendinblue::IGNORE_EVENT)
  238.         ) {
  239.             return;
  240.         }
  241.         foreach ($event->getEvents()->getElements() as $eventElement) {
  242.             if ($eventElement->getName() === 'newsletter_recipient.written') {
  243.                 foreach ($eventElement->getWriteResults() as $writeResult) {
  244.                     if ($writeResult->getOperation() === 'update') {
  245.                         $payload $writeResult->getPayload();
  246.                         $newsletterRecipientId $writeResult->getPrimaryKey();
  247.                         $salesChannelId $payload['salesChannelId'] ?? null;
  248.                         if (empty($salesChannelId)) {
  249.                             if ($event->getContext()->getSource() instanceof SalesChannelApiSource) {
  250.                                 $salesChannelId $event->getContext()->getSource()->getSalesChannelId();
  251.                             }
  252.                         }
  253.                         if (isset($payload['status']) && $payload['status'] == 'optOut') {
  254.                             $this->newsletterRecipientProducer->unsubscribeContact($newsletterRecipientId$event->getContext(), $salesChannelId);
  255.                         } else {
  256.                             $this->newsletterRecipientProducer->setAdditionalUpdateData([
  257.                                 Constant::OPT_IN_ENABLED => 'true',
  258.                                 Constant::OPT_IN_CHECKED => 'true'
  259.                             ]);
  260.                             $this->newsletterRecipientProducer->updateContact($newsletterRecipientId$event->getContext(), $salesChannelId);
  261.                             $this->isRecipientAlreadyUpdated true;
  262.                         }
  263.                     }
  264.                 }
  265.             }
  266.             if ($eventElement->getName() === 'customer.written') {
  267.                 foreach ($eventElement->getWriteResults() as $writeResult) {
  268.                     if ($writeResult->getOperation() === 'update') {
  269.                         $payload $writeResult->getPayload();
  270.                         // Skip if customer is created - not call updateContact
  271.                         if (array_key_exists('customFields'$payload)
  272.                             && count($payload['customFields']) == 1
  273.                             && (array_key_exists(CustomerProducer::FULL_SYNC_CUSTOMER_REGISTERED$payload['customFields'])
  274.                                 || array_key_exists(CustomerProducer::SW_SUB_STATUS_ATTR$payload['customFields']))
  275.                         ) {
  276.                             continue;
  277.                         }
  278.                         if (array_key_exists('lastLogin'$payload)
  279.                             || array_key_exists('remoteAddress'$payload)
  280.                         ) {
  281.                             continue;
  282.                         }
  283.                         if (count($payload) === 3
  284.                             && array_key_exists('newsletter'$payload)
  285.                             && array_key_exists('updatedAt'$payload)
  286.                             && !empty($payload['updatedAt'])
  287.                         ) {
  288.                             continue;
  289.                         }
  290.                         $customerId $writeResult->getPrimaryKey();
  291.                         $salesChannelId $payload['salesChannelId'] ?? null;
  292.                         if (empty($salesChannelId)) {
  293.                             if ($event->getContext()->getSource() instanceof SalesChannelApiSource) {
  294.                                 $salesChannelId $event->getContext()->getSource()->getSalesChannelId();
  295.                             }
  296.                         }
  297.                         $this->customerProducer->updateContact($customerId$event->getContext(), $salesChannelId);
  298.                     }
  299.                 }
  300.             }
  301.         }
  302.     }
  303.     /**
  304.      * @param MailBeforeValidateEvent $event
  305.      */
  306.     public function onMailBeforeValidateEvent(MailBeforeValidateEvent $event): void
  307.     {
  308.         $data $event->getData();
  309.         $templateData $event->getTemplateData();
  310.         $context $event->getContext();
  311.         if (!isset($templateData['newsletterRecipient']) || !isset($data['templateId'])) {
  312.             return;
  313.         }
  314.         if (!$this->isNewsletterMailTemplate($data['templateId'], $context)) {
  315.             return;
  316.         }
  317.         $salesChannelId null;
  318.         if (!empty($data['salesChannelId'])) {
  319.             $salesChannelId $data['salesChannelId'];
  320.         }
  321.         if (empty($salesChannelId) && method_exists($context->getSource(), 'getSalesChannelId')) {
  322.             $salesChannelId $context->getSource()->getSalesChannelId();
  323.         }
  324.         if (!empty($salesChannelId)) {
  325.             $this->configService->setSalesChannelId($salesChannelId);
  326.         }
  327.         if (!$this->configService->getSubscriptionMailing()) {
  328.             return;
  329.         }
  330.         $event->stopPropagation();
  331.     }
  332.     /**
  333.      * @param string $templateId
  334.      * @param Context $context
  335.      * @return bool
  336.      */
  337.     private function isNewsletterMailTemplate(string $templateIdContext $context)
  338.     {
  339.         $criteria = new Criteria([$templateId]);
  340.         $criteria->addAssociation('mailTemplateType');
  341.         $mailTemplate $this->mailTemplateRepository->search($criteria$context)->first();
  342.         if (empty($mailTemplate)) {
  343.             return false;
  344.         }
  345.         return in_array($mailTemplate->getMailTemplateType()->getTechnicalName(), ['newsletterDoubleOptIn''newsletterRegister']);
  346.     }
  347. }