Add fetching of global subscribers stats to Homepage data controller

[MAILPOET-4828]
This commit is contained in:
Rostislav Wolny
2023-01-18 15:01:31 +01:00
committed by Aschepikov
parent 9217f197f9
commit ab35f5c8c1
4 changed files with 89 additions and 1 deletions

View File

@ -8,6 +8,7 @@ use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Automation\Integrations\MailPoet\Triggers\SomeoneSubscribesTrigger; use MailPoet\Automation\Integrations\MailPoet\Triggers\SomeoneSubscribesTrigger;
use MailPoet\Automation\Integrations\MailPoet\Triggers\UserRegistrationTrigger; use MailPoet\Automation\Integrations\MailPoet\Triggers\UserRegistrationTrigger;
use MailPoet\Entities\NewsletterEntity; use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Form\FormsRepository; use MailPoet\Form\FormsRepository;
use MailPoet\Newsletter\NewslettersRepository; use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Services\Bridge; use MailPoet\Services\Bridge;
@ -15,6 +16,8 @@ use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Util\License\Features\Subscribers; use MailPoet\Util\License\Features\Subscribers;
use MailPoet\WooCommerce\Helper as WooCommerceHelper; use MailPoet\WooCommerce\Helper as WooCommerceHelper;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Carbon\Carbon;
class HomepageDataController { class HomepageDataController {
public const UPSELL_SUBSCRIBERS_COUNT_REQUIRED = 600; public const UPSELL_SUBSCRIBERS_COUNT_REQUIRED = 600;
@ -40,6 +43,9 @@ class HomepageDataController {
/** @var Subscribers */ /** @var Subscribers */
private $subscribers; private $subscribers;
/** @var WPFunctions */
private $wp;
public function __construct( public function __construct(
SettingsController $settingsController, SettingsController $settingsController,
SubscribersRepository $subscribersRepository, SubscribersRepository $subscribersRepository,
@ -47,6 +53,7 @@ class HomepageDataController {
NewslettersRepository $newslettersRepository, NewslettersRepository $newslettersRepository,
AutomationStorage $automationStorage, AutomationStorage $automationStorage,
Subscribers $subscribers, Subscribers $subscribers,
WPFunctions $wp,
WooCommerceHelper $wooCommerceHelper WooCommerceHelper $wooCommerceHelper
) { ) {
$this->settingsController = $settingsController; $this->settingsController = $settingsController;
@ -54,6 +61,7 @@ class HomepageDataController {
$this->formsRepository = $formsRepository; $this->formsRepository = $formsRepository;
$this->newslettersRepository = $newslettersRepository; $this->newslettersRepository = $newslettersRepository;
$this->automationStorage = $automationStorage; $this->automationStorage = $automationStorage;
$this->wp = $wp;
$this->wooCommerceHelper = $wooCommerceHelper; $this->wooCommerceHelper = $wooCommerceHelper;
$this->subscribers = $subscribers; $this->subscribers = $subscribers;
} }
@ -73,6 +81,7 @@ class HomepageDataController {
'upsellStatus' => $showUpsell ? $this->getUpsellStatus($subscribersCount) : null, 'upsellStatus' => $showUpsell ? $this->getUpsellStatus($subscribersCount) : null,
'wooCustomersCount' => $this->wooCommerceHelper->getCustomersCount(), 'wooCustomersCount' => $this->wooCommerceHelper->getCustomersCount(),
'subscribersCount' => $subscribersCount, 'subscribersCount' => $subscribersCount,
'subscribersStats' => $this->getSubscribersStats(),
]; ];
} }
@ -129,4 +138,17 @@ class HomepageDataController {
'canDisplay' => !$hasValidMssKey && $subscribersCount > self::UPSELL_SUBSCRIBERS_COUNT_REQUIRED, 'canDisplay' => !$hasValidMssKey && $subscribersCount > self::UPSELL_SUBSCRIBERS_COUNT_REQUIRED,
]; ];
} }
/**
* @return array{global:array{subscribed:int, unsubscribed:int}}
*/
private function getSubscribersStats(): array {
$thirtyDaysAgo = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'))->subDays(30);
return [
'global' => [
'subscribed' => $this->subscribersRepository->getCountOfCreatedAfterWithStatues($thirtyDaysAgo, [SubscriberEntity::STATUS_SUBSCRIBED]),
'unsubscribed' => $this->subscribersRepository->getCountOfUnsubscribedAfter($thirtyDaysAgo),
],
];
}
} }

View File

@ -5,6 +5,7 @@ namespace MailPoet\Subscribers;
use MailPoet\Config\SubscriberChangesNotifier; use MailPoet\Config\SubscriberChangesNotifier;
use MailPoet\Doctrine\Repository; use MailPoet\Doctrine\Repository;
use MailPoet\Entities\SegmentEntity; use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\StatisticsUnsubscribeEntity;
use MailPoet\Entities\SubscriberCustomFieldEntity; use MailPoet\Entities\SubscriberCustomFieldEntity;
use MailPoet\Entities\SubscriberEntity; use MailPoet\Entities\SubscriberEntity;
use MailPoet\Entities\SubscriberSegmentEntity; use MailPoet\Entities\SubscriberSegmentEntity;
@ -370,6 +371,35 @@ class SubscribersRepository extends Repository {
return is_int($maxSubscriberId) ? $maxSubscriberId : 0; return is_int($maxSubscriberId) ? $maxSubscriberId : 0;
} }
public function getCountOfCreatedAfterWithStatues(\DateTimeInterface $createdAfter, array $statuses): int {
$result = $this->entityManager->createQueryBuilder()
->select('COUNT(s.id)')
->from(SubscriberEntity::class, 's')
->where('s.createdAt > :createdAfter')
->andWhere('s.status IN (:statuses)')
->andWhere('s.deletedAt IS NULL')
->setParameter('createdAfter', $createdAfter)
->setParameter('statuses', $statuses)
->getQuery()
->getSingleScalarResult();
return intval($result);
}
public function getCountOfUnsubscribedAfter(\DateTimeInterface $unsubscribedAfter): int {
$result = $this->entityManager->createQueryBuilder()
->select('COUNT(s.id)')
->from(StatisticsUnsubscribeEntity::class, 'su')
->join('su.subscriber', 's')
->where('s.createdAt > :unsubscribedAfter')
->andWhere('s.status = :status')
->andWhere('s.deletedAt IS NULL')
->setParameter('unsubscribedAfter', $unsubscribedAfter)
->setParameter('status', SubscriberEntity::STATUS_UNSUBSCRIBED)
->getQuery()
->getSingleScalarResult();
return intval($result);
}
/** /**
* @return int - number of processed ids * @return int - number of processed ids
*/ */

View File

@ -136,7 +136,7 @@ class Subscriber {
* @return $this * @return $this
*/ */
public function withCreatedAt(DateTimeInterface $createdAt) { public function withCreatedAt(DateTimeInterface $createdAt) {
$this->data['setCreatedAt'] = $createdAt; $this->data['createdAt'] = $createdAt;
return $this; return $this;
} }
@ -208,6 +208,7 @@ class Subscriber {
if (isset($this->data['subscribedIp'])) $subscriber->setSubscribedIp($this->data['subscribedIp']); if (isset($this->data['subscribedIp'])) $subscriber->setSubscribedIp($this->data['subscribedIp']);
if (isset($this->data['confirmedIp'])) $subscriber->setConfirmedIp($this->data['confirmedIp']); if (isset($this->data['confirmedIp'])) $subscriber->setConfirmedIp($this->data['confirmedIp']);
if (isset($this->data['unconfirmedData'])) $subscriber->setUnconfirmedData($this->data['unconfirmedData']); if (isset($this->data['unconfirmedData'])) $subscriber->setUnconfirmedData($this->data['unconfirmedData']);
if (isset($this->data['createdAt'])) $subscriber->setCreatedAt($this->data['createdAt']);
if (isset($this->data['source'])) { if (isset($this->data['source'])) {
$subscriber->setSource($this->data['source']); $subscriber->setSource($this->data['source']);
} }

View File

@ -7,11 +7,13 @@ use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\NewsletterOptionEntity; use MailPoet\Entities\NewsletterOptionEntity;
use MailPoet\Entities\NewsletterOptionFieldEntity; use MailPoet\Entities\NewsletterOptionFieldEntity;
use MailPoet\Entities\SettingEntity; use MailPoet\Entities\SettingEntity;
use MailPoet\Entities\StatisticsUnsubscribeEntity;
use MailPoet\Entities\SubscriberEntity; use MailPoet\Entities\SubscriberEntity;
use MailPoet\Settings\SettingsController; use MailPoet\Settings\SettingsController;
use MailPoet\Test\DataFactories\Form; use MailPoet\Test\DataFactories\Form;
use MailPoet\Test\DataFactories\Newsletter; use MailPoet\Test\DataFactories\Newsletter;
use MailPoet\Test\DataFactories\Subscriber; use MailPoet\Test\DataFactories\Subscriber;
use MailPoetVendor\Carbon\Carbon;
class HomepageDataControllerTest extends \MailPoetTest { class HomepageDataControllerTest extends \MailPoetTest {
/** @var HomepageDataController */ /** @var HomepageDataController */
@ -34,6 +36,8 @@ class HomepageDataControllerTest extends \MailPoetTest {
expect($data['productDiscoveryStatus'])->notEmpty(); expect($data['productDiscoveryStatus'])->notEmpty();
expect($data['wooCustomersCount'])->int(); expect($data['wooCustomersCount'])->int();
expect($data['subscribersCount'])->int(); expect($data['subscribersCount'])->int();
expect($data['subscribersStats'])->array();
expect($data['taskListStatus'])->notEmpty();
} }
public function testItFetchesSenderTaskListStatus(): void { public function testItFetchesSenderTaskListStatus(): void {
@ -169,6 +173,37 @@ class HomepageDataControllerTest extends \MailPoetTest {
expect($productDiscoveryStatus['setUpAbandonedCartEmail'])->true(); expect($productDiscoveryStatus['setUpAbandonedCartEmail'])->true();
} }
public function testItFetchesSubscriberStatsForZeroSubscribers(): void {
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['subscribed'])->equals(0);
expect($subscribersStats['global']['unsubscribed'])->equals(0);
}
public function testItFetchesCorrectGlobalSubscriberStats(): void {
$thirtyOneDaysAgo = Carbon::now()->subDays(31);
$twentyNineDaysAgo = Carbon::now()->subDays(29);
// Old subscribed
(new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)->create();
// New subscribed
(new Subscriber())->withCreatedAt($twentyNineDaysAgo)->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)->create();
// Unsubscribed long time ago
$oldUnsubscribed = (new Subscriber())->withCreatedAt($thirtyOneDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create();
$oldUnsubscribedStats = new StatisticsUnsubscribeEntity(null, null, $oldUnsubscribed);
$oldUnsubscribedStats->setCreatedAt($thirtyOneDaysAgo);
$this->entityManager->persist($oldUnsubscribedStats);
$this->entityManager->flush();
// Freshly unsubscribed
$newUnsubscribed = (new Subscriber())->withCreatedAt($twentyNineDaysAgo)->withStatus(SubscriberEntity::STATUS_UNSUBSCRIBED)->create();
$newUnsubscribedStats = new StatisticsUnsubscribeEntity(null, null, $newUnsubscribed);
$newUnsubscribedStats->setCreatedAt($twentyNineDaysAgo);
$this->entityManager->persist($newUnsubscribedStats);
$this->entityManager->flush();
$subscribersStats = $this->homepageDataController->getPageData()['subscribersStats'];
expect($subscribersStats['global']['subscribed'])->equals(1);
expect($subscribersStats['global']['unsubscribed'])->equals(1);
}
private function cleanup(): void { private function cleanup(): void {
$this->truncateEntity(SettingEntity::class); $this->truncateEntity(SettingEntity::class);
$this->truncateEntity(SubscriberEntity::class); $this->truncateEntity(SubscriberEntity::class);