From 01b61abc28d2b9c9e8c54e1a544e5bec1fd273af Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Mon, 8 Jun 2020 16:14:25 +0200 Subject: [PATCH] Add deleting closely related entities into newsletter bulk delete [MAILPOET-2898] --- lib/Newsletter/NewslettersRepository.php | 38 ++++++++- .../Newsletter/NewsletterRepositoryTest.php | 79 +++++++++++++++++-- 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/lib/Newsletter/NewslettersRepository.php b/lib/Newsletter/NewslettersRepository.php index e023fc2e64..4065b98fbe 100644 --- a/lib/Newsletter/NewslettersRepository.php +++ b/lib/Newsletter/NewslettersRepository.php @@ -10,6 +10,7 @@ use MailPoet\AutomaticEmails\WooCommerce\Events\PurchasedProduct; use MailPoet\Doctrine\Repository; use MailPoet\Entities\NewsletterEntity; use MailPoet\Entities\NewsletterOptionFieldEntity; +use MailPoet\Entities\NewsletterSegmentEntity; use MailPoet\Entities\ScheduledTaskEntity; use MailPoet\Entities\SendingQueueEntity; use MailPoetVendor\Carbon\Carbon; @@ -133,7 +134,7 @@ class NewslettersRepository extends Repository { WHERE q.`newsletter_id` IN (:ids) ", ['ids' => $ids], ['ids' => Connection::PARAM_INT_ARRAY]); - // Trash scheduled tasks + // Trash sending queues tasks $this->entityManager->getConnection()->executeUpdate(" UPDATE $sendingQueueTable q SET q.`deleted_at` = NOW() @@ -201,10 +202,45 @@ class NewslettersRepository extends Repository { } public function bulkDelete(array $ids) { + // Delete children + $childrenIds = $this->entityManager->createQueryBuilder()->select( 'n.id') + ->from(NewsletterEntity::class, 'n') + ->where('n.parent IN (:ids)') + ->setParameter('ids', $ids) + ->getQuery()->getScalarResult(); + $deletedChildrenCount = 0; + if (count($childrenIds)) { + $deletedChildrenCount = $this->bulkDelete(array_column($childrenIds, 'id')); + } + + // Delete scheduled tasks and queues + $scheduledTasksTable = $this->entityManager->getClassMetadata(ScheduledTaskEntity::class)->getTableName(); + $sendingQueueTable = $this->entityManager->getClassMetadata(SendingQueueEntity::class)->getTableName(); + $this->entityManager->getConnection()->executeUpdate(" + DELETE t FROM $scheduledTasksTable t + JOIN $sendingQueueTable q ON t.`id` = q.`task_id` + WHERE q.`newsletter_id` IN (:ids) + ", ['ids' => $ids], ['ids' => Connection::PARAM_INT_ARRAY]); + + // Delete sending queues + $this->entityManager->getConnection()->executeUpdate(" + DELETE q FROM $sendingQueueTable q + WHERE q.`newsletter_id` IN (:ids) + ", ['ids' => $ids], ['ids' => Connection::PARAM_INT_ARRAY]); + + // Delete newsletter segments + $newsletterSegmentsTable = $this->entityManager->getClassMetadata(NewsletterSegmentEntity::class)->getTableName(); + $this->entityManager->getConnection()->executeUpdate(" + DELETE ns FROM $newsletterSegmentsTable ns + WHERE ns.`newsletter_id` IN (:ids) + ", ['ids' => $ids], ['ids' => Connection::PARAM_INT_ARRAY]); + $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->delete(NewsletterEntity::class, 'n') ->where('n.id IN (:ids)') ->setParameter('ids', $ids) ->getQuery()->execute(); + + return $deletedChildrenCount + count($ids); } } diff --git a/tests/integration/Newsletter/NewsletterRepositoryTest.php b/tests/integration/Newsletter/NewsletterRepositoryTest.php index 81ed09882c..f72b78a6a4 100644 --- a/tests/integration/Newsletter/NewsletterRepositoryTest.php +++ b/tests/integration/Newsletter/NewsletterRepositoryTest.php @@ -4,7 +4,9 @@ namespace MailPoet\Newsletter; use Codeception\Util\Fixtures; use MailPoet\Entities\NewsletterEntity; +use MailPoet\Entities\NewsletterSegmentEntity; use MailPoet\Entities\ScheduledTaskEntity; +use MailPoet\Entities\SegmentEntity; use MailPoet\Entities\SendingQueueEntity; use MailPoet\Tasks\Sending as SendingTask; @@ -20,10 +22,10 @@ class NewsletterRepositoryTest extends \MailPoetTest { public function testItBulkTrashNewslettersAndChildren() { $newsletter1 = $this->createNewsletter(NewsletterEntity::TYPE_STANDARD); - $this->createQueueWithTask($newsletter1); + $this->createQueueWithTaskAndSegment($newsletter1); $newsletter2 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_ACTIVE); $newsletter2Child1 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_SCHEDULED, $newsletter2); - $this->createQueueWithTask($newsletter2Child1); + $this->createQueueWithTaskAndSegment($newsletter2Child1); $this->repository->bulkTrash([$newsletter1->getId(), $newsletter2->getId()]); $this->entityManager->refresh($newsletter1); $this->entityManager->refresh($newsletter2); @@ -57,10 +59,10 @@ class NewsletterRepositoryTest extends \MailPoetTest { public function testItBulkRestoresNewslettersAndChildren() { $newsletter1 = $this->createNewsletter(NewsletterEntity::TYPE_STANDARD, NewsletterEntity::STATUS_SENDING); - $this->createQueueWithTask($newsletter1, null); // Null for scheduled task being processed + $this->createQueueWithTaskAndSegment($newsletter1, null); // Null for scheduled task being processed $newsletter2 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_ACTIVE); $newsletter2Child1 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_SCHEDULED, $newsletter2); - $this->createQueueWithTask($newsletter2Child1); + $this->createQueueWithTaskAndSegment($newsletter2Child1); // Trash $this->repository->bulkTrash([$newsletter1->getId(), $newsletter2->getId()]); // Restore @@ -100,6 +102,60 @@ class NewsletterRepositoryTest extends \MailPoetTest { expect($scheduledTask->getDeletedAt())->null(); } + public function testItBulkDeleteNewslettersAndChildren() { + $newsletter1 = $this->createNewsletter(NewsletterEntity::TYPE_STANDARD, NewsletterEntity::STATUS_SENDING); + $this->createQueueWithTaskAndSegment($newsletter1, null); // Null for scheduled task being processed + $newsletter2 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_ACTIVE); + $newsletter2Child1 = $this->createNewsletter(NewsletterEntity::TYPE_NOTIFICATION, NewsletterEntity::STATUS_SCHEDULED, $newsletter2); + $this->createQueueWithTaskAndSegment($newsletter2Child1); + // Trash + $this->repository->bulkTrash([$newsletter1->getId(), $newsletter2->getId()]); + $newsletter1Queue = $newsletter1->getLatestQueue(); + assert($newsletter1Queue instanceof SendingQueueEntity); + $newsletter1Segment = $newsletter1->getNewsletterSegments()->first(); + assert($newsletter1Segment instanceof NewsletterSegmentEntity); + $childrenQueue = $newsletter2Child1->getLatestQueue(); + assert($childrenQueue instanceof SendingQueueEntity); + $scheduledTask1 = $newsletter1Queue->getTask(); + assert($scheduledTask1 instanceof ScheduledTaskEntity); + $childrenScheduledTask = $childrenQueue->getTask(); + assert($childrenScheduledTask instanceof ScheduledTaskEntity); + $childSegment = $newsletter2Child1->getNewsletterSegments()->first(); + assert($childSegment instanceof NewsletterSegmentEntity); + + // Delete + $this->repository->bulkDelete([$newsletter1->getId(), $newsletter2->getId()]); + + // Detach entities so that ORM forget them + $this->entityManager->detach($newsletter1); + $this->entityManager->detach($newsletter2); + $this->entityManager->detach($newsletter2Child1); + $this->entityManager->detach($newsletter1Queue); + $this->entityManager->detach($childrenQueue); + $this->entityManager->detach($scheduledTask1); + $this->entityManager->detach($childrenScheduledTask); + $this->entityManager->detach($newsletter1Segment); + $this->entityManager->detach($childSegment); + + // Check they were all deleted + // Newsletters + expect($this->repository->findOneById($newsletter1->getId()))->null(); + expect($this->repository->findOneById($newsletter2->getId()))->null(); + expect($this->repository->findOneById($newsletter2Child1->getId()))->null(); + + // Sending queues + expect($this->entityManager->find(SendingQueueEntity::class, $newsletter1Queue->getId()))->null(); + expect($this->entityManager->find(SendingQueueEntity::class, $childrenQueue->getId()))->null(); + + // Scheduled tasks + expect($this->entityManager->find(ScheduledTaskEntity::class, $scheduledTask1->getId()))->null(); + expect($this->entityManager->find(ScheduledTaskEntity::class, $childrenScheduledTask->getId()))->null(); + + // Newsletter segments + expect($this->entityManager->find(NewsletterSegmentEntity::class, $newsletter1Segment->getId()))->null(); + expect($this->entityManager->find(NewsletterSegmentEntity::class, $childSegment->getId()))->null(); + } + public function _after() { $this->cleanup(); } @@ -116,7 +172,7 @@ class NewsletterRepositoryTest extends \MailPoetTest { return $newsletter; } - private function createQueueWithTask(NewsletterEntity $newsletter, $status = ScheduledTaskEntity::STATUS_SCHEDULED): SendingQueueEntity { + private function createQueueWithTaskAndSegment(NewsletterEntity $newsletter, $status = ScheduledTaskEntity::STATUS_SCHEDULED): SendingQueueEntity { $task = new ScheduledTaskEntity(); $task->setType(SendingTask::TASK_TYPE); $task->setStatus($status); @@ -126,8 +182,17 @@ class NewsletterRepositoryTest extends \MailPoetTest { $queue->setNewsletter($newsletter); $queue->setTask($task); $this->entityManager->persist($queue); - $newsletter->getQueues()->add($queue); + + $segment = new SegmentEntity(); + $segment->setType(SegmentEntity::TYPE_DEFAULT); + $segment->setName(" List for newsletter id {$newsletter->getId()}"); + $segment->setDescription(''); + $this->entityManager->persist($segment); + + $newsletterSegment = new NewsletterSegmentEntity($newsletter, $segment); + $newsletter->getNewsletterSegments()->add($newsletterSegment); + $this->entityManager->persist($newsletterSegment); $this->entityManager->flush(); return $queue; } @@ -136,5 +201,7 @@ class NewsletterRepositoryTest extends \MailPoetTest { $this->truncateEntity(NewsletterEntity::class); $this->truncateEntity(ScheduledTaskEntity::class); $this->truncateEntity(SendingQueueEntity::class); + $this->truncateEntity(NewsletterSegmentEntity::class); + $this->truncateEntity(SegmentEntity::class); } }