Add newsletter statistics to doctrine
[MAILPOET-2439]
This commit is contained in:
committed by
Jack Kitterhing
parent
ad6e6009d2
commit
5a2edff9fe
@ -5,6 +5,7 @@ namespace MailPoet\Cron\Workers\StatsNotifications;
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Config\Renderer;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterLinkEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\StatsNotificationEntity;
|
||||
@ -12,6 +13,7 @@ use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Mailer\MetaInfo;
|
||||
use MailPoet\Models\NewsletterLink;
|
||||
use MailPoet\Models\ScheduledTask;
|
||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Tasks\Sending;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
@ -46,6 +48,9 @@ class Worker {
|
||||
/** @var NewsletterLinkRepository */
|
||||
private $newsletter_link_repository;
|
||||
|
||||
/** @var NewsletterStatisticsRepository */
|
||||
private $newsletter_statistics_repository;
|
||||
|
||||
function __construct(
|
||||
Mailer $mailer,
|
||||
Renderer $renderer,
|
||||
@ -53,6 +58,7 @@ class Worker {
|
||||
MetaInfo $mailerMetaInfo,
|
||||
StatsNotificationsRepository $repository,
|
||||
NewsletterLinkRepository $newsletter_link_repository,
|
||||
NewsletterStatisticsRepository $newsletter_statistics_repository,
|
||||
EntityManager $entity_manager,
|
||||
$timer = false
|
||||
) {
|
||||
@ -64,6 +70,7 @@ class Worker {
|
||||
$this->repository = $repository;
|
||||
$this->entity_manager = $entity_manager;
|
||||
$this->newsletter_link_repository = $newsletter_link_repository;
|
||||
$this->newsletter_statistics_repository = $newsletter_statistics_repository;
|
||||
}
|
||||
|
||||
/** @throws \Exception */
|
||||
@ -94,14 +101,14 @@ class Worker {
|
||||
}
|
||||
|
||||
private function constructNewsletter(StatsNotificationEntity $stats_notification_entity) {
|
||||
$newsletter = $this->getNewsletter($stats_notification_entity);
|
||||
$newsletter = $stats_notification_entity->getNewsletter();
|
||||
try {
|
||||
$link = $this->newsletter_link_repository->findTopLinkForNewsletter($newsletter->id);
|
||||
$link = $this->newsletter_link_repository->findTopLinkForNewsletter($newsletter->getId());
|
||||
} catch (\MailPoetVendor\Doctrine\ORM\UnexpectedResultException $e) {
|
||||
$link = null;
|
||||
}
|
||||
$context = $this->prepareContext($newsletter, $link);
|
||||
$subject = $newsletter->queue['newsletter_rendered_subject'];
|
||||
$subject = $newsletter->getLatestQueue()->getNewsletterRenderedSubject();
|
||||
return [
|
||||
'subject' => sprintf(_x('Stats for email %s', 'title of an automatic email containing statistics (newsletter open rate, click rate, etc)', 'mailpoet'), $subject),
|
||||
'body' => [
|
||||
@ -111,25 +118,12 @@ class Worker {
|
||||
];
|
||||
}
|
||||
|
||||
private function getNewsletter(StatsNotificationEntity $stats_notification_entity) {
|
||||
$newsletter = $stats_notification_entity->getNewsletter();
|
||||
$newsletter = Newsletter::findOne($newsletter->getId());
|
||||
return $newsletter
|
||||
->withSendingQueue()
|
||||
->withTotalSent()
|
||||
->withStatistics($this->woocommerce_helper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Newsletter $newsletter
|
||||
* @param NewsletterLinkEntity $link
|
||||
* @return array
|
||||
*/
|
||||
private function prepareContext(Newsletter $newsletter, NewsletterLinkEntity $link = null) {
|
||||
$clicked = ($newsletter->statistics['clicked'] * 100) / $newsletter->total_sent;
|
||||
$opened = ($newsletter->statistics['opened'] * 100) / $newsletter->total_sent;
|
||||
$unsubscribed = ($newsletter->statistics['unsubscribed'] * 100) / $newsletter->total_sent;
|
||||
$subject = $newsletter->queue['newsletter_rendered_subject'];
|
||||
private function prepareContext(NewsletterEntity $newsletter, NewsletterLinkEntity $link = null) {
|
||||
$statistics = $this->newsletter_statistics_repository->getStatistics($newsletter);
|
||||
$clicked = ($statistics->getClickCount() * 100) / $statistics->getTotalSentCount();
|
||||
$opened = ($statistics->getOpenCount() * 100) / $statistics->getTotalSentCount();
|
||||
$unsubscribed = ($statistics->getUnsubscribeCount() * 100) / $statistics->getTotalSentCount();
|
||||
$subject = $newsletter->getLatestQueue()->getNewsletterRenderedSubject();
|
||||
$context = [
|
||||
'subject' => $subject,
|
||||
'preheader' => sprintf(_x(
|
||||
@ -140,7 +134,7 @@ class Worker {
|
||||
),
|
||||
'topLinkClicks' => 0,
|
||||
'linkSettings' => WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-settings#basics'),
|
||||
'linkStats' => WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-newsletters#/stats/' . $newsletter->id()),
|
||||
'linkStats' => WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-newsletters#/stats/' . $newsletter->getId()),
|
||||
'clicked' => $clicked,
|
||||
'opened' => $opened,
|
||||
];
|
||||
|
@ -11,6 +11,7 @@ use MailPoet\Cron\Workers\SendingQueue\Migration as MigrationWorker;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingErrorHandler;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\AutomatedEmails as StatsNotificationsWorkerForAutomatedEmails;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\NewsletterLinkRepository;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\Scheduler as StatsNotificationScheduler;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\StatsNotificationsRepository;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\Worker as StatsNotificationsWorker;
|
||||
@ -19,6 +20,7 @@ use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Mailer\MetaInfo;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||
use MailPoet\Segments\SubscribersFinder;
|
||||
use MailPoet\Segments\WooCommerce as WooCommerceSegment;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
@ -81,6 +83,12 @@ class WorkersFactory {
|
||||
*/
|
||||
private $newsletters_repository;
|
||||
|
||||
/** @var NewsletterLinkRepository */
|
||||
private $newsletter_link_repository;
|
||||
|
||||
/** @var NewsletterStatisticsRepository */
|
||||
private $newsletter_statistics_repository;
|
||||
|
||||
public function __construct(
|
||||
SendingErrorHandler $sending_error_handler,
|
||||
StatsNotificationScheduler $statsNotificationsScheduler,
|
||||
@ -97,6 +105,8 @@ class WorkersFactory {
|
||||
LoggerFactory $logger_factory,
|
||||
StatsNotificationsRepository $stats_notifications_repository,
|
||||
NewslettersRepository $newsletters_repository,
|
||||
NewsletterLinkRepository $newsletter_link_repository,
|
||||
NewsletterStatisticsRepository $newsletter_statistics_repository,
|
||||
EntityManager $entity_manager
|
||||
) {
|
||||
$this->sending_error_handler = $sending_error_handler;
|
||||
@ -115,6 +125,8 @@ class WorkersFactory {
|
||||
$this->stats_notifications_repository = $stats_notifications_repository;
|
||||
$this->entity_manager = $entity_manager;
|
||||
$this->newsletters_repository = $newsletters_repository;
|
||||
$this->newsletter_link_repository = $newsletter_link_repository;
|
||||
$this->newsletter_statistics_repository = $newsletter_statistics_repository;
|
||||
}
|
||||
|
||||
/** @return SchedulerWorker */
|
||||
@ -141,6 +153,8 @@ class WorkersFactory {
|
||||
$this->settings,
|
||||
$this->mailerMetaInfo,
|
||||
$this->stats_notifications_repository,
|
||||
$this->newsletter_link_repository,
|
||||
$this->newsletter_statistics_repository,
|
||||
$this->entity_manager,
|
||||
$timer
|
||||
);
|
||||
|
@ -203,6 +203,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
||||
// Newsletter
|
||||
$container->autowire(\MailPoet\Newsletter\AutomatedLatestContent::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\Newsletter\NewslettersRepository::class);
|
||||
$container->autowire(\MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository::class);
|
||||
$container->autowire(\MailPoet\Newsletter\Scheduler\WelcomeScheduler::class);
|
||||
$container->autowire(\MailPoet\Newsletter\Scheduler\PostNotificationScheduler::class);
|
||||
// Util
|
||||
|
39
lib/Entities/StatisticsOpensEntity.php
Normal file
39
lib/Entities/StatisticsOpensEntity.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Entities;
|
||||
|
||||
use MailPoet\Doctrine\EntityTraits\AutoincrementedIdTrait;
|
||||
use MailPoet\Doctrine\EntityTraits\CreatedAtTrait;
|
||||
use MailPoet\Doctrine\EntityTraits\UpdatedAtTrait;
|
||||
use MailPoetVendor\Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="statistics_opens")
|
||||
*/
|
||||
class StatisticsOpensEntity {
|
||||
use AutoincrementedIdTrait;
|
||||
use CreatedAtTrait;
|
||||
use UpdatedAtTrait;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="MailPoet\Entities\NewsletterEntity")
|
||||
* @ORM\JoinColumn(name="newsletter_id", referencedColumnName="id")
|
||||
* @var NewsletterEntity|null
|
||||
*/
|
||||
private $newsletter;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="MailPoet\Entities\SendingQueueEntity")
|
||||
* @ORM\JoinColumn(name="queue_id", referencedColumnName="id")
|
||||
* @var SendingQueueEntity|null
|
||||
*/
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @var int|null
|
||||
*/
|
||||
private $subscriber_id;
|
||||
|
||||
}
|
39
lib/Entities/StatisticsUnsubscribesEntity.php
Normal file
39
lib/Entities/StatisticsUnsubscribesEntity.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Entities;
|
||||
|
||||
use MailPoet\Doctrine\EntityTraits\AutoincrementedIdTrait;
|
||||
use MailPoet\Doctrine\EntityTraits\CreatedAtTrait;
|
||||
use MailPoet\Doctrine\EntityTraits\UpdatedAtTrait;
|
||||
use MailPoetVendor\Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="statistics_unsubscribes")
|
||||
*/
|
||||
class StatisticsUnsubscribesEntity {
|
||||
use AutoincrementedIdTrait;
|
||||
use CreatedAtTrait;
|
||||
use UpdatedAtTrait;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="MailPoet\Entities\NewsletterEntity")
|
||||
* @ORM\JoinColumn(name="newsletter_id", referencedColumnName="id")
|
||||
* @var NewsletterEntity|null
|
||||
*/
|
||||
private $newsletter;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="MailPoet\Entities\SendingQueueEntity")
|
||||
* @ORM\JoinColumn(name="queue_id", referencedColumnName="id")
|
||||
* @var SendingQueueEntity|null
|
||||
*/
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @var int|null
|
||||
*/
|
||||
private $subscriber_id;
|
||||
|
||||
}
|
54
lib/Newsletter/Statistics/NewsletterStatistics.php
Normal file
54
lib/Newsletter/Statistics/NewsletterStatistics.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Newsletter\Statistics;
|
||||
|
||||
class NewsletterStatistics {
|
||||
|
||||
/** @var int */
|
||||
private $clickCount;
|
||||
|
||||
/** @var int */
|
||||
private $openCount;
|
||||
|
||||
/** @var int */
|
||||
private $unsubscribeCount;
|
||||
|
||||
/** @var int */
|
||||
private $total_sent_count;
|
||||
|
||||
function __construct($clickCount, $openCount, $unsubscribeCount, $total_sent_count) {
|
||||
$this->clickCount = $clickCount;
|
||||
$this->openCount = $openCount;
|
||||
$this->unsubscribeCount = $unsubscribeCount;
|
||||
$this->total_sent_count = $total_sent_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getClickCount() {
|
||||
return $this->clickCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOpenCount() {
|
||||
return $this->openCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUnsubscribeCount() {
|
||||
return $this->unsubscribeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalSentCount() {
|
||||
return $this->total_sent_count;
|
||||
}
|
||||
|
||||
}
|
90
lib/Newsletter/Statistics/NewsletterStatisticsRepository.php
Normal file
90
lib/Newsletter/Statistics/NewsletterStatisticsRepository.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Newsletter\Statistics;
|
||||
|
||||
use MailPoet\Doctrine\Repository;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\StatisticsClicksEntity;
|
||||
use MailPoet\Entities\StatisticsOpensEntity;
|
||||
use MailPoet\Entities\StatisticsUnsubscribesEntity;
|
||||
use MailPoetVendor\Doctrine\Common\Collections\Criteria;
|
||||
use MailPoetVendor\Doctrine\ORM\UnexpectedResultException as UnexpectedResultExceptionAlias;
|
||||
|
||||
class NewsletterStatisticsRepository extends Repository {
|
||||
protected function getEntityClassName() {
|
||||
return NewsletterEntity::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return int
|
||||
*/
|
||||
function getTotalSentCount(NewsletterEntity $newsletter) {
|
||||
try {
|
||||
return (int)$this->doctrine_repository
|
||||
->createQueryBuilder('n')
|
||||
->join('n.queues', 'q')
|
||||
->join('q.task', 't')
|
||||
->select('SUM(q.count_processed)')
|
||||
->where('t.status = :status')
|
||||
->setParameter('status', ScheduledTaskEntity::STATUS_COMPLETED)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
} catch (UnexpectedResultExceptionAlias $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return NewsletterStatistics
|
||||
*/
|
||||
function getStatistics(NewsletterEntity $newsletter) {
|
||||
return new NewsletterStatistics(
|
||||
$this->getStatisticsClickCount($newsletter),
|
||||
$this->getStatisticsOpenCount($newsletter),
|
||||
$this->getStatisticsUnsubscribeCount($newsletter),
|
||||
$this->getTotalSentCount($newsletter)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return int
|
||||
*/
|
||||
function getStatisticsClickCount(NewsletterEntity $newsletter) {
|
||||
return $this->getStatisticsCount($newsletter, StatisticsClicksEntity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return int
|
||||
*/
|
||||
function getStatisticsOpenCount(NewsletterEntity $newsletter) {
|
||||
return $this->getStatisticsCount($newsletter, StatisticsOpensEntity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return int
|
||||
*/
|
||||
function getStatisticsUnsubscribeCount(NewsletterEntity $newsletter) {
|
||||
return $this->getStatisticsCount($newsletter, StatisticsUnsubscribesEntity::class);
|
||||
}
|
||||
|
||||
private function getStatisticsCount(NewsletterEntity $newsletter, $statistics_entity_name) {
|
||||
try {
|
||||
$qb = $this->entity_manager
|
||||
->createQueryBuilder();
|
||||
return $qb->select('COUNT(DISTINCT stats.subscriber_id) as cnt')
|
||||
->from($statistics_entity_name, 'stats')
|
||||
->where('stats.newsletter = :newsletter')
|
||||
->setParameter('newsletter', $newsletter)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
} catch (UnexpectedResultExceptionAlias $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\StatisticsClicks;
|
||||
use MailPoet\Models\StatisticsOpens;
|
||||
use MailPoet\Models\StatisticsUnsubscribes;
|
||||
use MailPoet\Models\StatsNotification;
|
||||
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
@ -48,6 +48,7 @@ class WorkerTest extends \MailPoetTest {
|
||||
function _before() {
|
||||
parent::_before();
|
||||
\ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);
|
||||
\ORM::raw_execute('TRUNCATE ' . StatisticsClicks::$_table);
|
||||
\ORM::raw_execute('TRUNCATE ' . ScheduledTask::$_table);
|
||||
\ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
|
||||
$this->repository = ContainerWrapper::getInstance()->get(StatsNotificationsRepository::class);
|
||||
@ -63,6 +64,7 @@ class WorkerTest extends \MailPoetTest {
|
||||
new MetaInfo,
|
||||
$this->repository,
|
||||
$this->newsletter_link_repository,
|
||||
ContainerWrapper::getInstance()->get(NewsletterStatisticsRepository::class),
|
||||
$this->entity_manager
|
||||
);
|
||||
$this->settings->set(Worker::SETTINGS_KEY, [
|
||||
|
Reference in New Issue
Block a user