Files
piratepoet/lib/Subscribers/InactiveSubscribersController.php
2020-01-14 15:22:42 +01:00

200 lines
7.0 KiB
PHP

<?php
namespace MailPoet\Subscribers;
use MailPoet\Config\MP2Migrator;
use MailPoet\Models\ScheduledTask;
use MailPoet\Models\ScheduledTaskSubscriber;
use MailPoet\Models\SendingQueue;
use MailPoet\Models\StatisticsOpens;
use MailPoet\Models\Subscriber;
use MailPoet\Settings\SettingsRepository;
use MailPoetVendor\Carbon\Carbon;
use MailPoetVendor\Idiorm\ORM;
class InactiveSubscribersController {
private $inactivesTaskIdsTableCreated = false;
/** @var SettingsRepository */
private $settingsRepository;
public function __construct(SettingsRepository $settingsRepository) {
$this->settingsRepository = $settingsRepository;
}
/**
* @param int $daysToInactive
* @param int $batchSize
* @return int|boolean
*/
public function markInactiveSubscribers($daysToInactive, $batchSize, $startId = null) {
$thresholdDate = $this->getThresholdDate($daysToInactive);
return $this->deactivateSubscribers($thresholdDate, $batchSize, $startId);
}
/**
* @param int $daysToInactive
* @param int $batchSize
* @return int
*/
public function markActiveSubscribers($daysToInactive, $batchSize) {
$thresholdDate = $this->getThresholdDate($daysToInactive);
return $this->activateSubscribers($thresholdDate, $batchSize);
}
/**
* @return void
*/
public function reactivateInactiveSubscribers() {
$reactivateAllInactiveQuery = sprintf(
"UPDATE %s SET status = '%s' WHERE status = '%s';",
Subscriber::$_table, Subscriber::STATUS_SUBSCRIBED, Subscriber::STATUS_INACTIVE
);
ORM::rawExecute($reactivateAllInactiveQuery);
}
/**
* @param int $daysToInactive
* @return Carbon
*/
private function getThresholdDate($daysToInactive) {
$now = new Carbon();
return $now->subDays($daysToInactive);
}
/**
* @param Carbon $thresholdDate
* @param int $batchSize
* @return int|boolean
*/
private function deactivateSubscribers(Carbon $thresholdDate, $batchSize, $startId = null) {
$subscribersTable = Subscriber::$_table;
$scheduledTasksTable = ScheduledTask::$_table;
$scheduledTaskSubcribresTable = ScheduledTaskSubscriber::$_table;
$statisticsOpensTable = StatisticsOpens::$_table;
$sendingQueuesTable = SendingQueue::$_table;
$thresholdDateIso = $thresholdDate->toDateTimeString();
$dayAgo = new Carbon();
$dayAgoIso = $dayAgo->subDay()->toDateTimeString();
// If MP2 migration occurred during detection interval we can't deactivate subscribers
// because they are imported with original subscription date but they were not present in a list for whole period
$mp2MigrationDate = $this->getMP2MigrationDate();
if ($mp2MigrationDate && $mp2MigrationDate > $thresholdDate) {
return false;
}
// We take into account only emails which have at least one opening tracked
// to ensure that tracking was enabled for the particular email
if (!$this->inactivesTaskIdsTableCreated) {
$inactivesTaskIdsTable = sprintf("
CREATE TEMPORARY TABLE IF NOT EXISTS inactives_task_ids
(INDEX task_id_ids (id))
SELECT DISTINCT task_id as id FROM $sendingQueuesTable as sq
JOIN $scheduledTasksTable as st ON sq.task_id = st.id
WHERE st.processed_at > '%s'
AND st.processed_at < '%s'
AND EXISTS (
SELECT 1
FROM $statisticsOpensTable as so
WHERE so.created_at > '%s'
AND so.newsletter_id = sq.newsletter_id
)",
$thresholdDateIso, $dayAgoIso, $thresholdDateIso
);
ORM::rawExecute($inactivesTaskIdsTable);
$this->inactivesTaskIdsTableCreated = true;
}
// Select subscribers who received a recent tracked email but didn't open it
$startId = (int)$startId;
$endId = $startId + $batchSize;
$inactiveSubscriberIdsTmpTable = 'inactive_subscriber_ids';
ORM::rawExecute("
CREATE TEMPORARY TABLE IF NOT EXISTS $inactiveSubscriberIdsTmpTable
(UNIQUE subscriber_id (id))
SELECT DISTINCT s.id FROM $subscribersTable as s
JOIN $scheduledTaskSubcribresTable as sts USE INDEX (subscriber_id) ON s.id = sts.subscriber_id
JOIN inactives_task_ids task_ids ON task_ids.id = sts.task_id
WHERE s.last_subscribed_at < ? AND s.status = ? AND s.id >= ? AND s.id < ?",
[$thresholdDateIso, Subscriber::STATUS_SUBSCRIBED, $startId, $endId]
);
$idsToDeactivate = ORM::forTable($inactiveSubscriberIdsTmpTable)->rawQuery("
SELECT s.id FROM $inactiveSubscriberIdsTmpTable s
LEFT OUTER JOIN $statisticsOpensTable as so ON s.id = so.subscriber_id AND so.created_at > ?
WHERE so.id IS NULL",
[$thresholdDateIso]
)->findArray();
ORM::rawExecute("DROP TABLE $inactiveSubscriberIdsTmpTable");
$idsToDeactivate = array_map(
function ($id) {
return (int)$id['id'];
},
$idsToDeactivate
);
if (!count($idsToDeactivate)) {
return 0;
}
ORM::rawExecute(sprintf(
"UPDATE %s SET status='" . Subscriber::STATUS_INACTIVE . "' WHERE id IN (%s);",
$subscribersTable,
implode(',', $idsToDeactivate)
));
return count($idsToDeactivate);
}
/**
* @param Carbon $thresholdDate
* @param int $batchSize
* @return int
*/
private function activateSubscribers(Carbon $thresholdDate, $batchSize) {
$subscribersTable = Subscriber::$_table;
$statsOpensTable = StatisticsOpens::$_table;
$mp2MigrationDate = $this->getMP2MigrationDate();
if ($mp2MigrationDate && $mp2MigrationDate > $thresholdDate) {
// If MP2 migration occurred during detection interval re-activate all subscribers created before migration
$idsToActivate = ORM::forTable($subscribersTable)->select("$subscribersTable.id")
->whereLt("$subscribersTable.created_at", $mp2MigrationDate)
->where("$subscribersTable.status", Subscriber::STATUS_INACTIVE)
->limit($batchSize)
->findArray();
} else {
$idsToActivate = ORM::forTable($subscribersTable)->select("$subscribersTable.id")
->leftOuterJoin($statsOpensTable, "$subscribersTable.id = $statsOpensTable.subscriber_id AND $statsOpensTable.created_at > '$thresholdDate'")
->whereLt("$subscribersTable.last_subscribed_at", $thresholdDate)
->where("$subscribersTable.status", Subscriber::STATUS_INACTIVE)
->whereRaw("$statsOpensTable.id IS NOT NULL")
->limit($batchSize)
->groupByExpr("$subscribersTable.id")
->findArray();
}
$idsToActivate = array_map(
function($id) {
return (int)$id['id'];
}, $idsToActivate
);
if (!count($idsToActivate)) {
return 0;
}
ORM::rawExecute(sprintf(
"UPDATE %s SET status='" . Subscriber::STATUS_SUBSCRIBED . "' WHERE id IN (%s);",
$subscribersTable,
implode(',', $idsToActivate)
));
return count($idsToActivate);
}
private function getMP2MigrationDate() {
$setting = $this->settingsRepository->findOneByName(MP2Migrator::MIGRATION_COMPLETE_SETTING_KEY);
return $setting ? Carbon::instance($setting->getCreatedAt()) : null;
}
}