Add SegmentSubscriberRepository with basic methods
[MAILPOET-3077]
This commit is contained in:
committed by
Veljko V
parent
a1b3677223
commit
231b7eb2a9
@ -256,8 +256,10 @@ class ContainerConfigurator implements IContainerConfigurator {
|
|||||||
$container->autowire(\MailPoet\Segments\WooCommerce::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\WooCommerce::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\SubscribersFinder::class);
|
$container->autowire(\MailPoet\Segments\SubscribersFinder::class);
|
||||||
$container->autowire(\MailPoet\Segments\SegmentsRepository::class);
|
$container->autowire(\MailPoet\Segments\SegmentsRepository::class);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\UserRole::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\SegmentSubscribersRepository::class);
|
||||||
|
$container->autowire(\MailPoet\Segments\DynamicSegments\FilterHandler::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\EmailAction::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\EmailAction::class)->setPublic(true);
|
||||||
|
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\UserRole::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceProduct::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceProduct::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceCategory::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceCategory::class)->setPublic(true);
|
||||||
// Services
|
// Services
|
||||||
|
@ -105,4 +105,8 @@ class SegmentEntity {
|
|||||||
public function getDynamicFilters() {
|
public function getDynamicFilters() {
|
||||||
return $this->dynamicFilters;
|
return $this->dynamicFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isDynamic(): bool {
|
||||||
|
return $this->getType() === self::TYPE_DYNAMIC;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
54
lib/Segments/DynamicSegments/FilterHandler.php
Normal file
54
lib/Segments/DynamicSegments/FilterHandler.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Segments\DynamicSegments;
|
||||||
|
|
||||||
|
use MailPoet\DynamicSegments\Exceptions\InvalidSegmentTypeException;
|
||||||
|
use MailPoet\Entities\DynamicSegmentFilterEntity;
|
||||||
|
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 MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
|
||||||
|
|
||||||
|
class FilterHandler {
|
||||||
|
/** @var EmailAction */
|
||||||
|
private $emailAction;
|
||||||
|
|
||||||
|
/** @var UserRole */
|
||||||
|
private $userRole;
|
||||||
|
|
||||||
|
/** @var WooCommerceProduct */
|
||||||
|
private $wooCommerceProduct;
|
||||||
|
|
||||||
|
/** @var WooCommerceCategory */
|
||||||
|
private $wooCommerceCategory;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EmailAction $emailAction,
|
||||||
|
UserRole $userRole,
|
||||||
|
WooCommerceProduct $wooCommerceProduct,
|
||||||
|
WooCommerceCategory $wooCommerceCategory
|
||||||
|
) {
|
||||||
|
$this->emailAction = $emailAction;
|
||||||
|
$this->userRole = $userRole;
|
||||||
|
$this->wooCommerceProduct = $wooCommerceProduct;
|
||||||
|
$this->wooCommerceCategory = $wooCommerceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filterEntity): QueryBuilder {
|
||||||
|
switch ($filterEntity->getSegmentType()) {
|
||||||
|
case DynamicSegmentFilterEntity::TYPE_USER_ROLE:
|
||||||
|
return $this->userRole->apply($queryBuilder, $filterEntity);
|
||||||
|
case DynamicSegmentFilterEntity::TYPE_EMAIL:
|
||||||
|
return $this->emailAction->apply($queryBuilder, $filterEntity);
|
||||||
|
case DynamicSegmentFilterEntity::TYPE_WOOCOMMERCE:
|
||||||
|
$action = $filterEntity->getFilterDataParam('action');
|
||||||
|
if ($action === WooCommerceProduct::ACTION_PRODUCT) {
|
||||||
|
return $this->wooCommerceProduct->apply($queryBuilder, $filterEntity);
|
||||||
|
}
|
||||||
|
return $this->wooCommerceCategory->apply($queryBuilder, $filterEntity);
|
||||||
|
default:
|
||||||
|
throw new InvalidSegmentTypeException('Invalid type', InvalidSegmentTypeException::INVALID_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
lib/Segments/SegmentSubscribersRepository.php
Normal file
111
lib/Segments/SegmentSubscribersRepository.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MailPoet\Segments;
|
||||||
|
|
||||||
|
use MailPoet\Entities\SegmentEntity;
|
||||||
|
use MailPoet\Entities\SubscriberEntity;
|
||||||
|
use MailPoet\Entities\SubscriberSegmentEntity;
|
||||||
|
use MailPoet\InvalidStateException;
|
||||||
|
use MailPoet\NotFoundException;
|
||||||
|
use MailPoet\Segments\DynamicSegments\FilterHandler;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Driver\Statement;
|
||||||
|
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
|
||||||
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
|
use function \MailPoetVendor\array_column;
|
||||||
|
|
||||||
|
class SegmentSubscribersRepository {
|
||||||
|
/** @var EntityManager */
|
||||||
|
private $entityManager;
|
||||||
|
|
||||||
|
/** @var FilterHandler */
|
||||||
|
private $filterHandler;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EntityManager $entityManager,
|
||||||
|
FilterHandler $filterHandler
|
||||||
|
) {
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
$this->filterHandler = $filterHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscriberIdsInSegment(int $segmentId): array {
|
||||||
|
$segment = $this->getSegment($segmentId);
|
||||||
|
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
|
||||||
|
$queryBuilder = $this->entityManager
|
||||||
|
->getConnection()
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select("DISTINCT $subscribersTable.id")
|
||||||
|
->from($subscribersTable);
|
||||||
|
|
||||||
|
if ($segment->isDynamic()) {
|
||||||
|
$queryBuilder = $this->filterSubscribersInDynamicSegment($queryBuilder, $segment);
|
||||||
|
} else {
|
||||||
|
$queryBuilder = $this->filterSubscribersInStaticSegment($queryBuilder, $segment);
|
||||||
|
}
|
||||||
|
$statement = $this->executeQuery($queryBuilder);
|
||||||
|
$result = $statement->fetchAll();
|
||||||
|
return array_column($result, 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscribersCount(int $segmentId): int {
|
||||||
|
$segment = $this->getSegment($segmentId);
|
||||||
|
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
|
||||||
|
$queryBuilder = $this->entityManager
|
||||||
|
->getConnection()
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select("DISTINCT count($subscribersTable.id)")
|
||||||
|
->from($subscribersTable);
|
||||||
|
|
||||||
|
if ($segment->isDynamic()) {
|
||||||
|
$queryBuilder = $this->filterSubscribersInDynamicSegment($queryBuilder, $segment);
|
||||||
|
} else {
|
||||||
|
$queryBuilder = $this->filterSubscribersInStaticSegment($queryBuilder, $segment);
|
||||||
|
}
|
||||||
|
$statement = $this->executeQuery($queryBuilder);
|
||||||
|
$result = $statement->fetchColumn();
|
||||||
|
return (int)$result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterSubscribersInStaticSegment(QueryBuilder $queryBuilder, SegmentEntity $segment): QueryBuilder {
|
||||||
|
$subscribersSegmentsTable = $this->entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
|
||||||
|
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
|
||||||
|
return $queryBuilder->join(
|
||||||
|
$subscribersTable,
|
||||||
|
$subscribersSegmentsTable,
|
||||||
|
'subsegment',
|
||||||
|
"subsegment.subscriber_id = $subscribersTable.id AND subsegment.segment_id = :segment"
|
||||||
|
)->andWhere("$subscribersTable.deleted_at IS NULL")
|
||||||
|
->andWhere("$subscribersTable.status = :status")
|
||||||
|
->setParameter('segment', $segment->getId())
|
||||||
|
->setParameter('status', SubscriberEntity::STATUS_SUBSCRIBED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterSubscribersInDynamicSegment(QueryBuilder $queryBuilder, SegmentEntity $segment): QueryBuilder {
|
||||||
|
$filters = $segment->getDynamicFilters();
|
||||||
|
// We don't allow dynamic segment without filers since it would return all subscribers
|
||||||
|
if (count($filters) === 0) {
|
||||||
|
throw new InvalidStateException('Missing filters for dynamic segment.');
|
||||||
|
}
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$queryBuilder = $this->filterHandler->apply($queryBuilder, $filter);
|
||||||
|
}
|
||||||
|
return $queryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSegment(int $id): SegmentEntity {
|
||||||
|
$segment = $this->entityManager->find(SegmentEntity::class, $id);
|
||||||
|
if (!$segment instanceof SegmentEntity) {
|
||||||
|
throw new NotFoundException('Segment not found');
|
||||||
|
}
|
||||||
|
return $segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeQuery(QueryBuilder $queryBuilder): Statement {
|
||||||
|
$statement = $queryBuilder->execute();
|
||||||
|
// Execute for select always returns statement but PHP Stan doesn't know that :(
|
||||||
|
if (!$statement instanceof Statement) {
|
||||||
|
throw new InvalidStateException('Invalid query.');
|
||||||
|
}
|
||||||
|
return $statement;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user