From db9b51e12a6eb157eb79a3cb66623f0d79e8911a Mon Sep 17 00:00:00 2001 From: Pavel Dohnal Date: Wed, 28 Oct 2020 11:26:48 +0100 Subject: [PATCH] Add a bulk unsubscribe server side action [MAILPOET-2978] --- lib/API/JSON/v1/Subscribers.php | 2 ++ lib/Subscribers/SubscribersRepository.php | 16 +++++++-- .../Subscribers/SubscribersRepositoryTest.php | 35 ++++++++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/API/JSON/v1/Subscribers.php b/lib/API/JSON/v1/Subscribers.php index 4c1fbfb555..968a52688c 100644 --- a/lib/API/JSON/v1/Subscribers.php +++ b/lib/API/JSON/v1/Subscribers.php @@ -513,6 +513,8 @@ class Subscribers extends APIEndpoint { $count = $this->subscribersRepository->bulkAddToSegment($segment, $ids); } elseif ($data['action'] === 'moveToList' && $segment instanceof SegmentEntity) { $count = $this->subscribersRepository->bulkMoveToSegment($segment, $ids); + } elseif ($data['action'] === 'unsubscribe') { + $count = $this->subscribersRepository->bulkUnsubscribe($ids); } else { throw UnexpectedValueException::create() ->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]); diff --git a/lib/Subscribers/SubscribersRepository.php b/lib/Subscribers/SubscribersRepository.php index 048fb75ff1..34c085e10e 100644 --- a/lib/Subscribers/SubscribersRepository.php +++ b/lib/Subscribers/SubscribersRepository.php @@ -177,7 +177,7 @@ class SubscribersRepository extends Repository { ->setParameter('ids', $ids) ->setParameter('segment', $segment) ->getQuery()->execute(); - + $this->entityManager->transactional(function (EntityManager $entityManager) use ($subscribers, $segment) { foreach ($subscribers as $subscriber) { $subscriberSegment = new SubscriberSegmentEntity($segment, $subscriber, SubscriberEntity::STATUS_SUBSCRIBED); @@ -199,5 +199,17 @@ class SubscribersRepository extends Repository { $this->bulkRemoveFromAllSegments($ids); return $this->bulkAddToSegment($segment, $ids); - } + } + + public function bulkUnsubscribe(array $ids): int { + $this->entityManager->createQueryBuilder() + ->update(SubscriberEntity::class, 's') + ->set('s.status', ':status') + ->where('s.id IN (:ids)') + ->setParameter('status', SubscriberEntity::STATUS_UNSUBSCRIBED) + ->setParameter('ids', $ids) + ->getQuery()->execute(); + + return count($ids); + } } diff --git a/tests/integration/Subscribers/SubscribersRepositoryTest.php b/tests/integration/Subscribers/SubscribersRepositoryTest.php index 084e974131..86676b367b 100644 --- a/tests/integration/Subscribers/SubscribersRepositoryTest.php +++ b/tests/integration/Subscribers/SubscribersRepositoryTest.php @@ -104,7 +104,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { // don't restored subscriber $subscriberTwo = $this->repository->findOneById($subscriberTwoId); assert($subscriberTwo instanceof SubscriberEntity); - expect($subscriberTwo->getDeletedAt())->notNull(); + expect($subscriberTwo->getDeletedAt())->notNull(); expect($this->subscriberSegmentRepository->findOneBy(['subscriber' => $subscriberTwoId]))->notNull(); expect($this->subscriberCustomFieldRepository->findOneBy(['subscriber' => $subscriberTwoId]))->notNull(); } @@ -141,7 +141,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { ]))->notNull(); // subscriber with removed segment two - expect($this->repository->findOneById($subscriberTwoId))->notNull(); + expect($this->repository->findOneById($subscriberTwoId))->notNull(); expect($this->subscriberSegmentRepository->findOneBy([ 'subscriber' => $subscriberTwoId, 'segment' => $segmentTwoId, @@ -152,6 +152,30 @@ class SubscribersRepositoryTest extends \MailPoetTest { ]))->notNull(); } + public function testItBulkUnsubscribes(): void { + $subscriberOne = $this->createSubscriber('one@removeAll.com', new DateTimeImmutable()); + $subscriberTwo = $this->createSubscriber('two@removeAll.com', new DateTimeImmutable()); + + $subscriberOneId = $subscriberOne->getId(); + $subscriberTwoId = $subscriberTwo->getId(); + + $this->repository->bulkUnsubscribe([$subscriberOneId]); + + $this->entityManager->clear(); + + // subscriber with removed segments + $unsubscribedSubscriber = $this->repository->findOneById($subscriberOneId); + expect($unsubscribedSubscriber)->notNull(); + assert($unsubscribedSubscriber instanceof SubscriberEntity); + expect($unsubscribedSubscriber->getStatus())->equals(SubscriberEntity::STATUS_UNSUBSCRIBED); + + // subscriber still subscribed + $subscribedSubscriber = $this->repository->findOneById($subscriberTwoId); + expect($subscribedSubscriber)->notNull(); + assert($subscribedSubscriber instanceof SubscriberEntity); + expect($subscribedSubscriber->getStatus())->equals(SubscriberEntity::STATUS_SUBSCRIBED); + } + public function testItBulkRemoveSubscriberFromAllSegments(): void { $subscriberOne = $this->createSubscriber('one@removeAll.com', new DateTimeImmutable()); $subscriberTwo = $this->createSubscriber('two@removeAll.com', new DateTimeImmutable()); @@ -176,7 +200,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { expect($this->subscriberSegmentRepository->findBy(['subscriber' => $subscriberOneId]))->count(0); // subscriber with segments - expect($this->repository->findOneById($subscriberTwoId))->notNull(); + expect($this->repository->findOneById($subscriberTwoId))->notNull(); expect($this->subscriberSegmentRepository->findOneBy([ 'subscriber' => $subscriberTwoId, 'segment' => $segmentOneId, @@ -208,7 +232,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { ]))->notNull(); // subscriber without segment - expect($this->repository->findOneById($subscriberTwoId))->notNull(); + expect($this->repository->findOneById($subscriberTwoId))->notNull(); expect($this->subscriberSegmentRepository->findBy(['subscriber' => $subscriberTwoId]))->count(0); } @@ -241,7 +265,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { ]))->notNull(); // subscriber which stay in segment two - expect($this->repository->findOneById($subscriberTwoId))->notNull(); + expect($this->repository->findOneById($subscriberTwoId))->notNull(); expect($this->subscriberSegmentRepository->findOneBy([ 'subscriber' => $subscriberTwoId, 'segment' => $segmentOneId, @@ -283,6 +307,7 @@ class SubscribersRepositoryTest extends \MailPoetTest { $subscriber->setEmail($email); $subscriber->setFirstName('John'); $subscriber->setLastName('Doe'); + $subscriber->setStatus(SubscriberEntity::STATUS_SUBSCRIBED); $subscriber->setDeletedAt($deletedAt); $this->entityManager->persist($subscriber); $this->entityManager->flush();