It is possible that one email (e.g., purchase in category) is sent multiple times to the same subscriber. AutomationEmailScheduler::getScheduledTaskSubscriber was selecting the task based on subscriber and newsletter. In the case of multiple emails sent to one subscriber, the method failed to pick ScheduledTaskSubsrciberEntity because the query was fetching multiple results, but getOneOrNullResult expects only one result. This commit fixes it by adding additional filtering by $runId to get the ScheduledTaskSubsriberEntity associated with the correct run. I did the filtering in PHP because an alternative would be using LIKE %% in the query. The meta column is text. [MAILPOET-6155]
103 lines
3.8 KiB
PHP
103 lines
3.8 KiB
PHP
<?php declare(strict_types = 1);
|
|
|
|
namespace MailPoet\Newsletter\Scheduler;
|
|
|
|
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
|
use MailPoet\Entities\NewsletterEntity;
|
|
use MailPoet\Entities\ScheduledTaskEntity;
|
|
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
|
use MailPoet\Entities\SendingQueueEntity;
|
|
use MailPoet\Entities\SubscriberEntity;
|
|
use MailPoet\InvalidStateException;
|
|
use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
|
|
use MailPoet\WP\Functions as WPFunctions;
|
|
use MailPoetVendor\Carbon\Carbon;
|
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
|
|
|
class AutomationEmailScheduler {
|
|
/** @var EntityManager */
|
|
private $entityManager;
|
|
|
|
private ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository;
|
|
|
|
/** @var WPFunctions */
|
|
private $wp;
|
|
|
|
public function __construct(
|
|
EntityManager $entityManager,
|
|
ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository,
|
|
WPFunctions $wp
|
|
) {
|
|
$this->entityManager = $entityManager;
|
|
$this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
|
|
$this->wp = $wp;
|
|
}
|
|
|
|
public function createSendingTask(NewsletterEntity $email, SubscriberEntity $subscriber, array $meta): ScheduledTaskEntity {
|
|
if (!in_array($email->getType(), [NewsletterEntity::TYPE_AUTOMATION, NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL], true)) {
|
|
throw InvalidStateException::create()->withMessage(
|
|
// translators: %s is the type which was given.
|
|
sprintf(__("Email with type 'automation' or 'automation_transactional' expected, '%s' given.", 'mailpoet'), $email->getType())
|
|
);
|
|
}
|
|
|
|
$task = new ScheduledTaskEntity();
|
|
$task->setType(SendingQueue::TASK_TYPE);
|
|
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
|
$task->setScheduledAt(Carbon::createFromTimestamp($this->wp->currentTime('timestamp')));
|
|
$task->setPriority(ScheduledTaskEntity::PRIORITY_MEDIUM);
|
|
$task->setMeta($meta);
|
|
$this->entityManager->persist($task);
|
|
|
|
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriber);
|
|
$this->entityManager->persist($taskSubscriber);
|
|
|
|
$queue = new SendingQueueEntity();
|
|
$queue->setTask($task);
|
|
$queue->setMeta($meta);
|
|
$queue->setNewsletter($email);
|
|
$queue->setCountToProcess(1);
|
|
$queue->setCountTotal(1);
|
|
$this->entityManager->persist($queue);
|
|
|
|
$this->entityManager->flush();
|
|
return $task;
|
|
}
|
|
|
|
public function getScheduledTaskSubscriber(NewsletterEntity $email, SubscriberEntity $subscriber, int $runId): ?ScheduledTaskSubscriberEntity {
|
|
$results = $this->entityManager->createQueryBuilder()
|
|
->select('sts')
|
|
->from(ScheduledTaskSubscriberEntity::class, 'sts')
|
|
->join('sts.task', 'st')
|
|
->join('st.sendingQueue', 'sq')
|
|
->where('sq.newsletter = :newsletter')
|
|
->andWhere('sts.subscriber = :subscriber')
|
|
->setParameter('newsletter', $email)
|
|
->setParameter('subscriber', $subscriber)
|
|
->getQuery()
|
|
->getResult();
|
|
$result = null;
|
|
foreach ($results as $scheduledTaskSubscriber) {
|
|
$task = $scheduledTaskSubscriber->getTask();
|
|
if (!$task instanceof ScheduledTaskEntity) {
|
|
continue;
|
|
}
|
|
$meta = $task->getMeta();
|
|
if (($meta['automation']['run_id'] ?? null) === $runId) {
|
|
$result = $scheduledTaskSubscriber;
|
|
break;
|
|
}
|
|
}
|
|
return $result instanceof ScheduledTaskSubscriberEntity ? $result : null;
|
|
}
|
|
|
|
public function saveError(ScheduledTaskSubscriberEntity $scheduledTaskSubscriber, string $error): void {
|
|
$task = $scheduledTaskSubscriber->getTask();
|
|
$subscriber = $scheduledTaskSubscriber->getSubscriber();
|
|
if (!$task || !$subscriber || !$subscriber->getId()) {
|
|
return;
|
|
}
|
|
$this->scheduledTaskSubscribersRepository->saveError($task, $subscriber->getId(), $error);
|
|
}
|
|
}
|