Fetch statistics for listing in batch
[MAILPOET-2645]
This commit is contained in:
committed by
Jack Kitterhing
parent
310d658647
commit
65ab8fd420
@@ -6,6 +6,7 @@ use MailPoet\Entities\NewsletterEntity;
|
|||||||
use MailPoet\Entities\SegmentEntity;
|
use MailPoet\Entities\SegmentEntity;
|
||||||
use MailPoet\Entities\SendingQueueEntity;
|
use MailPoet\Entities\SendingQueueEntity;
|
||||||
use MailPoet\Models\SendingQueue;
|
use MailPoet\Models\SendingQueue;
|
||||||
|
use MailPoet\Newsletter\Statistics\NewsletterStatistics;
|
||||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||||
|
|
||||||
class NewslettersResponseBuilder {
|
class NewslettersResponseBuilder {
|
||||||
@@ -76,7 +77,21 @@ class NewslettersResponseBuilder {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForListing(NewsletterEntity $newsletter): array {
|
/**
|
||||||
|
* @param NewsletterEntity[] $newsletters
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public function buildForListing(array $newsletters): array {
|
||||||
|
$statistics = $this->newslettersStatsRepository->getBatchStatistics($newsletters);
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
foreach ($newsletters as $newsletter) {
|
||||||
|
$data[] = $this->buildListingItem($newsletter, $statistics[$newsletter->getId()] ?? null);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildListingItem(NewsletterEntity $newsletter, NewsletterStatistics $statistics = null): array {
|
||||||
$data = [
|
$data = [
|
||||||
'id' => (string)$newsletter->getId(), // (string) for BC
|
'id' => (string)$newsletter->getId(), // (string) for BC
|
||||||
'hash' => $newsletter->getHash(),
|
'hash' => $newsletter->getHash(),
|
||||||
@@ -88,18 +103,18 @@ class NewslettersResponseBuilder {
|
|||||||
'deleted_at' => ($deletedAt = $newsletter->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
|
'deleted_at' => ($deletedAt = $newsletter->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
|
||||||
'segments' => [],
|
'segments' => [],
|
||||||
'queue' => false,
|
'queue' => false,
|
||||||
'statistics' => false,
|
'statistics' => ($statistics && $newsletter->getType() !== NewsletterEntity::TYPE_NOTIFICATION)
|
||||||
|
? $statistics->asArray()
|
||||||
|
: false,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD) {
|
if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD) {
|
||||||
$data['segments'] = $this->buildSegments($newsletter);
|
$data['segments'] = $this->buildSegments($newsletter);
|
||||||
$data['statistics'] = $this->newslettersStatsRepository->getStatistics($newsletter)->asArray();
|
|
||||||
$data['queue'] = ($queue = $newsletter->getLatestQueue()) ? $this->buildQueue($queue) : false; // false for BC
|
$data['queue'] = ($queue = $newsletter->getLatestQueue()) ? $this->buildQueue($queue) : false; // false for BC
|
||||||
} elseif (in_array($newsletter->getType(), [NewsletterEntity::TYPE_WELCOME, NewsletterEntity::TYPE_AUTOMATIC], true)) {
|
} elseif (in_array($newsletter->getType(), [NewsletterEntity::TYPE_WELCOME, NewsletterEntity::TYPE_AUTOMATIC], true)) {
|
||||||
$data['segments'] = [];
|
$data['segments'] = [];
|
||||||
$data['statistics'] = $this->newslettersStatsRepository->getStatistics($newsletter)->asArray();
|
|
||||||
$data['options'] = $this->buildOptions($newsletter);
|
$data['options'] = $this->buildOptions($newsletter);
|
||||||
$data['total_sent'] = $this->newslettersStatsRepository->getTotalSentCount($newsletter);
|
$data['total_sent'] = $statistics ? $statistics->getTotalSentCount() : 0;
|
||||||
$data['total_scheduled'] = (int)SendingQueue::findTaskByNewsletterId($newsletter->getId())
|
$data['total_scheduled'] = (int)SendingQueue::findTaskByNewsletterId($newsletter->getId())
|
||||||
->where('tasks.status', SendingQueue::STATUS_SCHEDULED)
|
->where('tasks.status', SendingQueue::STATUS_SCHEDULED)
|
||||||
->count();
|
->count();
|
||||||
@@ -109,7 +124,6 @@ class NewslettersResponseBuilder {
|
|||||||
$data['options'] = $this->buildOptions($newsletter);
|
$data['options'] = $this->buildOptions($newsletter);
|
||||||
} elseif ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION_HISTORY) {
|
} elseif ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION_HISTORY) {
|
||||||
$data['segments'] = $this->buildSegments($newsletter);
|
$data['segments'] = $this->buildSegments($newsletter);
|
||||||
$data['statistics'] = $this->newslettersStatsRepository->getStatistics($newsletter)->asArray();
|
|
||||||
$data['queue'] = ($queue = $newsletter->getLatestQueue()) ? $this->buildQueue($queue) : false; // false for BC
|
$data['queue'] = ($queue = $newsletter->getLatestQueue()) ? $this->buildQueue($queue) : false; // false for BC
|
||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
|
@@ -528,17 +528,16 @@ class Newsletters extends APIEndpoint {
|
|||||||
$groups = $this->newsletterListingRepository->getGroups($definition);
|
$groups = $this->newsletterListingRepository->getGroups($definition);
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
foreach ($items as $newsletter) {
|
foreach ($this->newslettersResponseBuilder->buildForListing($items) as $newsletterData) {
|
||||||
$queue = false;
|
$queue = false;
|
||||||
if (in_array($newsletter->getStatus(), [Newsletter::STATUS_SENT, Newsletter::STATUS_SENDING], true)) {
|
if (in_array($newsletterData['status'], [Newsletter::STATUS_SENT, Newsletter::STATUS_SENDING], true)) {
|
||||||
$queue = SendingTask::getByNewsletterId($newsletter->getId());
|
$queue = SendingTask::getByNewsletterId($newsletterData['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$newsletterData = $this->newslettersResponseBuilder->buildForListing($newsletter);
|
|
||||||
$newsletterData['preview_url'] = NewsletterUrl::getViewInBrowserUrl(
|
$newsletterData['preview_url'] = NewsletterUrl::getViewInBrowserUrl(
|
||||||
(object)[
|
(object)[
|
||||||
'id' => $newsletter->getId(),
|
'id' => $newsletterData['id'],
|
||||||
'hash' => $newsletter->getHash(),
|
'hash' => $newsletterData['hash'],
|
||||||
],
|
],
|
||||||
null,
|
null,
|
||||||
$queue
|
$queue
|
||||||
|
@@ -27,33 +27,7 @@ class NewsletterStatisticsRepository extends Repository {
|
|||||||
return NewsletterEntity::class;
|
return NewsletterEntity::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getStatistics(NewsletterEntity $newsletter): NewsletterStatistics {
|
||||||
* @param NewsletterEntity $newsletter
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getTotalSentCount(NewsletterEntity $newsletter) {
|
|
||||||
try {
|
|
||||||
return (int)$this->doctrineRepository
|
|
||||||
->createQueryBuilder('n')
|
|
||||||
->join('n.queues', 'q')
|
|
||||||
->join('q.task', 't')
|
|
||||||
->select('SUM(q.countProcessed)')
|
|
||||||
->where('t.status = :status')
|
|
||||||
->setParameter('status', ScheduledTaskEntity::STATUS_COMPLETED)
|
|
||||||
->andWhere('q.newsletter = :newsletter')
|
|
||||||
->setParameter('newsletter', $newsletter)
|
|
||||||
->getQuery()
|
|
||||||
->getSingleScalarResult();
|
|
||||||
} catch (UnexpectedResultException $e) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param NewsletterEntity $newsletter
|
|
||||||
* @return NewsletterStatistics
|
|
||||||
*/
|
|
||||||
public function getStatistics(NewsletterEntity $newsletter) {
|
|
||||||
return new NewsletterStatistics(
|
return new NewsletterStatistics(
|
||||||
$this->getStatisticsClickCount($newsletter),
|
$this->getStatisticsClickCount($newsletter),
|
||||||
$this->getStatisticsOpenCount($newsletter),
|
$this->getStatisticsOpenCount($newsletter),
|
||||||
@@ -64,51 +38,53 @@ class NewsletterStatisticsRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param NewsletterEntity $newsletter
|
* @param NewsletterEntity[] $newsletters
|
||||||
* @return int
|
* @return NewsletterStatistics[]
|
||||||
*/
|
*/
|
||||||
public function getStatisticsClickCount(NewsletterEntity $newsletter) {
|
public function getBatchStatistics(array $newsletters): array {
|
||||||
return $this->getStatisticsCount($newsletter, StatisticsClickEntity::class);
|
$totalSentCounts = $this->getTotalSentCounts($newsletters);
|
||||||
|
$clickCounts = $this->getStatisticCounts(StatisticsClickEntity::class, $newsletters);
|
||||||
|
$openCounts = $this->getStatisticCounts(StatisticsOpenEntity::class, $newsletters);
|
||||||
|
$unsubscribeCounts = $this->getStatisticCounts(StatisticsUnsubscribeEntity::class, $newsletters);
|
||||||
|
$wooCommerceRevenues = $this->getWooCommerceRevenues($newsletters);
|
||||||
|
|
||||||
|
$statistics = [];
|
||||||
|
foreach ($newsletters as $newsletter) {
|
||||||
|
$id = $newsletter->getId();
|
||||||
|
$statistics[$id] = new NewsletterStatistics(
|
||||||
|
$clickCounts[$id] ?? 0,
|
||||||
|
$openCounts[$id] ?? 0,
|
||||||
|
$unsubscribeCounts[$id] ?? 0,
|
||||||
|
$totalSentCounts[$id] ?? 0,
|
||||||
|
$wooCommerceRevenues[$id] ?? null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $statistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getTotalSentCount(NewsletterEntity $newsletter): int {
|
||||||
* @param NewsletterEntity $newsletter
|
$counts = $this->getTotalSentCounts([$newsletter]);
|
||||||
* @return int
|
return $counts[$newsletter->getId()] ?? 0;
|
||||||
*/
|
|
||||||
public function getStatisticsOpenCount(NewsletterEntity $newsletter) {
|
|
||||||
return $this->getStatisticsCount($newsletter, StatisticsOpenEntity::class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getStatisticsClickCount(NewsletterEntity $newsletter): int {
|
||||||
* @param NewsletterEntity $newsletter
|
$counts = $this->getStatisticCounts(StatisticsClickEntity::class, [$newsletter]);
|
||||||
* @return int
|
return $counts[$newsletter->getId()] ?? 0;
|
||||||
*/
|
}
|
||||||
public function getStatisticsUnsubscribeCount(NewsletterEntity $newsletter) {
|
|
||||||
return $this->getStatisticsCount($newsletter, StatisticsUnsubscribeEntity::class);
|
public function getStatisticsOpenCount(NewsletterEntity $newsletter): int {
|
||||||
|
$counts = $this->getStatisticCounts(StatisticsOpenEntity::class, [$newsletter]);
|
||||||
|
return $counts[$newsletter->getId()] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatisticsUnsubscribeCount(NewsletterEntity $newsletter): int {
|
||||||
|
$counts = $this->getStatisticCounts(StatisticsUnsubscribeEntity::class, [$newsletter]);
|
||||||
|
return $counts[$newsletter->getId()] ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWooCommerceRevenue(NewsletterEntity $newsletter) {
|
public function getWooCommerceRevenue(NewsletterEntity $newsletter) {
|
||||||
if (!$this->wcHelper->isWooCommerceActive()) {
|
$revenues = $this->getWooCommerceRevenues([$newsletter]);
|
||||||
return null;
|
return $revenues[$newsletter->getId()] ?? null;
|
||||||
}
|
|
||||||
try {
|
|
||||||
$currency = $this->wcHelper->getWoocommerceCurrency();
|
|
||||||
list($data) = $this->entityManager
|
|
||||||
->createQueryBuilder()
|
|
||||||
->select('SUM(stats.orderPriceTotal) AS total, COUNT(stats.id) AS cnt')
|
|
||||||
->from(StatisticsWooCommercePurchaseEntity::class, 'stats')
|
|
||||||
->where('stats.newsletter = :newsletter')
|
|
||||||
->andWhere('stats.orderCurrency = :currency')
|
|
||||||
->setParameter('newsletter', $newsletter)
|
|
||||||
->setParameter('currency', $currency)
|
|
||||||
->getQuery()
|
|
||||||
->getResult();
|
|
||||||
$value = (float)$data['total'];
|
|
||||||
$count = (int)$data['cnt'];
|
|
||||||
return new NewsletterWooCommerceRevenue($currency, $value, $count, $this->wcHelper);
|
|
||||||
} catch (UnexpectedResultException $e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,18 +106,71 @@ class NewsletterStatisticsRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getStatisticsCount(NewsletterEntity $newsletter, $statisticsEntityName) {
|
private function getTotalSentCounts(array $newsletters): array {
|
||||||
try {
|
$results = $this->doctrineRepository
|
||||||
$qb = $this->entityManager
|
->createQueryBuilder('n')
|
||||||
->createQueryBuilder();
|
->select('n.id, SUM(q.countProcessed) AS cnt')
|
||||||
return $qb->select('COUNT(DISTINCT stats.subscriberId) as cnt')
|
->join('n.queues', 'q')
|
||||||
->from($statisticsEntityName, 'stats')
|
->join('q.task', 't')
|
||||||
->where('stats.newsletter = :newsletter')
|
->where('t.status = :status')
|
||||||
->setParameter('newsletter', $newsletter)
|
->setParameter('status', ScheduledTaskEntity::STATUS_COMPLETED)
|
||||||
->getQuery()
|
->andWhere('q.newsletter IN (:newsletters)')
|
||||||
->getSingleScalarResult();
|
->setParameter('newsletters', $newsletters)
|
||||||
} catch (UnexpectedResultException $e) {
|
->groupBy('n.id')
|
||||||
return 0;
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$counts = [];
|
||||||
|
foreach ($results ?: [] as $result) {
|
||||||
|
$counts[(int)$result['id']] = (int)$result['cnt'];
|
||||||
}
|
}
|
||||||
|
return $counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getStatisticCounts(string $statisticsEntityName, array $newsletters): array {
|
||||||
|
$results = $this->entityManager->createQueryBuilder()
|
||||||
|
->select('IDENTITY(stats.newsletter) AS id, COUNT(DISTINCT stats.subscriberId) as cnt')
|
||||||
|
->from($statisticsEntityName, 'stats')
|
||||||
|
->where('stats.newsletter IN (:newsletters)')
|
||||||
|
->groupBy('stats.newsletter')
|
||||||
|
->setParameter('newsletters', $newsletters)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$counts = [];
|
||||||
|
foreach ($results ?: [] as $result) {
|
||||||
|
$counts[(int)$result['id']] = (int)$result['cnt'];
|
||||||
|
}
|
||||||
|
return $counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getWooCommerceRevenues(array $newsletters) {
|
||||||
|
if (!$this->wcHelper->isWooCommerceActive()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = $this->wcHelper->getWoocommerceCurrency();
|
||||||
|
$results = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('IDENTITY(stats.newsletter) AS id, SUM(stats.orderPriceTotal) AS total, COUNT(stats.id) AS cnt')
|
||||||
|
->from(StatisticsWooCommercePurchaseEntity::class, 'stats')
|
||||||
|
->where('stats.newsletter IN (:newsletters)')
|
||||||
|
->andWhere('stats.orderCurrency = :currency')
|
||||||
|
->setParameter('newsletters', $newsletters)
|
||||||
|
->setParameter('currency', $currency)
|
||||||
|
->groupBy('stats.newsletter')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$revenues = [];
|
||||||
|
foreach ($results ?: [] as $result) {
|
||||||
|
$revenues[(int)$result['id']] = new NewsletterWooCommerceRevenue(
|
||||||
|
$currency,
|
||||||
|
(float)$result['total'],
|
||||||
|
(int)$result['cnt'],
|
||||||
|
$this->wcHelper
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $revenues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ class NewslettersResponseBuilderTest extends \MailPoetTest {
|
|||||||
'getChildrenCount' => $stats['children_count'],
|
'getChildrenCount' => $stats['children_count'],
|
||||||
'getStatistics' => new NewsletterStatistics(4, 6, 2, 10, null),
|
'getStatistics' => new NewsletterStatistics(4, 6, 2, 10, null),
|
||||||
]);
|
]);
|
||||||
$responseBuilder = new NewslettersResponseBuilder($repository);
|
$responseBuilder = new NewslettersResponseBuilder($em, $repository);
|
||||||
$response = $responseBuilder->build($newsletter, [
|
$response = $responseBuilder->build($newsletter, [
|
||||||
NewslettersResponseBuilder::RELATION_CHILDREN_COUNT,
|
NewslettersResponseBuilder::RELATION_CHILDREN_COUNT,
|
||||||
NewslettersResponseBuilder::RELATION_TOTAL_SENT,
|
NewslettersResponseBuilder::RELATION_TOTAL_SENT,
|
||||||
|
@@ -63,6 +63,7 @@ class NewslettersTest extends \MailPoetTest {
|
|||||||
ContainerWrapper::getInstance()->get(Newsletters::class),
|
ContainerWrapper::getInstance()->get(Newsletters::class),
|
||||||
[
|
[
|
||||||
'newslettersResponseBuilder' => new NewslettersResponseBuilder(
|
'newslettersResponseBuilder' => new NewslettersResponseBuilder(
|
||||||
|
$this->diContainer->get(EntityManager::class),
|
||||||
new NewsletterStatisticsRepository(
|
new NewsletterStatisticsRepository(
|
||||||
$this->diContainer->get(EntityManager::class),
|
$this->diContainer->get(EntityManager::class),
|
||||||
$this->makeEmpty(WCHelper::class)
|
$this->makeEmpty(WCHelper::class)
|
||||||
|
Reference in New Issue
Block a user