Skip to content

Commit 28ff359

Browse files
TatevikGrtatevikg1
andauthored
ISSUE-12: subscription confirmation email (#342)
* ISSUE-12: subscription confirmation * ISSUE-12: subscription confirmation async * ISSUE-12: configs * ISSUE-12: configs + style --------- Co-authored-by: Tatevik <[email protected]>
1 parent a5d85bf commit 28ff359

File tree

13 files changed

+364
-79
lines changed

13 files changed

+364
-79
lines changed

config/packages/messenger.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ framework:
2424
routing:
2525
# Route your messages to the transports
2626
'PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage': async_email
27+
'PhpList\Core\Domain\Messaging\Message\SubscriberConfirmationMessage': async_email

config/parameters.yml.dist

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@ parameters:
2222
database_password: '%%env(PHPLIST_DATABASE_PASSWORD)%%'
2323
env(PHPLIST_DATABASE_PASSWORD): 'phplist'
2424

25-
# mailer configs
25+
# Email configuration
2626
app.mailer_from: '%%env(MAILER_FROM)%%'
2727
env(MAILER_FROM): '[email protected]'
2828
app.mailer_dsn: '%%env(MAILER_DSN)%%'
2929
env(MAILER_DSN): 'smtp://username:[email protected]:2525'
30+
app.confirmation_url: '%%env(CONFIRMATION_URL)%%'
31+
env(CONFIRMATION_URL): 'https://example.com/confirm/'
32+
33+
# Messenger configuration for asynchronous processing
34+
app.messenger_transport_dsn: '%%env(MESSENGER_TRANSPORT_DSN)%%'
35+
env(MESSENGER_TRANSPORT_DSN): 'doctrine://default?auto_setup=true'
3036

3137
# A secret key that's used to generate certain security-related tokens
3238
secret: '%%env(PHPLIST_SECRET)%%'

config/services.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ services:
3737
public: true
3838
tags: [controller.service_arguments]
3939

40-
# Register message handlers for Symfony Messenger
41-
PhpList\Core\Domain\Messaging\MessageHandler\:
42-
resource: '../src/Domain/Messaging/MessageHandler'
43-
tags: ['messenger.message_handler']
44-
4540
doctrine.orm.metadata.annotation_reader:
4641
alias: doctrine.annotation_reader
4742

config/services/managers.yml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,6 @@ services:
5252
autowire: true
5353
autoconfigure: true
5454

55-
PhpList\Core\Domain\Subscription\Service\SubscriberCsvExporter:
56-
autowire: true
57-
autoconfigure: true
58-
public: true
59-
60-
PhpList\Core\Domain\Subscription\Service\SubscriberCsvImporter:
61-
autowire: true
62-
autoconfigure: true
63-
public: true
64-
65-
PhpList\Core\Domain\Messaging\Service\EmailService:
66-
autowire: true
67-
autoconfigure: true
68-
arguments:
69-
$defaultFromEmail: '%app.mailer_from%'
70-
7155
PhpList\Core\Domain\Configuration\Service\Manager\ConfigManager:
7256
autowire: true
7357
autoconfigure: true

config/services/messenger.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
services:
2+
# Register message handlers for Symfony Messenger
3+
PhpList\Core\Domain\Messaging\MessageHandler\:
4+
resource: '../../src/Domain/Messaging/MessageHandler'
5+
tags: [ 'messenger.message_handler' ]
6+
7+
PhpList\Core\Domain\Messaging\MessageHandler\SubscriberConfirmationMessageHandler:
8+
autowire: true
9+
autoconfigure: true
10+
tags: [ 'messenger.message_handler' ]
11+
arguments:
12+
$confirmationUrl: '%app.confirmation_url%'

config/services/services.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
services:
2+
PhpList\Core\Domain\Subscription\Service\SubscriberCsvExporter:
3+
autowire: true
4+
autoconfigure: true
5+
public: true
6+
7+
PhpList\Core\Domain\Subscription\Service\SubscriberCsvImporter:
8+
autowire: true
9+
autoconfigure: true
10+
public: true
11+
12+
PhpList\Core\Domain\Messaging\Service\EmailService:
13+
autowire: true
14+
autoconfigure: true
15+
arguments:
16+
$defaultFromEmail: '%app.mailer_from%'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Messaging\Message;
6+
7+
/**
8+
* Message class for asynchronous subscriber confirmation email processing
9+
*/
10+
class SubscriberConfirmationMessage
11+
{
12+
private string $email;
13+
private string $uniqueId;
14+
private bool $htmlEmail;
15+
16+
/**
17+
* @SuppressWarnings("BooleanArgumentFlag")
18+
*/
19+
public function __construct(
20+
string $email,
21+
string $uniqueId,
22+
bool $htmlEmail = false
23+
) {
24+
$this->email = $email;
25+
$this->uniqueId = $uniqueId;
26+
$this->htmlEmail = $htmlEmail;
27+
}
28+
29+
public function getEmail(): string
30+
{
31+
return $this->email;
32+
}
33+
34+
public function getUniqueId(): string
35+
{
36+
return $this->uniqueId;
37+
}
38+
39+
public function hasHtmlEmail(): bool
40+
{
41+
return $this->htmlEmail;
42+
}
43+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Messaging\MessageHandler;
6+
7+
use PhpList\Core\Domain\Messaging\Message\SubscriberConfirmationMessage;
8+
use PhpList\Core\Domain\Messaging\Service\EmailService;
9+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
10+
use Symfony\Component\Mime\Email;
11+
12+
/**
13+
* Handler for processing asynchronous subscriber confirmation email messages
14+
*/
15+
#[AsMessageHandler]
16+
class SubscriberConfirmationMessageHandler
17+
{
18+
private EmailService $emailService;
19+
private string $confirmationUrl;
20+
21+
public function __construct(EmailService $emailService, string $confirmationUrl)
22+
{
23+
$this->emailService = $emailService;
24+
$this->confirmationUrl = $confirmationUrl;
25+
}
26+
27+
/**
28+
* Process a subscriber confirmation message by sending the confirmation email
29+
*/
30+
public function __invoke(SubscriberConfirmationMessage $message): void
31+
{
32+
$confirmationLink = $this->generateConfirmationLink($message->getUniqueId());
33+
34+
$subject = 'Please confirm your subscription';
35+
$textContent = "Thank you for subscribing!\n\n"
36+
. "Please confirm your subscription by clicking the link below:\n"
37+
. $confirmationLink . "\n\n"
38+
. 'If you did not request this subscription, please ignore this email.';
39+
40+
$htmlContent = '';
41+
if ($message->hasHtmlEmail()) {
42+
$htmlContent = '<p>Thank you for subscribing!</p>'
43+
. '<p>Please confirm your subscription by clicking the link below:</p>'
44+
. '<p><a href="' . $confirmationLink . '">Confirm Subscription</a></p>'
45+
. '<p>If you did not request this subscription, please ignore this email.</p>';
46+
}
47+
48+
$email = (new Email())
49+
->to($message->getEmail())
50+
->subject($subject)
51+
->text($textContent);
52+
53+
if (!empty($htmlContent)) {
54+
$email->html($htmlContent);
55+
}
56+
57+
$this->emailService->sendEmail($email);
58+
}
59+
60+
/**
61+
* Generate a confirmation link for the subscriber
62+
*/
63+
private function generateConfirmationLink(string $uniqueId): string
64+
{
65+
return $this->confirmationUrl . $uniqueId;
66+
}
67+
}

src/Domain/Messaging/Service/EmailService.php

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace PhpList\Core\Domain\Messaging\Service;
66

77
use PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage;
8-
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
98
use Symfony\Component\Mailer\MailerInterface;
109
use Symfony\Component\Messenger\MessageBusInterface;
1110
use Symfony\Component\Mime\Email;
@@ -27,16 +26,6 @@ public function __construct(
2726
$this->messageBus = $messageBus;
2827
}
2928

30-
/**
31-
* Send a simple email asynchronously
32-
*
33-
* @param Email $email
34-
* @param array $cc
35-
* @param array $bcc
36-
* @param array $replyTo
37-
* @param array $attachments
38-
* @return void
39-
*/
4029
public function sendEmail(
4130
Email $email,
4231
array $cc = [],
@@ -52,17 +41,6 @@ public function sendEmail(
5241
$this->messageBus->dispatch($message);
5342
}
5443

55-
/**
56-
* Send a simple email synchronously
57-
*
58-
* @param Email $email
59-
* @param array $cc
60-
* @param array $bcc
61-
* @param array $replyTo
62-
* @param array $attachments
63-
* @return void
64-
* @throws TransportExceptionInterface
65-
*/
6644
public function sendEmailSync(
6745
Email $email,
6846
array $cc = [],
@@ -93,19 +71,6 @@ public function sendEmailSync(
9371
$this->mailer->send($email);
9472
}
9573

96-
/**
97-
* Email multiple recipients asynchronously
98-
*
99-
* @param array $toAddresses Array of recipient email addresses
100-
* @param string $subject Email subject
101-
* @param string $text Plain text content
102-
* @param string $html HTML content (optional)
103-
* @param string|null $from Sender email address (optional, uses default if not provided)
104-
* @param string|null $fromName Sender name (optional)
105-
* @param array $attachments Array of file paths to attach (optional)
106-
*
107-
* @return void
108-
*/
10974
public function sendBulkEmail(
11075
array $toAddresses,
11176
string $subject,
@@ -132,20 +97,6 @@ public function sendBulkEmail(
13297
}
13398
}
13499

135-
/**
136-
* Email multiple recipients synchronously
137-
*
138-
* @param array $toAddresses Array of recipient email addresses
139-
* @param string $subject Email subject
140-
* @param string $text Plain text content
141-
* @param string $html HTML content (optional)
142-
* @param string|null $from Sender email address (optional, uses default if not provided)
143-
* @param string|null $fromName Sender name (optional)
144-
* @param array $attachments Array of file paths to attach (optional)
145-
*
146-
* @return void
147-
* @throws TransportExceptionInterface
148-
*/
149100
public function sendBulkEmailSync(
150101
array $toAddresses,
151102
string $subject,

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,29 @@
55
namespace PhpList\Core\Domain\Subscription\Service\Manager;
66

77
use Doctrine\ORM\EntityManagerInterface;
8+
use PhpList\Core\Domain\Messaging\Message\SubscriberConfirmationMessage;
89
use PhpList\Core\Domain\Subscription\Model\Dto\CreateSubscriberDto;
910
use PhpList\Core\Domain\Subscription\Model\Dto\ImportSubscriberDto;
1011
use PhpList\Core\Domain\Subscription\Model\Dto\UpdateSubscriberDto;
1112
use PhpList\Core\Domain\Subscription\Model\Subscriber;
1213
use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository;
1314
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
15+
use Symfony\Component\Messenger\MessageBusInterface;
1416

1517
class SubscriberManager
1618
{
1719
private SubscriberRepository $subscriberRepository;
1820
private EntityManagerInterface $entityManager;
21+
private MessageBusInterface $messageBus;
1922

20-
public function __construct(SubscriberRepository $subscriberRepository, EntityManagerInterface $entityManager)
21-
{
23+
public function __construct(
24+
SubscriberRepository $subscriberRepository,
25+
EntityManagerInterface $entityManager,
26+
MessageBusInterface $messageBus
27+
) {
2228
$this->subscriberRepository = $subscriberRepository;
2329
$this->entityManager = $entityManager;
30+
$this->messageBus = $messageBus;
2431
}
2532

2633
public function createSubscriber(CreateSubscriberDto $subscriberDto): Subscriber
@@ -35,9 +42,24 @@ public function createSubscriber(CreateSubscriberDto $subscriberDto): Subscriber
3542

3643
$this->subscriberRepository->save($subscriber);
3744

45+
if ($subscriberDto->requestConfirmation) {
46+
$this->sendConfirmationEmail($subscriber);
47+
}
48+
3849
return $subscriber;
3950
}
4051

52+
private function sendConfirmationEmail(Subscriber $subscriber): void
53+
{
54+
$message = new SubscriberConfirmationMessage(
55+
email: $subscriber->getEmail(),
56+
uniqueId:$subscriber->getUniqueId(),
57+
htmlEmail: $subscriber->hasHtmlEmail()
58+
);
59+
60+
$this->messageBus->dispatch($message);
61+
}
62+
4163
public function getSubscriber(int $subscriberId): Subscriber
4264
{
4365
$subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriberId);

0 commit comments

Comments
 (0)