Skip to content

Commit 589d85f

Browse files
committed
PaginatedResult
1 parent 97ad3ba commit 589d85f

24 files changed

+318
-131
lines changed

src/Domain/Analytics/Service/AnalyticsService.php

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,14 @@
1313

1414
class AnalyticsService
1515
{
16-
private LinkTrackManager $linkTrackManager;
17-
private UserMessageViewManager $userMessageViewManager;
18-
private MessageRepository $messageRepository;
19-
private UserMessageBounceRepository $messageBounceRepository;
20-
private UserMessageForwardRepository $messageForwardRepository;
21-
private SubscriberRepository $subscriberRepository;
22-
2316
public function __construct(
24-
LinkTrackManager $linkTrackManager,
25-
UserMessageViewManager $userMessageViewManager,
26-
MessageRepository $messageRepository,
27-
UserMessageBounceRepository $messageBounceRepository,
28-
UserMessageForwardRepository $messageForwardRepository,
29-
SubscriberRepository $subscriberRepository
17+
private readonly LinkTrackManager $linkTrackManager,
18+
private readonly UserMessageViewManager $userMessageViewManager,
19+
private readonly MessageRepository $messageRepository,
20+
private readonly UserMessageBounceRepository $messageBounceRepository,
21+
private readonly UserMessageForwardRepository $messageForwardRepository,
22+
private readonly SubscriberRepository $subscriberRepository
3023
) {
31-
$this->linkTrackManager = $linkTrackManager;
32-
$this->userMessageViewManager = $userMessageViewManager;
33-
$this->messageRepository = $messageRepository;
34-
$this->messageBounceRepository = $messageBounceRepository;
35-
$this->messageForwardRepository = $messageForwardRepository;
36-
$this->subscriberRepository = $subscriberRepository;
3724
}
3825

3926
/**
@@ -55,10 +42,9 @@ public function __construct(
5542
*/
5643
public function getCampaignStatistics(int $limit = 50, int $lastId = 0): array
5744
{
58-
$messages = $this->messageRepository->getFilteredAfterId($lastId, $limit);
45+
$messages = $this->messageRepository->getFilteredAfterId($lastId, $limit)->getItems();
5946

6047
$campaignStats = [];
61-
6248
foreach ($messages as $message) {
6349
$views = $this->userMessageViewManager->countViewsByMessageId($message->getId());
6450
$linkTracks = $this->linkTrackManager->getLinkTracksByMessageId($message->getId());
@@ -113,11 +99,10 @@ public function getCampaignStatistics(int $limit = 50, int $lastId = 0): array
11399
*/
114100
public function getViewOpensStatistics(int $limit = 50, int $lastId = 0): array
115101
{
116-
$messages = $this->messageRepository->getFilteredAfterId($lastId, $limit);
102+
$messagesResult = $this->messageRepository->getFilteredAfterId($lastId, $limit);
117103

118104
$viewStats = [];
119-
120-
foreach ($messages as $message) {
105+
foreach ($messagesResult->getItems() as $message) {
121106
$views = $this->userMessageViewManager->countViewsByMessageId($message->getId());
122107
$sentCount = $message->getMetadata()->getBounceCount() + $views;
123108

@@ -134,9 +119,9 @@ public function getViewOpensStatistics(int $limit = 50, int $lastId = 0): array
134119

135120
return [
136121
'campaigns' => $viewStats,
137-
'total' => count($viewStats),
138-
'hasMore' => count($messages) === $limit,
139-
'lastId' => count($messages) > 0 ? $messages[count($messages) - 1]->getId() : $lastId,
122+
'total' => $messagesResult->getTotal(),
123+
'hasMore' => count($messagesResult->getItems()) === $limit,
124+
'lastId' => $messagesResult->getLastId(),
140125
];
141126
}
142127

@@ -153,10 +138,9 @@ public function getViewOpensStatistics(int $limit = 50, int $lastId = 0): array
153138
*/
154139
public function getTopDomains(int $limit = 50, int $minSubscribers = 5): array
155140
{
156-
$domains = [];
157-
158141
$subscribers = $this->subscriberRepository->findAll();
159142

143+
$domains = [];
160144
foreach ($subscribers as $subscriber) {
161145
$email = $subscriber->getEmail();
162146
$domain = substr(strrchr($email, '@'), 1) ?: '';
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Common\Model;
6+
7+
use PhpList\Core\Domain\Common\Model\Interfaces\DomainModel;
8+
9+
/** @template T of DomainModel */
10+
class PaginatedResult
11+
{
12+
/** @var list<T> */
13+
private array $items;
14+
private int $total;
15+
private int $limit;
16+
private int $lastId;
17+
18+
/** @param list<T> $items */
19+
public function __construct(
20+
array $items,
21+
int $total,
22+
int $limit,
23+
int $lastId,
24+
) {
25+
$this->items = $items;
26+
$this->total = $total;
27+
$this->limit = $limit;
28+
$this->lastId = $lastId;
29+
}
30+
31+
/** @return list<T> */
32+
public function getItems(): array
33+
{
34+
return $this->items;
35+
}
36+
37+
public function getTotal(): int
38+
{
39+
return $this->total;
40+
}
41+
42+
public function getLimit(): int
43+
{
44+
return $this->limit;
45+
}
46+
47+
public function getLastId(): int
48+
{
49+
return $this->lastId;
50+
}
51+
}

src/Domain/Common/Repository/CursorPaginationTrait.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,50 @@
66

77
use BadMethodCallException;
88
use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface;
9-
use PhpList\Core\Domain\Common\Model\Interfaces\DomainModel;
9+
use PhpList\Core\Domain\Common\Model\PaginatedResult;
1010

1111
trait CursorPaginationTrait
1212
{
1313
/**
1414
* @param int $lastId Last seen ID
1515
* @param int $limit Max results
16-
* @return array
1716
*/
18-
public function getAfterId(int $lastId, int $limit): array
17+
public function getAfterId(int $lastId, int $limit): PaginatedResult
1918
{
20-
return $this->createQueryBuilder('e')
19+
$queryBuilder = $this->createQueryBuilder('e');
20+
21+
$countQb = clone $queryBuilder;
22+
$total = (int) $countQb
23+
->select('COUNT(DISTINCT e.id)')
24+
->getQuery()
25+
->getSingleScalarResult();
26+
27+
$items = $queryBuilder
2128
->andWhere('e.id > :lastId')
2229
->setParameter('lastId', $lastId)
2330
->orderBy('e.id', 'ASC')
2431
->setMaxResults($limit)
2532
->getQuery()
2633
->getResult();
34+
35+
return new PaginatedResult(
36+
items: $items,
37+
total: $total,
38+
limit: $limit,
39+
lastId: $lastId,
40+
);
2741
}
2842

2943
/**
3044
* Get filtered + paginated messages for a given owner and status.
3145
*
32-
* @return DomainModel[]
3346
* @throws BadMethodCallException
3447
* */
35-
public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterface $filter = null): array
36-
{
48+
public function getFilteredAfterId(
49+
int $lastId,
50+
int $limit,
51+
?FilterRequestInterface $filter = null
52+
): PaginatedResult {
3753
if ($filter === null) {
3854
return $this->getAfterId($lastId, $limit);
3955
}

src/Domain/Common/Repository/Interfaces/PaginatableRepositoryInterface.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
namespace PhpList\Core\Domain\Common\Repository\Interfaces;
66

77
use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface;
8+
use PhpList\Core\Domain\Common\Model\PaginatedResult;
89

910
interface PaginatableRepositoryInterface
1011
{
11-
public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterface $filter = null): array;
12-
public function count(): int;
12+
public function getFilteredAfterId(
13+
int $lastId,
14+
int $limit,
15+
?FilterRequestInterface $filter = null
16+
): PaginatedResult;
1317
}

src/Domain/Configuration/Repository/EventLogRepository.php

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use InvalidArgumentException;
88
use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface;
9+
use PhpList\Core\Domain\Common\Model\PaginatedResult;
910
use PhpList\Core\Domain\Common\Repository\AbstractRepository;
1011
use PhpList\Core\Domain\Common\Repository\CursorPaginationTrait;
1112
use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface;
@@ -17,16 +18,15 @@ class EventLogRepository extends AbstractRepository implements PaginatableReposi
1718
use CursorPaginationTrait;
1819

1920
/**
20-
* @return EventLog[]
21+
* @return PaginatedResult<EventLog>
2122
* @throws InvalidArgumentException
2223
*/
23-
public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterface $filter = null): array
24-
{
25-
$queryBuilder = $this->createQueryBuilder('e')
26-
->andWhere('e.id > :lastId')
27-
->setParameter('lastId', $lastId)
28-
->orderBy('e.id', 'ASC')
29-
->setMaxResults($limit);
24+
public function getFilteredAfterId(
25+
int $lastId,
26+
int $limit,
27+
?FilterRequestInterface $filter = null
28+
): PaginatedResult {
29+
$queryBuilder = $this->createQueryBuilder('e');
3030

3131
if ($filter === null) {
3232
return $queryBuilder->getQuery()->getResult();
@@ -46,6 +46,26 @@ public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterf
4646
$queryBuilder->andWhere('e.entered <= :dateTo')->setParameter('dateTo', $filter->getDateTo());
4747
}
4848

49-
return $queryBuilder->getQuery()->getResult();
49+
$countQb = clone $queryBuilder;
50+
$total = (int) $countQb
51+
->select('COUNT(DISTINCT e.id)')
52+
->getQuery()
53+
->getSingleScalarResult();
54+
55+
/** @var list<EventLog> $items */
56+
$items = $queryBuilder
57+
->andWhere('e.id > :lastId')
58+
->setParameter('lastId', $lastId)
59+
->orderBy('e.id', 'ASC')
60+
->setMaxResults($limit)
61+
->getQuery()
62+
->getResult();
63+
64+
return new PaginatedResult(
65+
items: $items,
66+
total: $total,
67+
limit: $limit,
68+
lastId: $lastId,
69+
);
5070
}
5171
}

src/Domain/Configuration/Service/Manager/EventLogManager.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function get(
4545
): array {
4646
$filter = new EventLogFilter($page, $dateFrom, $dateTo);
4747

48-
return $this->repository->getFilteredAfterId($lastId, $limit, $filter);
48+
return $this->repository->getFilteredAfterId($lastId, $limit, $filter)->getItems();
4949
}
5050

5151
public function delete(EventLog $log): void

src/Domain/Identity/Repository/AdminAttributeValueRepository.php

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
use InvalidArgumentException;
88
use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface;
9+
use PhpList\Core\Domain\Common\Model\PaginatedResult;
910
use PhpList\Core\Domain\Common\Repository\AbstractRepository;
1011
use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface;
1112
use PhpList\Core\Domain\Identity\Model\AdminAttributeValue;
1213
use PhpList\Core\Domain\Identity\Model\Filter\AdminAttributeValueFilter;
14+
use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeValue;
1315

1416
class AdminAttributeValueRepository extends AbstractRepository implements PaginatableRepositoryInterface
1517
{
@@ -26,29 +28,47 @@ public function findOneByAdminIdAndAttributeId(int $adminId, int $definitionId):
2628
->getOneOrNullResult();
2729
}
2830

29-
3031
/**
31-
* @return AdminAttributeValue[]
32+
* @return PaginatedResult<SubscriberAttributeValue>
3233
* @throws InvalidArgumentException
3334
*/
34-
public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterface $filter = null): array
35-
{
35+
public function getFilteredAfterId(
36+
int $lastId,
37+
int $limit,
38+
?FilterRequestInterface $filter = null
39+
): PaginatedResult {
3640
if (!$filter instanceof AdminAttributeValueFilter) {
3741
throw new InvalidArgumentException('Expected AdminAttributeValueFilter.');
3842
}
39-
$query = $this->createQueryBuilder('aav')
43+
$queryBuilder = $this->createQueryBuilder('aav')
4044
->join('aav.administrator', 'a')
41-
->join('aav.attributeDefinition', 'ad')
42-
->where('ad.id > :lastId')
43-
->setParameter('lastId', $lastId);
45+
->join('aav.attributeDefinition', 'ad');
4446

4547
if ($filter->getAdminId() !== null) {
46-
$query->andWhere('a.id = :adminId')
48+
$queryBuilder->andWhere('a.id = :adminId')
4749
->setParameter('adminId', $filter->getAdminId());
4850
}
49-
return $query->orderBy('ad.id', 'ASC')
51+
52+
$countQb = clone $queryBuilder;
53+
$total = (int) $countQb
54+
->select('COUNT(DISTINCT ad.id)')
55+
->getQuery()
56+
->getSingleScalarResult();
57+
58+
/** @var list<SubscriberAttributeValue> $items */
59+
$items = $queryBuilder
60+
->andWhere('ad.id > :lastId')
61+
->setParameter('lastId', $lastId)
62+
->orderBy('ad.id', 'ASC')
5063
->setMaxResults($limit)
5164
->getQuery()
5265
->getResult();
66+
67+
return new PaginatedResult(
68+
items: $items,
69+
total: $total,
70+
limit: $limit,
71+
lastId: $lastId,
72+
);
5373
}
5474
}

src/Domain/Identity/Service/Manager/AdminAttributeDefinitionManager.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpList\Core\Domain\Identity\Service\Manager;
66

7+
use PhpList\Core\Domain\Common\Model\PaginatedResult;
78
use PhpList\Core\Domain\Identity\Exception\AttributeDefinitionCreationException;
89
use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition;
910
use PhpList\Core\Domain\Identity\Model\Dto\AdminAttributeDefinitionDto;
@@ -13,18 +14,11 @@
1314

1415
class AdminAttributeDefinitionManager
1516
{
16-
private AdminAttributeDefinitionRepository $definitionRepository;
17-
private AttributeTypeValidator $attributeTypeValidator;
18-
private TranslatorInterface $translator;
19-
2017
public function __construct(
21-
AdminAttributeDefinitionRepository $definitionRepository,
22-
AttributeTypeValidator $attributeTypeValidator,
23-
TranslatorInterface $translator
18+
private readonly AdminAttributeDefinitionRepository $definitionRepository,
19+
private readonly AttributeTypeValidator $attributeTypeValidator,
20+
private readonly TranslatorInterface $translator
2421
) {
25-
$this->definitionRepository = $definitionRepository;
26-
$this->attributeTypeValidator = $attributeTypeValidator;
27-
$this->translator = $translator;
2822
}
2923

3024
public function create(AdminAttributeDefinitionDto $attributeDefinitionDto): AdminAttributeDefinition
@@ -79,7 +73,7 @@ public function getTotalCount(): int
7973
return $this->definitionRepository->count();
8074
}
8175

82-
public function getAttributesAfterId(int $afterId, int $limit): array
76+
public function getAttributesAfterId(int $afterId, int $limit): PaginatedResult
8377
{
8478
return $this->definitionRepository->getAfterId($afterId, $limit);
8579
}

0 commit comments

Comments
 (0)