Cache homepage subscribers stats

[MAILPOET-5180]
This commit is contained in:
alex-mailpoet
2023-05-09 11:35:40 +03:00
committed by Aschepikov
parent d7fd11387f
commit 4cd1667146
4 changed files with 64 additions and 20 deletions

View File

@@ -8,6 +8,7 @@ use MailPoetVendor\Carbon\Carbon;
class TransientCache { class TransientCache {
public const SUBSCRIBERS_STATISTICS_COUNT_KEY = 'mailpoet_subscribers_statistics_count_cache'; public const SUBSCRIBERS_STATISTICS_COUNT_KEY = 'mailpoet_subscribers_statistics_count_cache';
public const SUBSCRIBERS_GLOBAL_STATUS_STATISTICS_COUNT_KEY = 'mailpoet_subscribers_statistics_count_global_status_cache'; public const SUBSCRIBERS_GLOBAL_STATUS_STATISTICS_COUNT_KEY = 'mailpoet_subscribers_statistics_count_global_status_cache';
public const SUBSCRIBERS_HOMEPAGE_STATISTICS_COUNT_KEY = 'mailpoet_subscribers_statistics_count_homepage_cache';
/** @var WPFunctions */ /** @var WPFunctions */
private $wp; private $wp;

View File

@@ -46,7 +46,10 @@ class SubscribersCountCacheRecalculation extends SimpleWorker {
// update cache for subscribers without segment // update cache for subscribers without segment
$this->recalculateSegmentCache($timer, 0); $this->recalculateSegmentCache($timer, 0);
$this->recalculateHomepageCache($timer);
// remove redundancies from cache // remove redundancies from cache
$this->cronHelper->enforceExecutionLimit($timer);
$this->subscribersCountsController->removeRedundancyFromStatisticsCache(); $this->subscribersCountsController->removeRedundancyFromStatisticsCache();
return true; return true;
@@ -68,6 +71,16 @@ class SubscribersCountCacheRecalculation extends SimpleWorker {
} }
} }
private function recalculateHomepageCache($timer): void {
$this->cronHelper->enforceExecutionLimit($timer);
$now = Carbon::now();
$item = $this->transientCache->getItem(TransientCache::SUBSCRIBERS_HOMEPAGE_STATISTICS_COUNT_KEY, 0);
if ($item === null || !isset($item['created_at']) || $now->diffInMinutes($item['created_at']) > self::EXPIRATION_IN_MINUTES) {
$this->cronHelper->enforceExecutionLimit($timer);
$this->subscribersCountsController->recalculateHomepageStatisticsCache();
}
}
public function getNextRunDate() { public function getNextRunDate() {
return Carbon::createFromTimestamp($this->wp->currentTime('timestamp')); return Carbon::createFromTimestamp($this->wp->currentTime('timestamp'));
} }

View File

@@ -8,16 +8,13 @@ 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;
use MailPoet\Settings\SettingsController; use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Subscribers\SubscribersCountsController;
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature; use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
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;
@@ -25,9 +22,6 @@ class HomepageDataController {
/** @var SettingsController */ /** @var SettingsController */
private $settingsController; private $settingsController;
/** @var SubscribersRepository */
private $subscribersRepository;
/** @var FormsRepository */ /** @var FormsRepository */
private $formsRepository; private $formsRepository;
@@ -43,27 +37,25 @@ class HomepageDataController {
/** @var SubscribersFeature */ /** @var SubscribersFeature */
private $subscribersFeature; private $subscribersFeature;
/** @var WPFunctions */ /** @var SubscribersCountsController */
private $wp; private $subscribersCountsController;
public function __construct( public function __construct(
SettingsController $settingsController, SettingsController $settingsController,
SubscribersRepository $subscribersRepository,
FormsRepository $formsRepository, FormsRepository $formsRepository,
NewslettersRepository $newslettersRepository, NewslettersRepository $newslettersRepository,
AutomationStorage $automationStorage, AutomationStorage $automationStorage,
SubscribersFeature $subscribersFeature, SubscribersFeature $subscribersFeature,
WPFunctions $wp, SubscribersCountsController $subscribersCountsController,
WooCommerceHelper $wooCommerceHelper WooCommerceHelper $wooCommerceHelper
) { ) {
$this->settingsController = $settingsController; $this->settingsController = $settingsController;
$this->subscribersRepository = $subscribersRepository;
$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->subscribersFeature = $subscribersFeature; $this->subscribersFeature = $subscribersFeature;
$this->subscribersCountsController = $subscribersCountsController;
} }
public function getPageData(): array { public function getPageData(): array {
@@ -158,15 +150,15 @@ class HomepageDataController {
* @return array{global:array{subscribed:int, unsubscribed:int, changePercent:float|int}, lists:array<int, array>} * @return array{global:array{subscribed:int, unsubscribed:int, changePercent:float|int}, lists:array<int, array>}
*/ */
private function getSubscribersStats(): array { private function getSubscribersStats(): array {
$thirtyDaysAgo = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'))->subDays(30);
$listData = []; $listData = [];
$listsDataSubscribed = $this->subscribersRepository->getListLevelCountsOfSubscribedAfter($thirtyDaysAgo); $counts = $this->subscribersCountsController->getHomepageStatistics();
$listsDataSubscribed = $counts['listsDataSubscribed'] ?? [];
foreach ($listsDataSubscribed as $list) { foreach ($listsDataSubscribed as $list) {
$listData[$list['id']] = array_intersect_key($list, array_flip(['name', 'id', 'type', 'averageEngagementScore'])); $listData[$list['id']] = array_intersect_key($list, array_flip(['name', 'id', 'type', 'averageEngagementScore']));
$listData[$list['id']]['subscribed'] = $list['count']; $listData[$list['id']]['subscribed'] = $list['count'];
$listData[$list['id']]['unsubscribed'] = 0; $listData[$list['id']]['unsubscribed'] = 0;
} }
$listsDataUnsubscribed = $this->subscribersRepository->getListLevelCountsOfUnsubscribedAfter($thirtyDaysAgo); $listsDataUnsubscribed = $counts['listsDataUnsubscribed'] ?? [];
foreach ($listsDataUnsubscribed as $list) { foreach ($listsDataUnsubscribed as $list) {
if (!isset($listData[$list['id']])) { if (!isset($listData[$list['id']])) {
$listData[$list['id']] = array_intersect_key($list, array_flip(['name', 'id', 'type', 'averageEngagementScore'])); $listData[$list['id']] = array_intersect_key($list, array_flip(['name', 'id', 'type', 'averageEngagementScore']));
@@ -175,9 +167,9 @@ class HomepageDataController {
$listData[$list['id']]['unsubscribed'] = $list['count']; $listData[$list['id']]['unsubscribed'] = $list['count'];
} }
$subscribedCount = $this->subscribersRepository->getCountOfLastSubscribedAfter($thirtyDaysAgo); $subscribedCount = intval($counts['subscribedCount'] ?? 0);
$unsubscribedCount = $this->subscribersRepository->getCountOfUnsubscribedAfter($thirtyDaysAgo); $unsubscribedCount = intval($counts['unsubscribedCount'] ?? 0);
$subscribedSubscribersCount = $this->subscribersRepository->getCountOfSubscribersForStates([SubscriberEntity::STATUS_SUBSCRIBED]); $subscribedSubscribersCount = intval($counts['subscribedSubscribersCount'] ?? 0);
$subscribedSubscribers30DaysAgo = $subscribedSubscribersCount - $subscribedCount + $unsubscribedCount; $subscribedSubscribers30DaysAgo = $subscribedSubscribersCount - $subscribedCount + $unsubscribedCount;
if ($subscribedSubscribers30DaysAgo > 0) { if ($subscribedSubscribers30DaysAgo > 0) {
$globalChangePercent = (($subscribedSubscribersCount - $subscribedSubscribers30DaysAgo) / $subscribedSubscribers30DaysAgo) * 100; $globalChangePercent = (($subscribedSubscribersCount - $subscribedSubscribers30DaysAgo) / $subscribedSubscribers30DaysAgo) * 100;

View File

@@ -4,10 +4,14 @@ namespace MailPoet\Subscribers;
use MailPoet\Cache\TransientCache; use MailPoet\Cache\TransientCache;
use MailPoet\Entities\SegmentEntity; use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\InvalidStateException; use MailPoet\InvalidStateException;
use MailPoet\Segments\SegmentsRepository; use MailPoet\Segments\SegmentsRepository;
use MailPoet\Segments\SegmentSubscribersRepository; use MailPoet\Segments\SegmentSubscribersRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tags\TagRepository; use MailPoet\Tags\TagRepository;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Carbon\Carbon;
class SubscribersCountsController { class SubscribersCountsController {
/** @var SegmentsRepository */ /** @var SegmentsRepository */
@@ -16,23 +20,33 @@ class SubscribersCountsController {
/** @var SegmentSubscribersRepository */ /** @var SegmentSubscribersRepository */
private $segmentSubscribersRepository; private $segmentSubscribersRepository;
/** @var SubscribersRepository */
private $subscribersRepository;
/** @var TagRepository */ /** @var TagRepository */
private $tagRepository; private $tagRepository;
/** @var TransientCache */ /** @var TransientCache */
private $transientCache; private $transientCache;
/** @var WPFunctions */
private $wp;
public function __construct( public function __construct(
SegmentsRepository $segmentsRepository, SegmentsRepository $segmentsRepository,
SegmentSubscribersRepository $segmentSubscribersRepository, SegmentSubscribersRepository $segmentSubscribersRepository,
SubscribersRepository $subscribersRepository,
TagRepository $subscriberTagRepository, TagRepository $subscriberTagRepository,
TransientCache $transientCache TransientCache $transientCache,
WPFunctions $wp
) { ) {
$this->segmentSubscribersRepository = $segmentSubscribersRepository; $this->segmentSubscribersRepository = $segmentSubscribersRepository;
$this->transientCache = $transientCache; $this->transientCache = $transientCache;
$this->segmentsRepository = $segmentsRepository; $this->segmentsRepository = $segmentsRepository;
$this->subscribersRepository = $subscribersRepository;
$this->tagRepository = $subscriberTagRepository; $this->tagRepository = $subscriberTagRepository;
$this->wp = $wp;
} }
public function getSubscribersWithoutSegmentStatisticsCount(): array { public function getSubscribersWithoutSegmentStatisticsCount(): array {
@@ -71,6 +85,14 @@ class SubscribersCountsController {
return $result; return $result;
} }
public function getHomepageStatistics(): array {
$result = $this->transientCache->getItem(TransientCache::SUBSCRIBERS_HOMEPAGE_STATISTICS_COUNT_KEY, 0) ?? [];
if (!$result) {
$result = $this->recalculateHomepageStatisticsCache();
}
return $result;
}
public function recalculateSegmentGlobalStatusStatisticsCache(SegmentEntity $segment): array { public function recalculateSegmentGlobalStatusStatisticsCache(SegmentEntity $segment): array {
$result = $this->segmentSubscribersRepository->getSubscribersGlobalStatusStatisticsCount($segment); $result = $this->segmentSubscribersRepository->getSubscribersGlobalStatusStatisticsCount($segment);
$this->transientCache->setItem( $this->transientCache->setItem(
@@ -97,6 +119,22 @@ class SubscribersCountsController {
return $result; return $result;
} }
public function recalculateHomepageStatisticsCache(): array {
$thirtyDaysAgo = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'))->subDays(30);
$result = [];
$result['listsDataSubscribed'] = $this->subscribersRepository->getListLevelCountsOfSubscribedAfter($thirtyDaysAgo);
$result['listsDataUnsubscribed'] = $this->subscribersRepository->getListLevelCountsOfUnsubscribedAfter($thirtyDaysAgo);
$result['subscribedCount'] = $this->subscribersRepository->getCountOfLastSubscribedAfter($thirtyDaysAgo);
$result['unsubscribedCount'] = $this->subscribersRepository->getCountOfUnsubscribedAfter($thirtyDaysAgo);
$result['subscribedSubscribersCount'] = $this->subscribersRepository->getCountOfSubscribersForStates([SubscriberEntity::STATUS_SUBSCRIBED]);
$this->transientCache->setItem(
TransientCache::SUBSCRIBERS_HOMEPAGE_STATISTICS_COUNT_KEY,
$result,
0
);
return $result;
}
public function removeRedundancyFromStatisticsCache() { public function removeRedundancyFromStatisticsCache() {
$segments = $this->segmentsRepository->findAll(); $segments = $this->segmentsRepository->findAll();
$segmentIds = array_map(function (SegmentEntity $segment): int { $segmentIds = array_map(function (SegmentEntity $segment): int {