Skip to content

Commit 19684f5

Browse files
committed
feat: add Recipient value object for named email recipients
Introduce a Recipient class to replace raw string arrays for to, cc, and bcc fields. This provides a consistent, type-safe API for associating names with email addresses across all fields and adapters. BREAKING CHANGE: Email constructor now accepts array<Recipient> for to, cc, and bcc instead of array<string> and array<array<string,string>>.
1 parent de06865 commit 19684f5

8 files changed

Lines changed: 104 additions & 93 deletions

File tree

src/Utopia/Messaging/Adapter/Email/Mailgun.php

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,15 @@ protected function process(EmailMessage $message): array
5151

5252
$domain = $this->isEU ? $euDomain : $usDomain;
5353

54+
$toEmails = \array_map(fn ($to) => $to->getEmail(), $message->getTo());
55+
5456
$body = [
55-
'to' => \implode(',', $message->getTo()),
57+
'to' => \implode(',', \array_map(
58+
fn ($to) => !empty($to->getName())
59+
? "{$to->getName()}<{$to->getEmail()}>"
60+
: $to->getEmail(),
61+
$message->getTo()
62+
)),
5663
'from' => "{$message->getFromName()}<{$message->getFromEmail()}>",
5764
'subject' => $message->getSubject(),
5865
'text' => $message->isHtml() ? null : $message->getContent(),
@@ -61,34 +68,30 @@ protected function process(EmailMessage $message): array
6168
];
6269

6370
if (\count($message->getTo()) > 1) {
64-
$body['recipient-variables'] = json_encode(array_fill_keys($message->getTo(), []));
71+
$body['recipient-variables'] = json_encode(array_fill_keys($toEmails, []));
6572
}
6673

6774
if (!\is_null($message->getCC())) {
6875
foreach ($message->getCC() as $cc) {
69-
if (!empty($cc['email'])) {
70-
$ccString = !empty($cc['name'])
71-
? "{$cc['name']}<{$cc['email']}>"
72-
: $cc['email'];
73-
74-
$body['cc'] = !empty($body['cc'])
75-
? "{$body['cc']},{$ccString}"
76-
: $ccString;
77-
}
76+
$ccString = !empty($cc->getName())
77+
? "{$cc->getName()}<{$cc->getEmail()}>"
78+
: $cc->getEmail();
79+
80+
$body['cc'] = !empty($body['cc'])
81+
? "{$body['cc']},{$ccString}"
82+
: $ccString;
7883
}
7984
}
8085

8186
if (!\is_null($message->getBCC())) {
8287
foreach ($message->getBCC() as $bcc) {
83-
if (!empty($bcc['email'])) {
84-
$bccString = !empty($bcc['name'])
85-
? "{$bcc['name']}<{$bcc['email']}>"
86-
: $bcc['email'];
87-
88-
$body['bcc'] = !empty($body['bcc'])
89-
? "{$body['bcc']},{$bccString}"
90-
: $bccString;
91-
}
88+
$bccString = !empty($bcc->getName())
89+
? "{$bcc->getName()}<{$bcc->getEmail()}>"
90+
: $bcc->getEmail();
91+
92+
$body['bcc'] = !empty($body['bcc'])
93+
? "{$body['bcc']},{$bccString}"
94+
: $bccString;
9295
}
9396
}
9497

@@ -140,16 +143,16 @@ protected function process(EmailMessage $message): array
140143
if ($statusCode >= 200 && $statusCode < 300) {
141144
$response->setDeliveredTo(\count($message->getTo()));
142145
foreach ($message->getTo() as $to) {
143-
$response->addResult($to);
146+
$response->addResult($to->getEmail());
144147
}
145148
} elseif ($statusCode >= 400 && $statusCode < 500) {
146149
foreach ($message->getTo() as $to) {
147150
if (\is_string($result['response'])) {
148-
$response->addResult($to, $result['response']);
151+
$response->addResult($to->getEmail(), $result['response']);
149152
} elseif (isset($result['response']['message'])) {
150-
$response->addResult($to, $result['response']['message']);
153+
$response->addResult($to->getEmail(), $result['response']['message']);
151154
} else {
152-
$response->addResult($to, 'Unknown error');
155+
$response->addResult($to->getEmail(), 'Unknown error');
153156
}
154157
}
155158
}

src/Utopia/Messaging/Adapter/Email/Mock.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,29 +47,29 @@ protected function process(EmailMessage $message): array
4747
$mail->isHTML($message->isHtml());
4848

4949
foreach ($message->getTo() as $to) {
50-
$mail->addAddress($to);
50+
$mail->addAddress($to->getEmail(), $to->getName());
5151
}
5252

5353
if (!empty($message->getCC())) {
5454
foreach ($message->getCC() as $cc) {
55-
$mail->addCC($cc['email'], $cc['name'] ?? '');
55+
$mail->addCC($cc->getEmail(), $cc->getName());
5656
}
5757
}
5858

5959
if (!empty($message->getBCC())) {
6060
foreach ($message->getBCC() as $bcc) {
61-
$mail->addBCC($bcc['email'], $bcc['name'] ?? '');
61+
$mail->addBCC($bcc->getEmail(), $bcc->getName());
6262
}
6363
}
6464

6565
if (!$mail->send()) {
6666
foreach ($message->getTo() as $to) {
67-
$response->addResult($to, $mail->ErrorInfo);
67+
$response->addResult($to->getEmail(), $mail->ErrorInfo);
6868
}
6969
} else {
7070
$response->setDeliveredTo(\count($message->getTo()));
7171
foreach ($message->getTo() as $to) {
72-
$response->addResult($to);
72+
$response->addResult($to->getEmail());
7373
}
7474
}
7575

src/Utopia/Messaging/Adapter/Email/Resend.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,15 @@ protected function process(EmailMessage $message): array
7777

7878
$emails = [];
7979
foreach ($message->getTo() as $to) {
80+
$toFormatted = !empty($to->getName())
81+
? "{$to->getName()} <{$to->getEmail()}>"
82+
: $to->getEmail();
83+
8084
$email = [
8185
'from' => $message->getFromName()
8286
? "{$message->getFromName()} <{$message->getFromEmail()}>"
8387
: $message->getFromEmail(),
84-
'to' => [$to],
88+
'to' => [$toFormatted],
8589
'subject' => $message->getSubject(),
8690
];
8791

@@ -100,11 +104,9 @@ protected function process(EmailMessage $message): array
100104
if (! \is_null($message->getCC()) && ! empty($message->getCC())) {
101105
$ccList = [];
102106
foreach ($message->getCC() as $cc) {
103-
if (! empty($cc['email'])) {
104-
$ccList[] = ! empty($cc['name'])
105-
? "{$cc['name']} <{$cc['email']}>"
106-
: $cc['email'];
107-
}
107+
$ccList[] = ! empty($cc->getName())
108+
? "{$cc->getName()} <{$cc->getEmail()}>"
109+
: $cc->getEmail();
108110
}
109111
if (! empty($ccList)) {
110112
$email['cc'] = $ccList;
@@ -118,11 +120,9 @@ protected function process(EmailMessage $message): array
118120
if (! \is_null($message->getBCC()) && ! empty($message->getBCC())) {
119121
$bccList = [];
120122
foreach ($message->getBCC() as $bcc) {
121-
if (! empty($bcc['email'])) {
122-
$bccList[] = ! empty($bcc['name'])
123-
? "{$bcc['name']} <{$bcc['email']}>"
124-
: $bcc['email'];
125-
}
123+
$bccList[] = ! empty($bcc->getName())
124+
? "{$bcc->getName()} <{$bcc->getEmail()}>"
125+
: $bcc->getEmail();
126126
}
127127
if (! empty($bccList)) {
128128
$email['bcc'] = $bccList;
@@ -157,9 +157,9 @@ protected function process(EmailMessage $message): array
157157

158158
foreach ($message->getTo() as $index => $to) {
159159
if (isset($failedIndices[$index])) {
160-
$response->addResult($to, $failedIndices[$index]);
160+
$response->addResult($to->getEmail(), $failedIndices[$index]);
161161
} else {
162-
$response->addResult($to);
162+
$response->addResult($to->getEmail());
163163
}
164164
}
165165

@@ -168,7 +168,7 @@ protected function process(EmailMessage $message): array
168168
} else {
169169
$response->setDeliveredTo(\count($message->getTo()));
170170
foreach ($message->getTo() as $to) {
171-
$response->addResult($to);
171+
$response->addResult($to->getEmail());
172172
}
173173
}
174174
} elseif ($statusCode >= 400 && $statusCode < 500) {
@@ -183,7 +183,7 @@ protected function process(EmailMessage $message): array
183183
}
184184

185185
foreach ($message->getTo() as $to) {
186-
$response->addResult($to, $errorMessage);
186+
$response->addResult($to->getEmail(), $errorMessage);
187187
}
188188
} elseif ($statusCode >= 500) {
189189
$errorMessage = 'Server error';
@@ -195,7 +195,7 @@ protected function process(EmailMessage $message): array
195195
}
196196

197197
foreach ($message->getTo() as $to) {
198-
$response->addResult($to, $errorMessage);
198+
$response->addResult($to->getEmail(), $errorMessage);
199199
}
200200
}
201201

src/Utopia/Messaging/Adapter/Email/SMTP.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,18 @@ protected function process(EmailMessage $message): array
9797
$mail->AltBody = \trim($mail->AltBody);
9898

9999
foreach ($message->getTo() as $to) {
100-
$mail->addAddress($to);
100+
$mail->addAddress($to->getEmail(), $to->getName());
101101
}
102102

103103
if (!empty($message->getCC())) {
104104
foreach ($message->getCC() as $cc) {
105-
$mail->addCC($cc['email'], $cc['name'] ?? '');
105+
$mail->addCC($cc->getEmail(), $cc->getName());
106106
}
107107
}
108108

109109
if (!empty($message->getBCC())) {
110110
foreach ($message->getBCC() as $bcc) {
111-
$mail->addBCC($bcc['email'], $bcc['name'] ?? '');
111+
$mail->addBCC($bcc->getEmail(), $bcc->getName());
112112
}
113113
}
114114

@@ -158,23 +158,23 @@ protected function process(EmailMessage $message): array
158158
? 'Unknown error'
159159
: $mail->ErrorInfo;
160160

161-
$response->addResult($to, $sent ? '' : $error);
161+
$response->addResult($to->getEmail(), $sent ? '' : $error);
162162
}
163163

164164
foreach ($message->getCC() as $cc) {
165165
$error = empty($mail->ErrorInfo)
166166
? 'Unknown error'
167167
: $mail->ErrorInfo;
168168

169-
$response->addResult($cc['email'], $sent ? '' : $error);
169+
$response->addResult($cc->getEmail(), $sent ? '' : $error);
170170
}
171171

172172
foreach ($message->getBCC() as $bcc) {
173173
$error = empty($mail->ErrorInfo)
174174
? 'Unknown error'
175175
: $mail->ErrorInfo;
176176

177-
$response->addResult($bcc['email'], $sent ? '' : $error);
177+
$response->addResult($bcc->getEmail(), $sent ? '' : $error);
178178
}
179179

180180
return $response->toArray();

src/Utopia/Messaging/Adapter/Email/Sendgrid.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ protected function process(EmailMessage $message): array
4545
{
4646
$personalizations = \array_map(
4747
fn ($to) => [
48-
'to' => [['email' => $to]],
48+
'to' => [!empty($to->getName())
49+
? ['email' => $to->getEmail(), 'name' => $to->getName()]
50+
: ['email' => $to->getEmail()]],
4951
'subject' => $message->getSubject(),
5052
],
5153
$message->getTo()
@@ -54,9 +56,9 @@ protected function process(EmailMessage $message): array
5456
if (!empty($message->getCC())) {
5557
foreach ($personalizations as &$personalization) {
5658
foreach ($message->getCC() as $cc) {
57-
$entry = ['email' => $cc['email']];
58-
if (!empty($cc['name'])) {
59-
$entry['name'] = $cc['name'];
59+
$entry = ['email' => $cc->getEmail()];
60+
if (!empty($cc->getName())) {
61+
$entry['name'] = $cc->getName();
6062
}
6163
$personalization['cc'][] = $entry;
6264
}
@@ -67,9 +69,9 @@ protected function process(EmailMessage $message): array
6769
if (!empty($message->getBCC())) {
6870
foreach ($personalizations as &$personalization) {
6971
foreach ($message->getBCC() as $bcc) {
70-
$entry = ['email' => $bcc['email']];
71-
if (!empty($bcc['name'])) {
72-
$entry['name'] = $bcc['name'];
72+
$entry = ['email' => $bcc->getEmail()];
73+
if (!empty($bcc->getName())) {
74+
$entry['name'] = $bcc->getName();
7375
}
7476
$personalization['bcc'][] = $entry;
7577
}
@@ -138,16 +140,16 @@ protected function process(EmailMessage $message): array
138140
if ($statusCode === 202) {
139141
$response->setDeliveredTo(\count($message->getTo()));
140142
foreach ($message->getTo() as $to) {
141-
$response->addResult($to);
143+
$response->addResult($to->getEmail());
142144
}
143145
} else {
144146
foreach ($message->getTo() as $to) {
145147
if (\is_string($result['response'])) {
146-
$response->addResult($to, $result['response']);
148+
$response->addResult($to->getEmail(), $result['response']);
147149
} elseif (!\is_null($result['response']['errors'][0]['message'] ?? null)) {
148-
$response->addResult($to, $result['response']['errors'][0]['message']);
150+
$response->addResult($to->getEmail(), $result['response']['errors'][0]['message']);
149151
} else {
150-
$response->addResult($to, 'Unknown error');
152+
$response->addResult($to->getEmail(), 'Unknown error');
151153
}
152154
}
153155
}

src/Utopia/Messaging/Messages/Email.php

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,22 @@
44

55
use Utopia\Messaging\Message;
66
use Utopia\Messaging\Messages\Email\Attachment;
7+
use Utopia\Messaging\Messages\Email\Recipient;
78

89
class Email implements Message
910
{
1011
/**
11-
* @param array<string> $to The recipients of the email.
12+
* @param array<Recipient> $to The recipients of the email.
1213
* @param string $subject The subject of the email.
1314
* @param string $content The content of the email.
1415
* @param string $fromName The name of the sender.
1516
* @param string $fromEmail The email address of the sender.
16-
* @param array<array<string,string>>|null $cc . The CC recipients of the email. Each recipient should be an array containing a "name" and an "email" key.
17-
* @param array<array<string,string>>|null $bcc . The BCC recipients of the email. Each recipient should be an array containing a "name" and an "email" key.
1817
* @param string|null $replyToName The name of the reply to.
1918
* @param string|null $replyToEmail The email address of the reply to.
19+
* @param array<Recipient>|null $cc The CC recipients of the email.
20+
* @param array<Recipient>|null $bcc The BCC recipients of the email.
2021
* @param array<Attachment>|null $attachments The attachments of the email.
2122
* @param bool $html Whether the message is HTML or not.
22-
*
23-
* @throws \InvalidArgumentException
2423
*/
2524
public function __construct(
2625
private array $to,
@@ -42,26 +41,10 @@ public function __construct(
4241
if (\is_null($this->replyToEmail)) {
4342
$this->replyToEmail = $this->fromEmail;
4443
}
45-
46-
if (!\is_null($this->cc)) {
47-
foreach ($this->cc as $recipient) {
48-
if (!isset($recipient['email'])) {
49-
throw new \InvalidArgumentException('Each CC recipient must have at least an email');
50-
}
51-
}
52-
}
53-
54-
if (!\is_null($this->bcc)) {
55-
foreach ($this->bcc as $recipient) {
56-
if (!isset($recipient['email'])) {
57-
throw new \InvalidArgumentException('Each BCC recipient must have at least an email');
58-
}
59-
}
60-
}
6144
}
6245

6346
/**
64-
* @return array<string>
47+
* @return array<Recipient>
6548
*/
6649
public function getTo(): array
6750
{
@@ -99,23 +82,23 @@ public function getReplyToEmail(): string
9982
}
10083

10184
/**
102-
* @return array<array<string, string>>|null
85+
* @return array<Recipient>|null
10386
*/
10487
public function getCC(): ?array
10588
{
10689
return $this->cc;
10790
}
10891

10992
/**
110-
* @return array<array<string, string>>|null
93+
* @return array<Recipient>|null
11194
*/
11295
public function getBCC(): ?array
11396
{
11497
return $this->bcc;
11598
}
11699

117100
/**
118-
* @return array<string, mixed>|null
101+
* @return array<Attachment>|null
119102
*/
120103
public function getAttachments(): ?array
121104
{

0 commit comments

Comments
 (0)