Add "was sent email" filter
MAILPOET-5004
This commit is contained in:
committed by
Aschepikov
parent
87f703f22a
commit
8a66c83257
@ -43,6 +43,7 @@ const componentsMap = {
|
|||||||
EmailOpensAbsoluteCountFields,
|
EmailOpensAbsoluteCountFields,
|
||||||
[EmailActionTypes.CLICKED]: EmailClickStatisticsFields,
|
[EmailActionTypes.CLICKED]: EmailClickStatisticsFields,
|
||||||
[EmailActionTypes.OPENED]: EmailOpenStatisticsFields,
|
[EmailActionTypes.OPENED]: EmailOpenStatisticsFields,
|
||||||
|
[EmailActionTypes.WAS_SENT]: EmailOpenStatisticsFields,
|
||||||
[EmailActionTypes.MACHINE_OPENED]: EmailOpenStatisticsFields,
|
[EmailActionTypes.MACHINE_OPENED]: EmailOpenStatisticsFields,
|
||||||
[EmailActionTypes.CLICKED_ANY]: null,
|
[EmailActionTypes.CLICKED_ANY]: null,
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,11 @@ export const EmailSegmentOptions = [
|
|||||||
label: MailPoet.I18n.t('emailActionMachineOpensAbsoluteCount'),
|
label: MailPoet.I18n.t('emailActionMachineOpensAbsoluteCount'),
|
||||||
group: SegmentTypes.Email,
|
group: SegmentTypes.Email,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: EmailActionTypes.WAS_SENT,
|
||||||
|
label: MailPoet.I18n.t('emailActionWasSent'),
|
||||||
|
group: SegmentTypes.Email,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: EmailActionTypes.OPENED,
|
value: EmailActionTypes.OPENED,
|
||||||
label: MailPoet.I18n.t('emailActionOpened'),
|
label: MailPoet.I18n.t('emailActionOpened'),
|
||||||
|
@ -12,6 +12,7 @@ export enum EmailActionTypes {
|
|||||||
MACHINE_OPENS_ABSOLUTE_COUNT = 'machineOpensAbsoluteCount',
|
MACHINE_OPENS_ABSOLUTE_COUNT = 'machineOpensAbsoluteCount',
|
||||||
OPENED = 'opened',
|
OPENED = 'opened',
|
||||||
MACHINE_OPENED = 'machineOpened',
|
MACHINE_OPENED = 'machineOpened',
|
||||||
|
WAS_SENT = 'wasSent',
|
||||||
CLICKED = 'clicked',
|
CLICKED = 'clicked',
|
||||||
CLICKED_ANY = 'clickedAny',
|
CLICKED_ANY = 'clickedAny',
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ class EmailAction implements Filter {
|
|||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
const ACTION_NOT_OPENED = 'notOpened';
|
const ACTION_NOT_OPENED = 'notOpened';
|
||||||
const ACTION_CLICKED = 'clicked';
|
const ACTION_CLICKED = 'clicked';
|
||||||
|
const ACTION_WAS_SENT = 'wasSent';
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
const ACTION_NOT_CLICKED = 'notClicked';
|
const ACTION_NOT_CLICKED = 'notClicked';
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ class EmailAction implements Filter {
|
|||||||
self::ACTION_OPENED,
|
self::ACTION_OPENED,
|
||||||
self::ACTION_MACHINE_OPENED,
|
self::ACTION_MACHINE_OPENED,
|
||||||
self::ACTION_CLICKED,
|
self::ACTION_CLICKED,
|
||||||
|
self::ACTION_WAS_SENT,
|
||||||
EmailActionClickAny::TYPE,
|
EmailActionClickAny::TYPE,
|
||||||
EmailOpensAbsoluteCountAction::TYPE,
|
EmailOpensAbsoluteCountAction::TYPE,
|
||||||
EmailOpensAbsoluteCountAction::MACHINE_TYPE,
|
EmailOpensAbsoluteCountAction::MACHINE_TYPE,
|
||||||
@ -35,11 +37,15 @@ class EmailAction implements Filter {
|
|||||||
|
|
||||||
/** @var EntityManager */
|
/** @var EntityManager */
|
||||||
private $entityManager;
|
private $entityManager;
|
||||||
|
/** @var FilterHelper */
|
||||||
|
private $filterHelper;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManager $entityManager
|
EntityManager $entityManager,
|
||||||
|
FilterHelper $filterHelper
|
||||||
) {
|
) {
|
||||||
$this->entityManager = $entityManager;
|
$this->entityManager = $entityManager;
|
||||||
|
$this->filterHelper = $filterHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filter): QueryBuilder {
|
public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filter): QueryBuilder {
|
||||||
@ -49,6 +55,8 @@ class EmailAction implements Filter {
|
|||||||
|
|
||||||
if ($action === self::ACTION_CLICKED) {
|
if ($action === self::ACTION_CLICKED) {
|
||||||
return $this->applyForClickedActions($queryBuilder, $filterData, $parameterSuffix);
|
return $this->applyForClickedActions($queryBuilder, $filterData, $parameterSuffix);
|
||||||
|
} elseif ($action === self::ACTION_WAS_SENT) {
|
||||||
|
return $this->applyForWasSentAction($queryBuilder, $filterData, $parameterSuffix);
|
||||||
} else {
|
} else {
|
||||||
return $this->applyForOpenedActions($queryBuilder, $filterData, $parameterSuffix);
|
return $this->applyForOpenedActions($queryBuilder, $filterData, $parameterSuffix);
|
||||||
}
|
}
|
||||||
@ -175,4 +183,36 @@ class EmailAction implements Filter {
|
|||||||
}
|
}
|
||||||
return $clause;
|
return $clause;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function applyForWasSentAction(QueryBuilder $queryBuilder, DynamicSegmentFilterData $filterData, string $parameterSuffix): QueryBuilder {
|
||||||
|
$newsletters = (array)$filterData->getParam('newsletters');
|
||||||
|
$operator = $filterData->getParam('operator') ?? DynamicSegmentFilterData::OPERATOR_ANY;
|
||||||
|
$subscribersTable = $this->filterHelper->getSubscribersTable();
|
||||||
|
$statisticsNewslettersTable = $this->entityManager->getClassMetadata(StatisticsNewsletterEntity::class)->getTableName();
|
||||||
|
|
||||||
|
if ($operator === DynamicSegmentFilterData::OPERATOR_NONE) {
|
||||||
|
$queryBuilder->leftJoin(
|
||||||
|
$this->filterHelper->getSubscribersTable(),
|
||||||
|
$statisticsNewslettersTable,
|
||||||
|
'statisticsNewsletter',
|
||||||
|
"$subscribersTable.id = statisticsNewsletter.subscriber_id AND statisticsNewsletter.newsletter_id IN (:newsletters" . $parameterSuffix . ')'
|
||||||
|
)
|
||||||
|
->setParameter('newsletters' . $parameterSuffix, $newsletters, Connection::PARAM_INT_ARRAY)
|
||||||
|
->andWhere('statisticsNewsletter.subscriber_id IS NULL');
|
||||||
|
} else {
|
||||||
|
$queryBuilder->innerJoin(
|
||||||
|
$subscribersTable,
|
||||||
|
$statisticsNewslettersTable,
|
||||||
|
'statisticsNewsletter',
|
||||||
|
"statisticsNewsletter.subscriber_id = $subscribersTable.id AND statisticsNewsletter.newsletter_id IN (:newsletters" . $parameterSuffix . ')'
|
||||||
|
)->setParameter('newsletters' . $parameterSuffix, $newsletters, Connection::PARAM_INT_ARRAY);
|
||||||
|
|
||||||
|
if ($operator === DynamicSegmentFilterData::OPERATOR_ALL) {
|
||||||
|
$queryBuilder->groupBy('subscriber_id');
|
||||||
|
$queryBuilder->having('COUNT(1) = ' . count($newsletters));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $queryBuilder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,71 @@ class EmailActionTest extends \MailPoetTest {
|
|||||||
$this->assertEqualsCanonicalizing(['opened_machine@example.com'], $emails);
|
$this->assertEqualsCanonicalizing(['opened_machine@example.com'], $emails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSentEmailAny(): void {
|
||||||
|
$subscriber1 = $this->createSubscriber('1@example.com');
|
||||||
|
$subscriber2 = $this->createSubscriber('2@example.com');
|
||||||
|
$this->createSubscriber('3@example.com');
|
||||||
|
|
||||||
|
|
||||||
|
$this->createStatsNewsletter($subscriber1, $this->newsletter);
|
||||||
|
$this->createStatsNewsletter($subscriber2, $this->newsletter2);
|
||||||
|
|
||||||
|
$segmentFilterData = $this->getSegmentFilterData(EmailAction::ACTION_WAS_SENT, [
|
||||||
|
'newsletters' => [$this->newsletter->getId(), $this->newsletter2->getId()],
|
||||||
|
'operator' => DynamicSegmentFilterData::OPERATOR_ANY,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$emails = $this->tester->getSubscriberEmailsMatchingDynamicFilter($segmentFilterData, $this->emailAction);
|
||||||
|
|
||||||
|
expect($emails)->contains('1@example.com');
|
||||||
|
expect($emails)->contains('2@example.com');
|
||||||
|
expect($emails)->notContains('3@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSentEmailAll(): void {
|
||||||
|
$subscriber1 = $this->createSubscriber('1@example.com');
|
||||||
|
$subscriber2 = $this->createSubscriber('2@example.com');
|
||||||
|
$subscriber3 = $this->createSubscriber('3@example.com');
|
||||||
|
|
||||||
|
$this->createStatsNewsletter($subscriber1, $this->newsletter);
|
||||||
|
$this->createStatsNewsletter($subscriber1, $this->newsletter2);
|
||||||
|
$this->createStatsNewsletter($subscriber2, $this->newsletter2);
|
||||||
|
$this->createStatsNewsletter($subscriber3, $this->newsletter2);
|
||||||
|
|
||||||
|
$segmentFilterData = $this->getSegmentFilterData(EmailAction::ACTION_WAS_SENT, [
|
||||||
|
'newsletters' => [$this->newsletter->getId(), $this->newsletter2->getId()],
|
||||||
|
'operator' => DynamicSegmentFilterData::OPERATOR_ALL,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$emails = $this->tester->getSubscriberEmailsMatchingDynamicFilter($segmentFilterData, $this->emailAction);
|
||||||
|
|
||||||
|
expect($emails)->contains('1@example.com');
|
||||||
|
expect($emails)->notContains('2@example.com');
|
||||||
|
expect($emails)->notContains('3@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSentEmailNone(): void {
|
||||||
|
$subscriber1 = $this->createSubscriber('1@example.com');
|
||||||
|
$subscriber2 = $this->createSubscriber('2@example.com');
|
||||||
|
$subscriber3 = $this->createSubscriber('3@example.com');
|
||||||
|
|
||||||
|
$this->createStatsNewsletter($subscriber1, $this->newsletter);
|
||||||
|
$this->createStatsNewsletter($subscriber1, $this->newsletter2);
|
||||||
|
$this->createStatsNewsletter($subscriber2, $this->newsletter2);
|
||||||
|
$this->createStatsNewsletter($subscriber3, $this->newsletter3);
|
||||||
|
|
||||||
|
$segmentFilterData = $this->getSegmentFilterData(EmailAction::ACTION_WAS_SENT, [
|
||||||
|
'newsletters' => [$this->newsletter->getId(), $this->newsletter2->getId()],
|
||||||
|
'operator' => DynamicSegmentFilterData::OPERATOR_NONE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$emails = $this->tester->getSubscriberEmailsMatchingDynamicFilter($segmentFilterData, $this->emailAction);
|
||||||
|
|
||||||
|
expect($emails)->notContains('1@example.com');
|
||||||
|
expect($emails)->notContains('2@example.com');
|
||||||
|
expect($emails)->contains('3@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
private function getSegmentFilterData(string $action, array $data): DynamicSegmentFilterData {
|
private function getSegmentFilterData(string $action, array $data): DynamicSegmentFilterData {
|
||||||
return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $action, $data);
|
return new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_EMAIL, $action, $data);
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@
|
|||||||
'selectUserRolePlaceholder': __('Search user roles'),
|
'selectUserRolePlaceholder': __('Search user roles'),
|
||||||
'selectCustomFieldPlaceholder': __('Select custom field'),
|
'selectCustomFieldPlaceholder': __('Select custom field'),
|
||||||
'emailActionOpened': _x('opened', 'Dynamic segment creation: when newsletter was opened'),
|
'emailActionOpened': _x('opened', 'Dynamic segment creation: when newsletter was opened'),
|
||||||
|
'emailActionWasSent': _x('was sent', 'Dynamic segment creation: when newsletter was sent'),
|
||||||
'emailActionMachineOpened': _x('machine-opened', 'Dynamic segment creation: list of all subscribers that opened the newsletter automatically in the background'),
|
'emailActionMachineOpened': _x('machine-opened', 'Dynamic segment creation: list of all subscribers that opened the newsletter automatically in the background'),
|
||||||
'emailActionOpensAbsoluteCount': __('# of opens'),
|
'emailActionOpensAbsoluteCount': __('# of opens'),
|
||||||
'emailActionMachineOpensAbsoluteCount': __('# of machine-opens'),
|
'emailActionMachineOpensAbsoluteCount': __('# of machine-opens'),
|
||||||
|
Reference in New Issue
Block a user