| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- <?php
- namespace MailPoet\API\JSON\ResponseBuilders;
- if (!defined('ABSPATH')) exit;
- use MailPoet\Entities\NewsletterEntity;
- use MailPoet\Entities\SegmentEntity;
- use MailPoet\Entities\SendingQueueEntity;
- use MailPoet\Models\SendingQueue;
- use MailPoet\Newsletter\NewslettersRepository;
- use MailPoet\Newsletter\Statistics\NewsletterStatistics;
- use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
- use MailPoet\Newsletter\Url as NewsletterUrl;
- use MailPoetVendor\Doctrine\ORM\EntityManager;
- class NewslettersResponseBuilder {
- const DATE_FORMAT = 'Y-m-d H:i:s';
- const RELATION_QUEUE = 'queue';
- const RELATION_SEGMENTS = 'segments';
- const RELATION_OPTIONS = 'options';
- const RELATION_TOTAL_SENT = 'total_sent';
- const RELATION_CHILDREN_COUNT = 'children_count';
- const RELATION_SCHEDULED = 'scheduled';
- const RELATION_STATISTICS = 'statistics';
- /** @var NewsletterStatisticsRepository */
- private $newslettersStatsRepository;
- /** @var NewslettersRepository */
- private $newslettersRepository;
- /** @var EntityManager */
- private $entityManager;
- /** @var NewsletterUrl */
- private $newsletterUrl;
- public function __construct(
- EntityManager $entityManager,
- NewslettersRepository $newslettersRepository,
- NewsletterStatisticsRepository $newslettersStatsRepository,
- NewsletterUrl $newsletterUrl
- ) {
- $this->newslettersStatsRepository = $newslettersStatsRepository;
- $this->newslettersRepository = $newslettersRepository;
- $this->entityManager = $entityManager;
- $this->newsletterUrl = $newsletterUrl;
- }
- public function build(NewsletterEntity $newsletter, $relations = []) {
- $data = [
- 'id' => (string)$newsletter->getId(), // (string) for BC
- 'hash' => $newsletter->getHash(),
- 'subject' => $newsletter->getSubject(),
- 'type' => $newsletter->getType(),
- 'sender_address' => $newsletter->getSenderAddress(),
- 'sender_name' => $newsletter->getSenderName(),
- 'status' => $newsletter->getStatus(),
- 'reply_to_address' => $newsletter->getReplyToAddress(),
- 'reply_to_name' => $newsletter->getReplyToName(),
- 'preheader' => $newsletter->getPreheader(),
- 'body' => $newsletter->getBody(),
- 'sent_at' => ($sentAt = $newsletter->getSentAt()) ? $sentAt->format(self::DATE_FORMAT) : null,
- 'created_at' => $newsletter->getCreatedAt()->format(self::DATE_FORMAT),
- 'updated_at' => $newsletter->getUpdatedAt()->format(self::DATE_FORMAT),
- 'deleted_at' => ($deletedAt = $newsletter->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
- 'parent_id' => ($parent = $newsletter->getParent()) ? $parent->getId() : null,
- 'unsubscribe_token' => $newsletter->getUnsubscribeToken(),
- 'ga_campaign' => $newsletter->getGaCampaign(),
- ];
- foreach ($relations as $relation) {
- if ($relation === self::RELATION_QUEUE) {
- $data['queue'] = ($queue = $newsletter->getLatestQueue()) ? $this->buildQueue($queue) : false; // false for BC
- }
- if ($relation === self::RELATION_SEGMENTS) {
- $data['segments'] = $this->buildSegments($newsletter);
- }
- if ($relation === self::RELATION_OPTIONS) {
- $data['options'] = $this->buildOptions($newsletter);
- }
- if ($relation === self::RELATION_TOTAL_SENT) {
- $data['total_sent'] = $this->newslettersStatsRepository->getTotalSentCount($newsletter);
- }
- if ($relation === self::RELATION_CHILDREN_COUNT) {
- $data['children_count'] = $this->newslettersStatsRepository->getChildrenCount($newsletter);
- }
- if ($relation === self::RELATION_SCHEDULED) {
- $data['total_scheduled'] = (int)SendingQueue::findTaskByNewsletterId($newsletter->getId())
- ->where('tasks.status', SendingQueue::STATUS_SCHEDULED)
- ->count();
- }
- if ($relation === self::RELATION_STATISTICS) {
- $data['statistics'] = $this->newslettersStatsRepository->getStatistics($newsletter)->asArray();
- }
- }
- return $data;
- }
- /**
- * @param NewsletterEntity[] $newsletters
- * @return mixed[]
- */
- public function buildForListing(array $newsletters): array {
- $statistics = $this->newslettersStatsRepository->getBatchStatistics($newsletters);
- $latestQueues = $this->getBatchLatestQueuesWithTasks($newsletters);
- $this->newslettersRepository->prefetchOptions($newsletters);
- $this->newslettersRepository->prefetchSegments($newsletters);
- $data = [];
- foreach ($newsletters as $newsletter) {
- $id = $newsletter->getId();
- $data[] = $this->buildListingItem($newsletter, $statistics[$id] ?? null, $latestQueues[$id] ?? null);
- }
- return $data;
- }
- private function buildListingItem(NewsletterEntity $newsletter, NewsletterStatistics $statistics = null, SendingQueueEntity $latestQueue = null): array {
- $data = [
- 'id' => (string)$newsletter->getId(), // (string) for BC
- 'hash' => $newsletter->getHash(),
- 'subject' => $newsletter->getSubject(),
- 'type' => $newsletter->getType(),
- 'status' => $newsletter->getStatus(),
- 'sent_at' => ($sentAt = $newsletter->getSentAt()) ? $sentAt->format(self::DATE_FORMAT) : null,
- 'updated_at' => $newsletter->getUpdatedAt()->format(self::DATE_FORMAT),
- 'deleted_at' => ($deletedAt = $newsletter->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
- 'segments' => [],
- 'queue' => false,
- 'statistics' => ($statistics && $newsletter->getType() !== NewsletterEntity::TYPE_NOTIFICATION)
- ? $statistics->asArray()
- : false,
- 'preview_url' => $this->newsletterUrl->getViewInBrowserUrl(
- (object)[
- 'id' => $newsletter->getId(),
- 'hash' => $newsletter->getHash(),
- ],
- null,
- in_array($newsletter->getStatus(), [NewsletterEntity::STATUS_SENT, NewsletterEntity::STATUS_SENDING], true)
- ? $latestQueue
- : false
- ),
- ];
- if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD) {
- $data['segments'] = $this->buildSegments($newsletter);
- $data['queue'] = $latestQueue ? $this->buildQueue($latestQueue) : false; // false for BC
- } elseif (in_array($newsletter->getType(), [NewsletterEntity::TYPE_WELCOME, NewsletterEntity::TYPE_AUTOMATIC], true)) {
- $data['segments'] = [];
- $data['options'] = $this->buildOptions($newsletter);
- $data['total_sent'] = $statistics ? $statistics->getTotalSentCount() : 0;
- $data['total_scheduled'] = (int)SendingQueue::findTaskByNewsletterId($newsletter->getId())
- ->where('tasks.status', SendingQueue::STATUS_SCHEDULED)
- ->count();
- } elseif ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION) {
- $data['segments'] = $this->buildSegments($newsletter);
- $data['children_count'] = $this->newslettersStatsRepository->getChildrenCount($newsletter);
- $data['options'] = $this->buildOptions($newsletter);
- } elseif ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION_HISTORY) {
- $data['segments'] = $this->buildSegments($newsletter);
- $data['queue'] = $latestQueue ? $this->buildQueue($latestQueue) : false; // false for BC
- } elseif ($newsletter->getType() === NewsletterEntity::TYPE_RE_ENGAGEMENT) {
- $data['segments'] = $this->buildSegments($newsletter);
- $data['options'] = $this->buildOptions($newsletter);
- $data['total_sent'] = $statistics ? $statistics->getTotalSentCount() : 0;
- $data['total_scheduled'] = SendingQueue::findTaskByNewsletterId($newsletter->getId())
- ->where('tasks.status', SendingQueue::STATUS_SCHEDULED)
- ->count();
- }
- return $data;
- }
- private function buildSegments(NewsletterEntity $newsletter) {
- $output = [];
- foreach ($newsletter->getNewsletterSegments() as $newsletterSegment) {
- $segment = $newsletterSegment->getSegment();
- if (!$segment || $segment->getDeletedAt()) {
- continue;
- }
- $output[] = $this->buildSegment($segment);
- }
- return $output;
- }
- private function buildOptions(NewsletterEntity $newsletter) {
- $output = [];
- foreach ($newsletter->getOptions() as $option) {
- $optionField = $option->getOptionField();
- if (!$optionField) {
- continue;
- }
- $output[$optionField->getName()] = $option->getValue();
- }
- // convert 'afterTimeNumber' string to integer
- if (isset($output['afterTimeNumber']) && is_numeric($output['afterTimeNumber'])) {
- $output['afterTimeNumber'] = (int)$output['afterTimeNumber'];
- }
- return $output;
- }
- private function buildSegment(SegmentEntity $segment) {
- return [
- 'id' => (string)$segment->getId(), // (string) for BC
- 'name' => $segment->getName(),
- 'type' => $segment->getType(),
- 'description' => $segment->getDescription(),
- 'created_at' => $segment->getCreatedAt()->format(self::DATE_FORMAT),
- 'updated_at' => $segment->getUpdatedAt()->format(self::DATE_FORMAT),
- 'deleted_at' => ($deletedAt = $segment->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
- ];
- }
- private function buildQueue(SendingQueueEntity $queue) {
- $task = $queue->getTask();
- if ($task === null) {
- return null;
- }
- // the following crazy mix of '$queue' and '$task' comes from 'array_merge($task, $queue)'
- // (MailPoet\Tasks\Sending) which means all equal-named fields will be taken from '$queue'
- return [
- 'id' => (string)$queue->getId(), // (string) for BC
- 'type' => $task->getType(),
- 'status' => $task->getStatus(),
- 'priority' => (string)$task->getPriority(), // (string) for BC
- 'scheduled_at' => ($scheduledAt = $task->getScheduledAt()) ? $scheduledAt->format(self::DATE_FORMAT) : null,
- 'processed_at' => ($processedAt = $task->getProcessedAt()) ? $processedAt->format(self::DATE_FORMAT) : null,
- 'created_at' => $queue->getCreatedAt()->format(self::DATE_FORMAT),
- 'updated_at' => $queue->getUpdatedAt()->format(self::DATE_FORMAT),
- 'deleted_at' => ($deletedAt = $queue->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
- 'meta' => $queue->getMeta(),
- 'task_id' => (string)$task->getId(), // (string) for BC
- 'newsletter_id' => ($newsletter = $queue->getNewsletter()) ? (string)$newsletter->getId() : null, // (string) for BC
- 'newsletter_rendered_subject' => $queue->getNewsletterRenderedSubject(),
- 'count_total' => (string)$queue->getCountTotal(), // (string) for BC
- 'count_processed' => (string)$queue->getCountProcessed(), // (string) for BC
- 'count_to_process' => (string)$queue->getCountToProcess(), // (string) for BC
- ];
- }
- private function getBatchLatestQueuesWithTasks(array $newsletters): array {
- // this implements the same logic as NewsletterEntity::getLatestQueue() but for a batch of $newsletters
- $subqueryQueryBuilder = $this->entityManager->createQueryBuilder();
- $subquery = $subqueryQueryBuilder
- ->select('MAX(subSq.id) AS maxId')
- ->from(SendingQueueEntity::class, 'subSq')
- ->where('subSq.newsletter IN (:newsletters)')
- ->setParameter('newsletters', $newsletters)
- ->groupBy('subSq.newsletter')
- ->getQuery();
- $latestQueueIds = array_column($subquery->getResult(), 'maxId');
- if (empty($latestQueueIds)) {
- return [];
- }
- $queryBuilder = $this->entityManager->createQueryBuilder();
- $results = $queryBuilder
- ->select('PARTIAL sq.{id, createdAt, updatedAt, deletedAt, meta, newsletterRenderedSubject, countTotal, countProcessed, countToProcess}')
- ->addSelect('PARTIAL t.{id, type, status, priority, scheduledAt, processedAt}')
- ->addSelect('IDENTITY(sq.newsletter)')
- ->from(SendingQueueEntity::class, 'sq')
- ->join('sq.task', 't')
- ->where('sq.id IN (:sub)')
- ->setParameter('sub', $latestQueueIds)
- ->getQuery()
- ->getResult();
- $latestQueues = [];
- foreach ($results as $result) {
- $latestQueues[(int)$result[1]] = $result[0];
- }
- return $latestQueues;
- }
- }
|