Skip to content

Commit f5e1d64

Browse files
committed
Add changeSetDto
1 parent f976d46 commit f5e1d64

File tree

12 files changed

+241
-55
lines changed

12 files changed

+241
-55
lines changed

config/services/providers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ services:
2828
arguments:
2929
$cache: '@Psr\SimpleCache\CacheInterface'
3030

31-
PhpList\Core\Domain\Subscription\Service\Provider\SubscriberAttributeProvider:
31+
PhpList\Core\Domain\Subscription\Service\Provider\SubscriberAttributeChangeSetProvider:
3232
autowire: true

src/Domain/Messaging/Service/Processor/CampaignProcessor.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
/**
2626
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
27+
* @SuppressWarnings(PHPMD.StaticAccess)
28+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
2729
*/
2830
class CampaignProcessor
2931
{
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Subscription\Model\Dto;
6+
7+
class ChangeSetDto
8+
{
9+
public const IGNORED_ATTRIBUTES = ['password', 'modified'];
10+
11+
/**
12+
* @var array<string, array{0: mixed, 1: mixed}>
13+
*
14+
* Example:
15+
* [
16+
* 'email' => [null, '[email protected]'],
17+
* 'isActive' => [true, false]
18+
* ]
19+
*/
20+
private array $changes = [];
21+
22+
/**
23+
* @param array<string, array{0: mixed, 1: mixed}> $changes
24+
*/
25+
public function __construct(array $changes = [])
26+
{
27+
$this->changes = $changes;
28+
}
29+
30+
/**
31+
* @return array<string, array{0: mixed, 1: mixed}>
32+
*/
33+
public function getChanges(): array
34+
{
35+
return $this->changes;
36+
}
37+
38+
public function hasChanges(): bool
39+
{
40+
return !empty($this->changes);
41+
}
42+
43+
public function hasField(string $field): bool
44+
{
45+
return array_key_exists($field, $this->changes);
46+
}
47+
48+
/**
49+
* @return array{0: mixed, 1: mixed}|null
50+
*/
51+
public function getFieldChange(string $field): ?array
52+
{
53+
return $this->changes[$field] ?? null;
54+
}
55+
56+
/**
57+
* @return mixed|null
58+
*/
59+
public function getOldValue(string $field): mixed
60+
{
61+
return $this->changes[$field][0] ?? null;
62+
}
63+
64+
/**
65+
* @return mixed|null
66+
*/
67+
public function getNewValue(string $field): mixed
68+
{
69+
return $this->changes[$field][1] ?? null;
70+
}
71+
72+
public function toArray(): array
73+
{
74+
return $this->changes;
75+
}
76+
77+
public static function fromDoctrineChangeSet(array $changeSet): self
78+
{
79+
return new self($changeSet);
80+
}
81+
}

src/Domain/Subscription/Service/Manager/SubscriberAttributeManager.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public function createOrUpdate(
3737
SubscriberAttributeDefinition $definition,
3838
?string $value = null
3939
): SubscriberAttributeValue {
40+
// phpcs:ignore Generic.Commenting.Todo
41+
// todo: clarify which attributes can be created/updated
4042
$subscriberAttribute = $this->attributeRepository
4143
->findOneBySubscriberAndAttribute($subscriber, $definition);
4244

@@ -65,11 +67,9 @@ public function delete(SubscriberAttributeValue $attribute): void
6567
$this->attributeRepository->remove($attribute);
6668
}
6769

68-
public function processAttributes(Subscriber $subscriber, array $extraData): void
70+
public function processAttributes(Subscriber $subscriber, array $attributeData): void
6971
{
70-
// $oldAttributesMap = $this->subscriberAttributeProvider->getMappedValues($subscriber);
71-
72-
foreach ($extraData as $key => $value) {
72+
foreach ($attributeData as $key => $value) {
7373
$lowerKey = strtolower((string)$key);
7474
if (in_array($lowerKey, ['password', 'modified'], true)) {
7575
continue;
@@ -84,7 +84,5 @@ public function processAttributes(Subscriber $subscriber, array $extraData): voi
8484
);
8585
}
8686
}
87-
88-
// $newAttributesMap = $this->subscriberAttributeProvider->getMappedValues($subscriber);
8987
}
9088
}

src/Domain/Subscription/Service/Manager/SubscriberHistoryManager.php

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PhpList\Core\Domain\Common\ClientIpResolver;
88
use PhpList\Core\Domain\Common\SystemInfoCollector;
99
use PhpList\Core\Domain\Identity\Model\Administrator;
10+
use PhpList\Core\Domain\Subscription\Model\Dto\ChangeSetDto;
1011
use PhpList\Core\Domain\Subscription\Model\Filter\SubscriberHistoryFilter;
1112
use PhpList\Core\Domain\Subscription\Model\Subscriber;
1213
use PhpList\Core\Domain\Subscription\Model\SubscriberHistory;
@@ -50,15 +51,42 @@ public function addHistory(Subscriber $subscriber, string $message, ?string $det
5051
return $subscriberHistory;
5152
}
5253

54+
public function addHistoryFromChangeSet(
55+
Subscriber $subscriber,
56+
string $message,
57+
ChangeSetDto $changeSet,
58+
): SubscriberHistory {
59+
$details = '';
60+
foreach ($changeSet->getChanges() as $attribute => [$old, $new]) {
61+
if (in_array($attribute, ChangeSetDto::IGNORED_ATTRIBUTES, true) || $new === null) {
62+
continue;
63+
}
64+
$details .= $this->translator->trans(
65+
"%attribute% = %new_value% \n changed from %old_value%",
66+
[
67+
'%attribute%' => $attribute,
68+
'%new_value%' => $new,
69+
'%old_value%' => $old ?? $this->translator->trans('(no data)'),
70+
]
71+
) . PHP_EOL;
72+
}
73+
74+
if ($details === '') {
75+
$details .= $this->translator->trans('No data changed') . PHP_EOL;
76+
}
77+
78+
return $this->addHistory($subscriber, $message, $details);
79+
}
80+
5381
public function addHistoryFromImport(
5482
Subscriber $subscriber,
5583
array $listLines,
56-
array $updatedData,
84+
ChangeSetDto $changeSetDto,
5785
?Administrator $admin = null,
5886
): void {
59-
$headerLine = sprintf("API-v2-import - %s: %s%s", $admin ? 'Admin' : 'CLI', $admin?->getId(), "\n\n");
87+
$headerLine = sprintf('API-v2-import - %s: %s%s', $admin ? 'Admin' : 'CLI', $admin?->getId(), "\n\n");
6088

61-
$lines = $this->getHistoryLines($updatedData, $listLines);
89+
$lines = $this->getHistoryLines($changeSetDto, $listLines);
6290

6391
$this->addHistory(
6492
subscriber: $subscriber,
@@ -70,7 +98,7 @@ public function addHistoryFromImport(
7098
public function addHistoryFromApi(
7199
Subscriber $subscriber,
72100
array $listLines,
73-
array $updatedData,
101+
ChangeSetDto $updatedData,
74102
?Administrator $admin = null,
75103
): void {
76104
$lines = $this->getHistoryLines($updatedData, $listLines);
@@ -82,15 +110,14 @@ public function addHistoryFromApi(
82110
);
83111
}
84112

85-
private function getHistoryLines(array $updatedData, array $listLines): array
113+
private function getHistoryLines(ChangeSetDto $updatedData, array $listLines): array
86114
{
87115
$lines = [];
88-
if (empty($updatedData) && empty($listLines)) {
116+
if (!$updatedData->hasChanges() && empty($listLines)) {
89117
$lines[] = $this->translator->trans('No user details changed');
90118
} else {
91-
$skip = ['password', 'modified'];
92-
foreach ($updatedData as $field => [$old, $new]) {
93-
if (in_array($field, $skip, true)) {
119+
foreach ($updatedData->getChanges() as $field => [$old, $new]) {
120+
if (in_array($field, ChangeSetDto::IGNORED_ATTRIBUTES, true)) {
94121
continue;
95122
}
96123
$lines[] = $this->translator->trans(

src/Domain/Subscription/Service/Manager/SubscriberManager.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\ORM\EntityManagerInterface;
88
use PhpList\Core\Domain\Identity\Model\Administrator;
99
use PhpList\Core\Domain\Messaging\Message\SubscriberConfirmationMessage;
10+
use PhpList\Core\Domain\Subscription\Model\Dto\ChangeSetDto;
1011
use PhpList\Core\Domain\Subscription\Model\Dto\CreateSubscriberDto;
1112
use PhpList\Core\Domain\Subscription\Model\Dto\ImportSubscriberDto;
1213
use PhpList\Core\Domain\Subscription\Model\Dto\UpdateSubscriberDto;
@@ -17,6 +18,9 @@
1718
use Symfony\Component\Messenger\MessageBusInterface;
1819
use Symfony\Contracts\Translation\TranslatorInterface;
1920

21+
/**
22+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
23+
*/
2024
class SubscriberManager
2125
{
2226
private SubscriberRepository $subscriberRepository;
@@ -77,6 +81,7 @@ public function getSubscriberById(int $subscriberId): ?Subscriber
7781
return $this->subscriberRepository->find($subscriberId);
7882
}
7983

84+
/** @SuppressWarnings(PHPMD.StaticAccess) */
8085
public function updateSubscriber(UpdateSubscriberDto $subscriberDto, Administrator $admin): Subscriber
8186
{
8287
/** @var Subscriber $subscriber */
@@ -92,9 +97,7 @@ public function updateSubscriber(UpdateSubscriberDto $subscriberDto, Administrat
9297
$uow = $this->entityManager->getUnitOfWork();
9398
$meta = $this->entityManager->getClassMetadata(Subscriber::class);
9499
$uow->computeChangeSet($meta, $subscriber);
95-
$changeSet = $uow->getEntityChangeSet($subscriber);
96-
97-
$this->entityManager->flush();
100+
$changeSet = ChangeSetDto::fromDoctrineChangeSet($uow->getEntityChangeSet($subscriber));
98101

99102
$this->subscriberHistoryManager->addHistoryFromApi($subscriber, [], $changeSet, $admin);
100103

@@ -146,7 +149,8 @@ public function createFromImport(ImportSubscriberDto $subscriberDto): Subscriber
146149
return $subscriber;
147150
}
148151

149-
public function updateFromImport(Subscriber $existingSubscriber, ImportSubscriberDto $subscriberDto): array
152+
/** @SuppressWarnings(PHPMD.StaticAccess) */
153+
public function updateFromImport(Subscriber $existingSubscriber, ImportSubscriberDto $subscriberDto): ChangeSetDto
150154
{
151155
$existingSubscriber->setEmail($subscriberDto->email);
152156
$existingSubscriber->setConfirmed($subscriberDto->confirmed);
@@ -159,7 +163,7 @@ public function updateFromImport(Subscriber $existingSubscriber, ImportSubscribe
159163
$meta = $this->entityManager->getClassMetadata(Subscriber::class);
160164
$uow->computeChangeSet($meta, $existingSubscriber);
161165

162-
return $uow->getEntityChangeSet($existingSubscriber);
166+
return ChangeSetDto::fromDoctrineChangeSet($uow->getEntityChangeSet($existingSubscriber));
163167
}
164168

165169
public function decrementBounceCount(Subscriber $subscriber): void
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Subscription\Service\Provider;
6+
7+
use PhpList\Core\Domain\Subscription\Model\Dto\ChangeSetDto;
8+
use PhpList\Core\Domain\Subscription\Model\Subscriber;
9+
use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeValueRepository;
10+
use PhpList\Core\Domain\Subscription\Service\Resolver\AttributeValueResolver;
11+
12+
class SubscriberAttributeChangeSetProvider
13+
{
14+
public function __construct(
15+
private readonly AttributeValueResolver $resolver,
16+
private readonly SubscriberAttributeValueRepository $attributesRepository,
17+
) {
18+
}
19+
20+
/**
21+
* Get the changes between the subscriber’s existing attributes and new attribute data.
22+
*
23+
* @param Subscriber $subscriber
24+
* @param array<string, mixed> $attributeData
25+
* @return ChangeSetDto
26+
*/
27+
public function getAttributeChangeSet(Subscriber $subscriber, array $attributeData): ChangeSetDto
28+
{
29+
$oldMap = $this->getMappedValues($subscriber);
30+
31+
$canon = static function (array $attributes): array {
32+
$out = [];
33+
foreach ($attributes as $key => $value) {
34+
$out[mb_strtolower((string)$key)] = $value;
35+
}
36+
return $out;
37+
};
38+
39+
$oldC = $canon($oldMap);
40+
$newC = $canon($attributeData);
41+
42+
foreach (ChangeSetDto::IGNORED_ATTRIBUTES as $ignoredAttribute) {
43+
$lowerCaseIgnoredAttribute = mb_strtolower($ignoredAttribute);
44+
unset($oldC[$lowerCaseIgnoredAttribute], $newC[$lowerCaseIgnoredAttribute]);
45+
}
46+
47+
$keys = array_values(array_unique(array_merge(array_keys($oldC), array_keys($newC))));
48+
49+
$changeSet = [];
50+
foreach ($keys as $key) {
51+
$hasOld = array_key_exists($key, $oldC);
52+
$hasNew = array_key_exists($key, $newC);
53+
54+
if ($hasOld && !$hasNew) {
55+
$changeSet[$key] = [$oldC[$key], null];
56+
continue;
57+
}
58+
59+
if (!$hasOld && $hasNew) {
60+
$changeSet[$key] = [null, $newC[$key]];
61+
continue;
62+
}
63+
64+
if ($oldC[$key] !== $newC[$key]) {
65+
$changeSet[$key] = [$oldC[$key], $newC[$key]];
66+
}
67+
}
68+
69+
return new ChangeSetDto($changeSet);
70+
}
71+
72+
private function getMappedValues(Subscriber $subscriber): array
73+
{
74+
$userAttributes = $this->attributesRepository->getForSubscriber($subscriber);
75+
foreach ($userAttributes as $userAttribute) {
76+
$data[$userAttribute->getAttributeDefinition()->getName()] = $this->resolver->resolve($userAttribute);
77+
}
78+
79+
return $data ?? [];
80+
}
81+
}

src/Domain/Subscription/Service/Provider/SubscriberAttributeProvider.php

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)