Enable joining segments using and/or
[MAILPOET-3212]
This commit is contained in:
@ -11,6 +11,7 @@ use MailPoet\Segments\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\Segments\DynamicSegments\Filters\UserRole;
|
||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceCategory;
|
||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceProduct;
|
||||
use MailPoet\Util\Security;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
@ -61,7 +62,28 @@ class FilterHandler {
|
||||
$queryBuilder->getParameters()
|
||||
));
|
||||
}
|
||||
$queryBuilder->innerJoin($subscribersTable, sprintf('(%s)', join(' UNION ', $filterSelects)), 'filtered_subscribers', 'filtered_subscribers.inner_subscriber_id = id');
|
||||
$this->joinSubqueries($queryBuilder, $segment, $filterSelects);
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
private function joinSubqueries(QueryBuilder $queryBuilder, SegmentEntity $segment, array $subQueries): QueryBuilder {
|
||||
$filter = $segment->getDynamicFilters()->first();
|
||||
if (!$filter) return $queryBuilder;
|
||||
$filterData = $filter->getFilterData();
|
||||
$data = $filterData->getData();
|
||||
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
|
||||
|
||||
if (!isset($data['connect']) || $data['connect'] === 'or') {
|
||||
// the final query: SELECT * FROM subscribers INNER JOIN (filter_select1 UNION filter_select2) filtered_subscribers ON filtered_subscribers.inner_subscriber_id = id
|
||||
$queryBuilder->innerJoin($subscribersTable, sprintf('(%s)', join(' UNION ', $subQueries)), 'filtered_subscribers', 'filtered_subscribers.inner_subscriber_id = id');
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
foreach ($subQueries as $subQuery) {
|
||||
// we need a unique name for each subquery so that we can join them together in the sql query - just make sure the identifier starts with a letter, not a number
|
||||
$subqueryName = 'a' . Security::generateRandomString(5);
|
||||
$queryBuilder->innerJoin($subscribersTable, "($subQuery)", $subqueryName, "$subqueryName.inner_subscriber_id = id");
|
||||
}
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace MailPoet\Segments\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||
use MailPoet\Entities\StatisticsClickEntity;
|
||||
use MailPoet\Entities\StatisticsNewsletterEntity;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace MailPoet\Segments\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace MailPoet\Segments\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace MailPoet\Segments\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
|
||||
|
@ -4,14 +4,26 @@ namespace MailPoet\Segments\DynamicSegments;
|
||||
|
||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\StatisticsOpenEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Segments\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
|
||||
class FilterHandlerTest extends \MailPoetTest {
|
||||
|
||||
/** @var FilterHandler */
|
||||
private $filterHandler;
|
||||
|
||||
/** @var SubscriberEntity */
|
||||
private $subscriber1;
|
||||
|
||||
/** @var SubscriberEntity */
|
||||
private $subscriber2;
|
||||
|
||||
public function _before() {
|
||||
$this->cleanWpUsers();
|
||||
$this->filterHandler = $this->diContainer->get(FilterHandler::class);
|
||||
@ -30,6 +42,12 @@ class FilterHandlerTest extends \MailPoetTest {
|
||||
'email' => 'user-role-test3@example.com',
|
||||
'wp_user_id' => $id,
|
||||
]);
|
||||
|
||||
// fetch entities
|
||||
/** @var SubscribersRepository $subscribersRepository */
|
||||
$subscribersRepository = $this->diContainer->get(SubscribersRepository::class);
|
||||
$this->subscriber1 = $subscribersRepository->findOneBy(['email' => 'user-role-test1@example.com']);
|
||||
$this->subscriber2 = $subscribersRepository->findOneBy(['email' => 'user-role-test2@example.com']);
|
||||
}
|
||||
|
||||
public function testItAppliesFilter() {
|
||||
@ -43,7 +61,7 @@ class FilterHandlerTest extends \MailPoetTest {
|
||||
expect($subscriber2->getEmail())->equals('user-role-test3@example.com');
|
||||
}
|
||||
|
||||
public function testItAppliesTwoFilters() {
|
||||
public function testItAppliesTwoFiltersWithoutSpecifyingConnection() {
|
||||
$segment = $this->getSegment('editor');
|
||||
$filter = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_USER_ROLE,
|
||||
@ -52,11 +70,84 @@ class FilterHandlerTest extends \MailPoetTest {
|
||||
$dynamicSegmentFilter = new DynamicSegmentFilterEntity($segment, $filter);
|
||||
$this->entityManager->persist($dynamicSegmentFilter);
|
||||
$segment->addDynamicFilter($dynamicSegmentFilter);
|
||||
$this->entityManager->flush();
|
||||
$queryBuilder = $this->filterHandler->apply($this->getQueryBuilder(), $segment);
|
||||
$result = $queryBuilder->execute()->fetchAll();
|
||||
expect($result)->count(3);
|
||||
}
|
||||
|
||||
public function testItAppliesTwoFiltersWithOr() {
|
||||
$segment = new SegmentEntity('Dynamic Segment', SegmentEntity::TYPE_DYNAMIC, 'description');
|
||||
$this->entityManager->persist($segment);
|
||||
$filterData = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_USER_ROLE,
|
||||
'wordpressRole' => 'administrator',
|
||||
'connect' => 'or',
|
||||
]);
|
||||
$dynamicSegmentFilter = new DynamicSegmentFilterEntity($segment, $filterData);
|
||||
$this->entityManager->persist($dynamicSegmentFilter);
|
||||
$segment->addDynamicFilter($dynamicSegmentFilter);
|
||||
$filterData = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_USER_ROLE,
|
||||
'wordpressRole' => 'editor',
|
||||
'connect' => 'or',
|
||||
]);
|
||||
$dynamicSegmentFilter = new DynamicSegmentFilterEntity($segment, $filterData);
|
||||
$this->entityManager->persist($dynamicSegmentFilter);
|
||||
$segment->addDynamicFilter($dynamicSegmentFilter);
|
||||
$this->entityManager->flush();
|
||||
$queryBuilder = $this->filterHandler->apply($this->getQueryBuilder(), $segment);
|
||||
$result = $queryBuilder->execute()->fetchAll();
|
||||
expect($result)->count(3);
|
||||
}
|
||||
|
||||
public function testItAppliesTwoFiltersWithAnd() {
|
||||
$segment = new SegmentEntity('Dynamic Segment', SegmentEntity::TYPE_DYNAMIC, 'description');
|
||||
$this->entityManager->persist($segment);
|
||||
// filter user is an editor
|
||||
$editorData = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_USER_ROLE,
|
||||
'wordpressRole' => 'editor',
|
||||
'connect' => 'and',
|
||||
]);
|
||||
$filterEditor = new DynamicSegmentFilterEntity($segment, $editorData);
|
||||
$this->entityManager->persist($filterEditor);
|
||||
$segment->addDynamicFilter($filterEditor);
|
||||
// filter user opened an email
|
||||
$newsletter = new NewsletterEntity();
|
||||
$task = new ScheduledTaskEntity();
|
||||
$this->entityManager->persist($task);
|
||||
$queue = new SendingQueueEntity();
|
||||
$queue->setNewsletter($newsletter);
|
||||
$queue->setTask($task);
|
||||
$this->entityManager->persist($queue);
|
||||
$newsletter->getQueues()->add($queue);
|
||||
$newsletter->setSubject('newsletter 1');
|
||||
$newsletter->setStatus('sent');
|
||||
$newsletter->setType(NewsletterEntity::TYPE_STANDARD);
|
||||
$this->entityManager->persist($newsletter);
|
||||
$open = new StatisticsOpenEntity($newsletter, $queue, $this->subscriber1);
|
||||
$this->entityManager->persist($open);
|
||||
$open = new StatisticsOpenEntity($newsletter, $queue, $this->subscriber2);
|
||||
$this->entityManager->persist($open);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$openedData = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_EMAIL,
|
||||
'action' => EmailAction::ACTION_OPENED,
|
||||
'newsletter_id' => $newsletter->getId(),
|
||||
'connect' => 'and',
|
||||
]);
|
||||
$filterOpened = new DynamicSegmentFilterEntity($segment, $openedData);
|
||||
$this->entityManager->persist($filterOpened);
|
||||
$segment->addDynamicFilter($filterOpened);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$queryBuilder = $this->filterHandler->apply($this->getQueryBuilder(), $segment);
|
||||
$result = $queryBuilder->execute()->fetchAll();
|
||||
expect($result)->count(1);
|
||||
}
|
||||
|
||||
private function getSegment(string $role): SegmentEntity {
|
||||
$filter = new DynamicSegmentFilterData([
|
||||
'segmentType' => DynamicSegmentFilterData::TYPE_USER_ROLE,
|
||||
@ -84,6 +175,11 @@ class FilterHandlerTest extends \MailPoetTest {
|
||||
$this->cleanWpUsers();
|
||||
$this->truncateEntity(SubscriberEntity::class);
|
||||
$this->truncateEntity(SegmentEntity::class);
|
||||
$this->truncateEntity(DynamicSegmentFilterEntity::class);
|
||||
$this->truncateEntity(NewsletterEntity::class);
|
||||
$this->truncateEntity(StatisticsOpenEntity::class);
|
||||
$this->truncateEntity(SendingQueueEntity::class);
|
||||
$this->truncateEntity(ScheduledTaskEntity::class);
|
||||
}
|
||||
|
||||
private function cleanWpUsers() {
|
||||
|
Reference in New Issue
Block a user