diff --git a/lib/Subscribers/ImportExport/ImportExportRepository.php b/lib/Subscribers/ImportExport/ImportExportRepository.php index a18447351f..109553cff3 100644 --- a/lib/Subscribers/ImportExport/ImportExportRepository.php +++ b/lib/Subscribers/ImportExport/ImportExportRepository.php @@ -208,6 +208,7 @@ class ImportExportRepository { ->leftJoin($subscriberSegmentTable, $segmentTable, $segmentTable, "{$segmentTable}.id = {$subscriberSegmentTable}.segment_id") ->andWhere("{$subscriberTable}.deleted_at IS NULL") ->groupBy("{$subscriberTable}.id, {$segmentTable}.id") + ->orderBy("{$subscriberTable}.id") ->setFirstResult($offset) ->setMaxResults($limit); } diff --git a/tests/integration/Subscribers/ImportExport/ImportExportRepositoryTest.php b/tests/integration/Subscribers/ImportExport/ImportExportRepositoryTest.php index 98036c0ba0..114da3a224 100644 --- a/tests/integration/Subscribers/ImportExport/ImportExportRepositoryTest.php +++ b/tests/integration/Subscribers/ImportExport/ImportExportRepositoryTest.php @@ -4,9 +4,14 @@ namespace MailPoet\Subscribers\ImportExport; use MailPoet\CustomFields\CustomFieldsRepository; use MailPoet\Entities\CustomFieldEntity; +use MailPoet\Entities\DynamicSegmentFilterEntity; +use MailPoet\Entities\SegmentEntity; use MailPoet\Entities\SubscriberCustomFieldEntity; use MailPoet\Entities\SubscriberEntity; +use MailPoet\Entities\SubscriberSegmentEntity; +use MailPoet\Segments\SegmentsRepository; use MailPoet\Subscribers\SubscriberCustomFieldRepository; +use MailPoet\Subscribers\SubscriberSegmentRepository; use MailPoet\Subscribers\SubscribersRepository; use MailPoetVendor\Carbon\Carbon; @@ -19,13 +24,19 @@ class ImportExportRepositoryTest extends \MailPoetTest { private $subscribersRepository; /** @var SubscriberCustomFieldRepository */ private $subscriberCustomFieldRepository; + /** @var SegmentsRepository */ + private $segmentsRepository; + /** @var SubscriberSegmentRepository */ + private $subscriberSegmentRepository; public function _before() { parent::_before(); $this->cleanup(); $this->repository = $this->diContainer->get(ImportExportRepository::class); $this->customFieldsRepository = $this->diContainer->get(CustomFieldsRepository::class); + $this->segmentsRepository = $this->diContainer->get(SegmentsRepository::class); $this->subscribersRepository = $this->diContainer->get(SubscribersRepository::class); + $this->subscriberSegmentRepository = $this->diContainer->get(SubscriberSegmentRepository::class); $this->subscriberCustomFieldRepository = $this->diContainer->get(SubscriberCustomFieldRepository::class); } @@ -36,8 +47,8 @@ class ImportExportRepositoryTest extends \MailPoetTest { 'last_name', ]; $data = [ - ['user1@test.com', 'One', 'User'], - ['user2@test.com', 'Two', 'User'], + ['user1@export-test.com', 'One', 'User'], + ['user2@export-test.com', 'Two', 'User'], ]; $count = $this->repository->insertMultiple( SubscriberEntity::class, @@ -50,27 +61,27 @@ class ImportExportRepositoryTest extends \MailPoetTest { expect($subscribers)->count(2); $user1 = $subscribers[0]; assert($user1 instanceof SubscriberEntity); - expect($user1->getEmail())->equals('user1@test.com'); + expect($user1->getEmail())->equals('user1@export-test.com'); expect($user1->getFirstName())->equals('One'); expect($user1->getLastName())->equals('User'); $user2 = $subscribers[1]; assert($user2 instanceof SubscriberEntity); - expect($user2->getEmail())->equals('user2@test.com'); + expect($user2->getEmail())->equals('user2@export-test.com'); expect($user2->getFirstName())->equals('Two'); expect($user2->getLastName())->equals('User'); } public function testItUpdateMultipleSubscribers(): void { - $this->createSubscriber('user1@test.com', 'One', 'User'); - $this->createSubscriber('user2@test.com', 'Two', 'User'); + $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $this->createSubscriber('user2@export-test.com', 'Two', 'User'); $columns = [ 'email', 'first_name', 'last_name', ]; $data = [ - ['user1@test.com', 'OneOne', 'UserOne'], - ['user2@test.com', 'TwoTwo', 'UserTwo'], + ['user1@export-test.com', 'OneOne', 'UserOne'], + ['user2@export-test.com', 'TwoTwo', 'UserTwo'], ]; $updatedAt = Carbon::createFromTimestamp(time()); $count = $this->repository->updateMultiple( @@ -86,21 +97,21 @@ class ImportExportRepositoryTest extends \MailPoetTest { expect($subscribers)->count(2); $user1 = $subscribers[0]; assert($user1 instanceof SubscriberEntity); - expect($user1->getEmail())->equals('user1@test.com'); + expect($user1->getEmail())->equals('user1@export-test.com'); expect($user1->getFirstName())->equals('OneOne'); expect($user1->getLastName())->equals('UserOne'); expect($user1->getUpdatedAt())->equals($updatedAt); $user2 = $subscribers[1]; assert($user2 instanceof SubscriberEntity); - expect($user2->getEmail())->equals('user2@test.com'); + expect($user2->getEmail())->equals('user2@export-test.com'); expect($user2->getFirstName())->equals('TwoTwo'); expect($user2->getLastName())->equals('UserTwo'); expect($user2->getUpdatedAt())->equals($updatedAt); } public function testItInsertMultipleSubscriberCustomFields(): void { - $subscriber1 = $this->createSubscriber('user1@test.com', 'One', 'User'); - $subscriber2 = $this->createSubscriber('user2@test.com', 'Two', 'User'); + $subscriber1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $subscriber2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); $customField = $this->createCustomField('age'); $columns = [ 'subscriber_id', @@ -133,8 +144,8 @@ class ImportExportRepositoryTest extends \MailPoetTest { } public function testItUpdateMultipleSubscriberCustomFields(): void { - $subscriber1 = $this->createSubscriber('user1@test.com', 'One', 'User'); - $subscriber2 = $this->createSubscriber('user2@test.com', 'Two', 'User'); + $subscriber1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $subscriber2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); $customField = $this->createCustomField('age'); $subscriber1CustomField = $this->createSubscriberCustomField($subscriber1, $customField, '0'); $subscriber2CustomField = $this->createSubscriberCustomField($subscriber2, $customField, '0'); @@ -177,6 +188,127 @@ class ImportExportRepositoryTest extends \MailPoetTest { expect($subscriberCustomField2->getValue())->equals('25'); } + public function testItGetSubscribersByDefaultSegment(): void { + $user1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $user2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); + $user3 = $this->createSubscriber('user3@export-test.com', 'Three', 'User'); + $segment1 = $this->createSegment('First', SegmentEntity::TYPE_DEFAULT); + $segment2 = $this->createSegment('Two', SegmentEntity::TYPE_DEFAULT); + $this->createSubscriberSegment($user1, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user2, $segment1, SubscriberEntity::STATUS_UNSUBSCRIBED); + $this->createSubscriberSegment($user3, $segment2, SubscriberEntity::STATUS_SUBSCRIBED); + + $exported = $this->repository->getSubscribersBatchBySegment($segment1, 100); + expect($exported)->count(2); + expect($exported[0]['first_name'])->equals('One'); + expect($exported[0]['last_name'])->equals('User'); + expect($exported[0]['email'])->equals('user1@export-test.com'); + expect($exported[0]['segment_name'])->equals('First'); + expect($exported[1]['first_name'])->equals('Two'); + expect($exported[1]['last_name'])->equals('User'); + expect($exported[1]['email'])->equals('user2@export-test.com'); + expect($exported[1]['segment_name'])->equals('First'); + } + + public function testItGetOnlyNodDeletedSubscribersByDefaultSegment(): void { + $user1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $user2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); + $user3 = $this->createSubscriber('user3@export-test.com', 'Three', 'User'); + $segment1 = $this->createSegment('First', SegmentEntity::TYPE_DEFAULT); + $this->createSubscriberSegment($user1, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user2, $segment1, SubscriberEntity::STATUS_UNSUBSCRIBED); + $this->createSubscriberSegment($user3, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $user2->setDeletedAt(new \DateTime()); + $user3->setDeletedAt(new \DateTime()); + $this->subscribersRepository->flush(); + + $exported = $this->repository->getSubscribersBatchBySegment($segment1, 100); + expect($exported)->count(1); + expect($exported[0]['first_name'])->equals('One'); + expect($exported[0]['last_name'])->equals('User'); + expect($exported[0]['email'])->equals('user1@export-test.com'); + expect($exported[0]['segment_name'])->equals('First'); + } + + public function testItGetSubscribersWithoutSegment(): void { + $user1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $user2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); + $user3 = $this->createSubscriber('user3@export-test.com', 'Three', 'User'); + $user4 = $this->createSubscriber('user4@export-test.com', 'Four', 'User'); + $segment1 = $this->createSegment('First', SegmentEntity::TYPE_DEFAULT); + $segment2 = $this->createSegment('Two', SegmentEntity::TYPE_DEFAULT); + $this->createSubscriberSegment($user1, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user3, $segment2, SubscriberEntity::STATUS_SUBSCRIBED); + + $exported = $this->repository->getSubscribersBatchBySegment(null, 100); + expect($exported)->count(2); + expect($exported[0]['first_name'])->equals('Two'); + expect($exported[0]['last_name'])->equals('User'); + expect($exported[0]['email'])->equals('user2@export-test.com'); + expect($exported[0]['segment_name'])->equals('Not In Segment'); + expect($exported[1]['first_name'])->equals('Four'); + expect($exported[1]['last_name'])->equals('User'); + expect($exported[1]['email'])->equals('user4@export-test.com'); + expect($exported[1]['segment_name'])->equals('Not In Segment'); + } + + public function testItGetSubscribersByDynamicSegment(): void { + $this->tester->createWordPressUser('user1@export-test.com', 'administrator'); + $this->tester->createWordPressUser('user2@export-test.com', 'editor'); + $this->tester->createWordPressUser('user3@export-test.com', 'editor'); + $user4 = $this->createSubscriber('user4@export-test.com', 'Four', 'User'); + $user5 = $this->createSubscriber('user5@export-test.com', 'Five', 'User'); + $segment1 = $this->createSegment('First', SegmentEntity::TYPE_DEFAULT); + $segment2 = $this->createSegment('Dynamic Segment', SegmentEntity::TYPE_DYNAMIC); + $this->createDynamicSegmentFilter($segment2, [ + 'segmentType' => DynamicSegmentFilterEntity::TYPE_USER_ROLE, + 'wordpressRole' => 'editor', + ]); + $this->createSubscriberSegment($user4, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user5, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + + $this->entityManager->clear(); + $segment2 = $this->segmentsRepository->findOneById($segment2->getId()); + assert($segment2 instanceof SegmentEntity); + $exported = $this->repository->getSubscribersBatchBySegment($segment2, 100); + expect($exported)->count(2); + expect($exported[0]['email'])->equals('user2@export-test.com'); + expect($exported[0]['segment_name'])->equals('Dynamic Segment'); + expect($exported[1]['email'])->equals('user3@export-test.com'); + expect($exported[1]['segment_name'])->equals('Dynamic Segment'); + } + + public function testItGetBatchSubscribersMoreTimes(): void { + $user1 = $this->createSubscriber('user1@export-test.com', 'One', 'User'); + $user2 = $this->createSubscriber('user2@export-test.com', 'Two', 'User'); + $user3 = $this->createSubscriber('user3@export-test.com', 'Three', 'User'); + $user4 = $this->createSubscriber('user4@export-test.com', 'Four', 'User'); + $user5 = $this->createSubscriber('user5@export-test.com', 'Five', 'User'); + $segment1 = $this->createSegment('First', SegmentEntity::TYPE_DEFAULT); + $segment2 = $this->createSegment('Two', SegmentEntity::TYPE_DEFAULT); + $this->createSubscriberSegment($user1, $segment1, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user2, $segment1, SubscriberEntity::STATUS_UNSUBSCRIBED); + $this->createSubscriberSegment($user3, $segment2, SubscriberEntity::STATUS_UNCONFIRMED); + $this->createSubscriberSegment($user4, $segment2, SubscriberEntity::STATUS_SUBSCRIBED); + $this->createSubscriberSegment($user5, $segment2, SubscriberEntity::STATUS_UNSUBSCRIBED); + + $offset = 0; + for ($i = 3; $i <= 5; $i++) { + $exported = $this->repository->getSubscribersBatchBySegment($segment2, 1, $offset); + $offset += count($exported); + expect($exported)->count(1); + expect($exported[0]['email'])->equals("user{$i}@export-test.com"); + expect($exported[0]['segment_name'])->equals('Two'); + } + } + + private function createSegment(string $name, string $type): SegmentEntity { + $segment = new SegmentEntity($name, $type, ''); + $this->segmentsRepository->persist($segment); + $this->segmentsRepository->flush(); + return $segment; + } + private function createSubscriber(string $email, string $firstName, string $lastName): SubscriberEntity { $subscriber = new SubscriberEntity(); $subscriber->setEmail($email); @@ -187,6 +319,17 @@ class ImportExportRepositoryTest extends \MailPoetTest { return $subscriber; } + private function createSubscriberSegment( + SubscriberEntity $subscriber, + SegmentEntity $segment, + string $status + ): SubscriberSegmentEntity { + $subscriberSegment = new SubscriberSegmentEntity($segment, $subscriber, $status); + $this->subscriberSegmentRepository->persist($subscriberSegment); + $this->subscriberSegmentRepository->flush(); + return $subscriberSegment; + } + private function createCustomField(string $name): CustomFieldEntity { $customField = new CustomFieldEntity(); $customField->setName($name); @@ -201,14 +344,43 @@ class ImportExportRepositoryTest extends \MailPoetTest { CustomFieldEntity $customField, string $value ): SubscriberCustomFieldEntity { - $subscirberCustomField = new SubscriberCustomFieldEntity($subscriber, $customField, $value); - $this->entityManager->persist($subscirberCustomField); + $subscriberCustomField = new SubscriberCustomFieldEntity($subscriber, $customField, $value); + $this->entityManager->persist($subscriberCustomField); $this->entityManager->flush(); - return $subscirberCustomField; + return $subscriberCustomField; + } + + private function createDynamicSegmentFilter( + SegmentEntity $segment, + array $filterData + ): DynamicSegmentFilterEntity { + $filter = new DynamicSegmentFilterEntity($segment, $filterData); + $this->entityManager->persist($filter); + $this->entityManager->flush(); + return $filter; + } + + private function cleanWpUsers(): void { + $emails = [ + 'user1@export-test.com', + 'user2@export-test.com', + 'user3@export-test.com', + ]; + foreach ($emails as $email) { + $this->tester->deleteWordPressUser($email); + } + } + + protected function _after() { + $this->cleanup(); } private function cleanup() { + $this->cleanWpUsers(); + $this->truncateEntity(DynamicSegmentFilterEntity::class); $this->truncateEntity(SubscriberEntity::class); + $this->truncateEntity(SegmentEntity::class); + $this->truncateEntity(SubscriberSegmentEntity::class); $this->truncateEntity(SubscriberCustomFieldEntity::class); $this->truncateEntity(CustomFieldEntity::class); }