oont-contents/plugins/mailpoet/lib/Newsletter/Listing/NewsletterListingRepository.php
2025-02-08 15:10:23 +01:00

284 lines
8.9 KiB
PHP

<?php declare(strict_types = 1);
namespace MailPoet\Newsletter\Listing;
if (!defined('ABSPATH')) exit;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Listing\ListingDefinition;
use MailPoet\Listing\ListingRepository;
use MailPoet\Util\Helpers;
use MailPoetVendor\Doctrine\ORM\QueryBuilder;
class NewsletterListingRepository extends ListingRepository {
private static $supportedStatuses = [
NewsletterEntity::STATUS_DRAFT,
NewsletterEntity::STATUS_SCHEDULED,
NewsletterEntity::STATUS_SENDING,
NewsletterEntity::STATUS_SENT,
NewsletterEntity::STATUS_ACTIVE,
];
private static $supportedTypes = [
NewsletterEntity::TYPE_STANDARD,
NewsletterEntity::TYPE_RE_ENGAGEMENT,
NewsletterEntity::TYPE_WELCOME,
NewsletterEntity::TYPE_AUTOMATIC,
NewsletterEntity::TYPE_NOTIFICATION,
NewsletterEntity::TYPE_NOTIFICATION_HISTORY,
];
public function getFilters(ListingDefinition $definition): array {
$group = $definition->getGroup();
$typeParam = $definition->getParameters()['type'] ?? null;
$groupParam = $definition->getParameters()['group'] ?? null;
// newsletter types without filters
if (in_array($typeParam, [NewsletterEntity::TYPE_NOTIFICATION_HISTORY])) {
return [];
}
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
if ($group) {
$this->applyGroup($queryBuilder, $group);
}
if ($typeParam) {
$this->applyType($queryBuilder, $typeParam, $groupParam);
}
$queryBuilder
->select('s.id, s.name, COUNT(n) AS newsletterCount')
->join('n.newsletterSegments', 'ns')
->join('ns.segment', 's')
->groupBy('s.id')
->addGroupBy('s.name')
->orderBy('s.name')
->having('COUNT(n) > 0');
// format segment list
$segmentList = [
[
'label' => __('All Lists', 'mailpoet'),
'value' => '',
],
];
foreach ($queryBuilder->getQuery()->getResult() as $item) {
$segmentList[] = [
'label' => sprintf('%s (%d)', $item['name'], $item['newsletterCount']),
'value' => $item['id'],
];
}
return ['segment' => $segmentList];
}
public function getGroups(ListingDefinition $definition): array {
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
$this->applyParameters($queryBuilder, $definition->getParameters());
// total count
$countQueryBuilder = clone $queryBuilder;
$countQueryBuilder->select('COUNT(n) AS newsletterCount');
$countQueryBuilder->andWhere('n.deletedAt IS NULL');
$totalCount = (int)$countQueryBuilder->getQuery()->getSingleScalarResult();
// trashed count
$trashedCountQueryBuilder = clone $queryBuilder;
$trashedCountQueryBuilder->select('COUNT(n) AS newsletterCount');
$trashedCountQueryBuilder->andWhere('n.deletedAt IS NOT NULL');
$trashedCount = (int)$trashedCountQueryBuilder->getQuery()->getSingleScalarResult();
// count-by-status query
$queryBuilder->select('n.status, COUNT(n) AS newsletterCount');
$queryBuilder->andWhere('n.deletedAt IS NULL');
$queryBuilder->groupBy('n.status');
$map = [];
foreach ($queryBuilder->getQuery()->getResult() as $item) {
$map[$item['status']] = (int)$item['newsletterCount'];
}
$groups = [
[
'name' => 'all',
'label' => __('All', 'mailpoet'),
'count' => $totalCount,
],
];
$type = $definition->getParameters()['type'] ?? null;
switch ($type) {
case NewsletterEntity::TYPE_STANDARD:
$groups = array_merge($groups, [
[
'name' => NewsletterEntity::STATUS_DRAFT,
'label' => __('Draft', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_DRAFT] ?? 0,
],
[
'name' => NewsletterEntity::STATUS_SCHEDULED,
'label' => __('Scheduled', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_SCHEDULED] ?? 0,
],
[
'name' => NewsletterEntity::STATUS_SENDING,
'label' => __('Sending', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_SENDING] ?? 0,
],
[
'name' => NewsletterEntity::STATUS_SENT,
'label' => __('Sent', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_SENT] ?? 0,
],
]);
break;
case NewsletterEntity::TYPE_NOTIFICATION_HISTORY:
$groups = array_merge($groups, [
[
'name' => NewsletterEntity::STATUS_SENDING,
'label' => __('Sending', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_SENDING] ?? 0,
],
[
'name' => NewsletterEntity::STATUS_SENT,
'label' => __('Sent', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_SENT] ?? 0,
],
]);
break;
case NewsletterEntity::TYPE_WELCOME:
case NewsletterEntity::TYPE_RE_ENGAGEMENT:
case NewsletterEntity::TYPE_NOTIFICATION:
case NewsletterEntity::TYPE_AUTOMATIC:
$groups = array_merge($groups, [
[
'name' => NewsletterEntity::STATUS_ACTIVE,
'label' => __('Active', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_ACTIVE] ?? 0,
],
[
'name' => NewsletterEntity::STATUS_DRAFT,
'label' => __('Not active', 'mailpoet'),
'count' => $map[NewsletterEntity::STATUS_DRAFT] ?? 0,
],
]);
break;
}
$groups[] = [
'name' => 'trash',
'label' => __('Trash', 'mailpoet'),
'count' => $trashedCount,
];
return $groups;
}
protected function applySelectClause(QueryBuilder $queryBuilder) {
$queryBuilder->select("PARTIAL n.{id,subject,hash,type,status,sentAt,updatedAt,deletedAt}, PARTIAL wpPost.{id,postTitle}");
}
protected function applyFromClause(QueryBuilder $queryBuilder) {
$queryBuilder->from(NewsletterEntity::class, 'n')
->leftJoin('n.wpPost', 'wpPost');
}
protected function applyGroup(QueryBuilder $queryBuilder, string $group) {
// include/exclude deleted
if ($group === 'trash') {
$queryBuilder->andWhere('n.deletedAt IS NOT NULL');
} else {
$queryBuilder->andWhere('n.deletedAt IS NULL');
}
if (!in_array($group, self::$supportedStatuses)) {
return;
}
$queryBuilder
->andWhere('n.status = :status')
->setParameter('status', $group);
}
protected function applySearch(QueryBuilder $queryBuilder, string $search, array $parameters = []) {
$search = Helpers::escapeSearch($search);
$type = $parameters['type'] ?? null;
if ($type && $type === NewsletterEntity::TYPE_NOTIFICATION_HISTORY) {
$queryBuilder
->leftJoin('n.queues', 'sq')
->andWhere('sq.newsletterRenderedSubject LIKE :search or n.subject LIKE :search')
->setParameter('search', "%$search%");
} else {
$queryBuilder
->andWhere('n.subject LIKE :search')
->setParameter('search', "%$search%");
}
}
protected function applyFilters(QueryBuilder $queryBuilder, array $filters) {
$segmentId = $filters['segment'] ?? null;
if ($segmentId) {
$queryBuilder
->join('n.newsletterSegments', 'ns')
->andWhere('ns.segment = :segmentId')
->setParameter('segmentId', $segmentId);
}
}
protected function applyParameters(QueryBuilder $queryBuilder, array $parameters) {
$type = $parameters['type'] ?? null;
$group = $parameters['group'] ?? null;
$parentId = $parameters['parentId'] ?? null;
if ($type) {
$this->applyType($queryBuilder, $type, $group);
}
if ($parentId) {
$queryBuilder
->andWhere('n.parent = :parentId')
->setParameter('parentId', $parentId);
}
}
protected function applySorting(QueryBuilder $queryBuilder, string $sortBy, string $sortOrder) {
if ($sortBy === 'name') {
$queryBuilder->addSelect('CONCAT(COALESCE(wpPost.postTitle, \'\'), n.subject) AS HIDDEN sortingName');
$queryBuilder->addOrderBy("sortingName", $sortOrder);
return;
}
if ($sortBy === 'sentAt') {
$queryBuilder->addSelect('CASE WHEN n.sentAt IS NULL THEN 1 ELSE 0 END AS HIDDEN sentAtIsNull');
$queryBuilder->addOrderBy('sentAtIsNull', 'DESC');
}
$queryBuilder->addOrderBy("n.$sortBy", $sortOrder);
}
private function applyType(QueryBuilder $queryBuilder, string $type, string $group = null) {
if (!in_array($type, self::$supportedTypes)) {
return;
}
if ($type === NewsletterEntity::TYPE_AUTOMATIC && $group) {
$queryBuilder
->join('n.options', 'o')
->join('o.optionField', 'opf')
->andWhere('o.value = :group')
->setParameter('group', $group)
->andWhere('opf.newsletterType = n.type');
} else {
$queryBuilder
->andWhere('n.type = :type')
->setParameter('type', $type);
}
}
}