vendor/bitbag/mailchimp-plugin/src/Handler/NewsletterSubscriptionHandler.php line 119

Open in your IDE?
  1. <?php
  2. /*
  3. * This file was created by developers working at BitBag
  4. * Do you need more information about us and what we do? Visit our https://bitbag.io website!
  5. * We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
  6. */
  7. declare(strict_types=1);
  8. namespace BitBag\SyliusMailChimpPlugin\Handler;
  9. use Doctrine\ORM\EntityManagerInterface;
  10. use DrewM\MailChimp\MailChimp;
  11. use Sylius\Component\Core\Model\CustomerInterface;
  12. use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
  13. use Sylius\Component\Resource\Factory\FactoryInterface;
  14. use Symfony\Component\HttpFoundation\Response;
  15. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  16. use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
  17. use Webmozart\Assert\Assert;
  18. class NewsletterSubscriptionHandler implements NewsletterSubscriptionInterface
  19. {
  20. public const API_PATH_LISTS = 'lists';
  21. public const API_PATH_MEMBERS = 'members';
  22. /** @var CustomerRepositoryInterface */
  23. private $customerRepository;
  24. /** @var FactoryInterface */
  25. private $customerFactory;
  26. /** @var EntityManagerInterface */
  27. private $customerManager;
  28. /** @var string */
  29. private $listId;
  30. /** @var MailChimp */
  31. private $mailChimp;
  32. public function __construct(
  33. CustomerRepositoryInterface $customerRepository,
  34. FactoryInterface $customerFactory,
  35. EntityManagerInterface $customerManager,
  36. MailChimp $mailChimp,
  37. string $listId
  38. ) {
  39. $this->customerRepository = $customerRepository;
  40. $this->customerFactory = $customerFactory;
  41. $this->customerManager = $customerManager;
  42. $this->mailChimp = $mailChimp;
  43. $this->listId = $listId;
  44. }
  45. public function subscribe(string $email): void
  46. {
  47. $customer = $this->customerRepository->findOneBy(['email' => $email]);
  48. if (!$customer instanceof CustomerInterface) {
  49. $customer = $this->createNewCustomer($email);
  50. }
  51. $this->addMailchimpData($email);
  52. $customer->setSubscribedToNewsletter(true);
  53. $this->customerManager->flush();
  54. }
  55. public function getValidMailchimpListIds(): array
  56. {
  57. $responseArray = $this->mailChimp->get(self::API_PATH_LISTS);
  58. $ids = [];
  59. if (false === $responseArray) {
  60. return $ids;
  61. }
  62. $lists = $responseArray['lists'];
  63. foreach ($lists as $list) {
  64. $ids[] = $list['id'];
  65. }
  66. return $ids;
  67. }
  68. public function unsubscribe(CustomerInterface $customer): void
  69. {
  70. $this->updateCustomer($customer, false);
  71. $email = $customer->getEmail();
  72. if (null !== $email) {
  73. $this->unsubscribeEmail($email);
  74. }
  75. }
  76. public function unsubscribeEmail(string $email): void
  77. {
  78. $this->mailChimp->delete($this->getListMemberEndpoint($email));
  79. }
  80. public function unsubscribeCustomerFromLocalDatabase(string $email): void
  81. {
  82. $customer = $this->customerRepository->findOneBy(['email' => $email]);
  83. $this->updateCustomer($customer, false);
  84. }
  85. private function createNewCustomer(string $email): CustomerInterface
  86. {
  87. /** @var CustomerInterface $customer */
  88. $customer = $this->customerFactory->createNew();
  89. $customer->setEmail($email);
  90. $this->customerRepository->add($customer);
  91. return $customer;
  92. }
  93. private function exportNewEmail(string $email): void
  94. {
  95. $response = $this->mailChimp->post($this->getListMemberEndpoint(), [
  96. 'email_address' => $email,
  97. 'status' => 'subscribed',
  98. ]);
  99. if (false === $response) {
  100. throw new BadRequestHttpException(
  101. sprintf('Mailchimp returned false instead of response array, last error : %s',
  102. $this->mailChimp->getLastError())
  103. );
  104. }
  105. Assert::keyExists($response, 'status');
  106. if ($response['status'] === Response::HTTP_NOT_FOUND) {
  107. $validListIds = $this->getValidMailchimpListIds();
  108. $concatenatedList = implode(',', $validListIds);
  109. throw new BadRequestHttpException(
  110. sprintf('Mailchimp returned %1$i code, is the MAIL_CHIMP_LIST_ID [ %2$s ] one of available ones: [ %3$s ] ?',
  111. Response::HTTP_NOT_FOUND,
  112. $this->listId,
  113. $concatenatedList
  114. )
  115. );
  116. }
  117. if ($response['status'] !== 'subscribed') {
  118. throw new BadRequestHttpException(
  119. sprintf('Response status is %s instead of %s', $response['status'], 'subscribed')
  120. );
  121. }
  122. }
  123. private function updateCustomer(CustomerInterface $customer, bool $subscribedToNewsletter = true): void
  124. {
  125. $customer->setSubscribedToNewsletter($subscribedToNewsletter);
  126. $this->customerManager->flush();
  127. }
  128. private function getEmailHash(string $email): string
  129. {
  130. return md5(strtolower($email));
  131. }
  132. protected function addMailchimpData(string $email): void
  133. {
  134. $response = $this->mailChimp->get($this->getListMemberEndpoint($email));
  135. if (false === $response) {
  136. throw new BadRequestHttpException(
  137. sprintf('Mailchimp returned false instead of response array, last error : %s',
  138. $this->mailChimp->getLastError())
  139. );
  140. }
  141. Assert::keyExists($response, 'status');
  142. if (Response::HTTP_UNAUTHORIZED === $response['status']) {
  143. Assert::keyExists($response, 'detail');
  144. throw new UnauthorizedHttpException('Mailchimp', $response['detail']);
  145. }
  146. if (Response::HTTP_NOT_FOUND === $response['status']) {
  147. $this->exportNewEmail($email);
  148. }
  149. }
  150. private function getListMemberEndpoint(string $email = null): string
  151. {
  152. $parts = [
  153. self::API_PATH_LISTS,
  154. $this->listId,
  155. self::API_PATH_MEMBERS,
  156. ];
  157. if (null !== $email) {
  158. $parts[] = $this->getEmailHash($email);
  159. }
  160. return implode('/', $parts);
  161. }
  162. }