emailer update fixed so it can now send to multiple recipients

This commit is contained in:
Simon Pocrnjič
2025-10-15 23:46:44 +02:00
parent ddfc79ffe8
commit ed62311ba4
7 changed files with 172 additions and 28 deletions
-1
View File
@@ -43,7 +43,6 @@ public function handle(): void
$result = $sender->sendFromLog($log);
$log->status = EmailLogStatus::Sent;
$log->message_id = $result['message_id'] ?? ($log->message_id ?? null);
$log->sent_at = now();
$log->duration_ms = (int) round((microtime(true) - $start) * 1000);
$log->save();
+1 -1
View File
@@ -23,9 +23,9 @@ class EmailLog extends Model
'template_id',
'mail_profile_id',
'user_id',
'message_id',
'correlation_id',
'to_email',
'to_recipients',
'to_name',
'cc',
'bcc',
+7
View File
@@ -12,6 +12,7 @@
use App\Models\EmailLog;
use App\Models\EmailLogStatus;
use App\Models\EmailTemplate;
use App\Models\MailProfile;
class AutoMailDispatcher
{
@@ -95,6 +96,8 @@ public function maybeQueue(Activity $activity, bool $sendFlag = true): array
$log->fill([
'uuid' => (string) \Str::uuid(),
'template_id' => $template->id,
'mail_profile_id' => optional(MailProfile::query()->where('active', true)->orderBy('priority')->orderBy('id')->first())->id,
'user_id' => auth()->id(),
'to_email' => (string) ($recipients[0] ?? ''),
'to_recipients' => $recipients,
'subject' => (string) ($rendered['subject'] ?? $template->subject_template ?? ''),
@@ -107,6 +110,10 @@ public function maybeQueue(Activity $activity, bool $sendFlag = true): array
'client_case_id' => $clientCase?->id,
'contract_id' => $contract?->id,
]);
// If multiple recipients, keep to_email null to avoid implying a single primary recipient
if (count($recipients) > 1) {
$log->to_email = null;
}
$log->save();
$log->body()->create([
+91 -24
View File
@@ -94,38 +94,75 @@ public function sendFromLog(EmailLog $log): array
$transport = Transport::fromDsn($dsn);
$mailer = new SymfonyMailer($transport);
$fromAddr = (string) ($log->from_email ?: ($profile->from_address ?: ($username ?: (config('mail.from.address') ?? ''))));
// Resolve a valid From address without falling back to to_email
$fromAddrCandidates = [
(string) ($log->from_email ?? ''),
(string) ($profile->from_address ?? ''),
(string) ($username ?? ''),
(string) (config('mail.from.address') ?? ''),
];
$fromAddr = '';
foreach ($fromAddrCandidates as $cand) {
$cand = trim($cand);
if ($cand !== '' && filter_var($cand, FILTER_VALIDATE_EMAIL)) {
$fromAddr = $cand;
break;
}
}
$fromName = (string) ($log->from_name ?: ($profile->from_name ?: (config('mail.from.name') ?? config('app.name') ?? '')));
// Fallback From if still empty: use a no-reply at app host
if ($fromAddr === '') {
$appUrl = (string) (config('app.url') ?? '');
$hostPart = '';
if ($appUrl !== '') {
$parsed = @parse_url($appUrl);
$hostPart = is_array($parsed) ? (string) ($parsed['host'] ?? '') : '';
}
$domain = $hostPart !== '' ? $hostPart : 'localhost.localdomain';
$fromAddr = 'no-reply@'.$domain;
}
// Build email with safe Address instances (Symfony Address does not allow null name)
$fromAddress = $fromName !== ''
? new Address($fromAddr ?: $log->to_email, $fromName)
: new Address($fromAddr ?: $log->to_email);
$toAddress = (string) ($log->to_name ?? '') !== ''
? new Address($log->to_email, (string) $log->to_name)
: new Address($log->to_email);
? new Address($fromAddr, $fromName)
: new Address($fromAddr);
$email = (new Email)
->from($fromAddress)
->subject($subject);
// If multiple recipients are present, address to all; otherwise single to
// Address recipients: prefer multi-recipient list; fall back to validated single address
$toList = (array) ($log->to_recipients ?? []);
if (! empty($toList)) {
$addresses = [];
foreach ($toList as $addr) {
$addr = trim((string) $addr);
if ($addr !== '' && filter_var($addr, FILTER_VALIDATE_EMAIL)) {
$addresses[] = new Address($addr);
}
}
if (! empty($addresses)) {
$email->to(...$addresses);
} else {
$email->to($toAddress);
$addresses = [];
foreach ($toList as $addr) {
$addr = trim((string) $addr);
if ($addr !== '' && filter_var($addr, FILTER_VALIDATE_EMAIL)) {
$addresses[] = new Address($addr);
}
}
if (! empty($addresses)) {
$email->to(...$addresses);
} else {
$email->to($toAddress);
// Validate single to_email before using
$singleTo = trim((string) $log->to_email);
if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
}
$email->to(new Address($singleTo, (string) ($log->to_name ?? '')));
}
// Always BCC the sender mailbox if present and not already in To
$senderBcc = null;
if ($fromAddr !== '' && filter_var($fromAddr, FILTER_VALIDATE_EMAIL)) {
// Check duplicates against toList
$lowerTo = array_map(fn($v) => strtolower(trim((string) $v)), (array) ($log->to_recipients ?? [$log->to_email]));
if (! in_array(strtolower($fromAddr), $lowerTo, true)) {
$senderBcc = $fromAddr;
$email->bcc(new Address($senderBcc));
// Persist BCC for auditing
$log->bcc = [$senderBcc];
}
}
if (! empty($text)) {
@@ -139,6 +176,10 @@ public function sendFromLog(EmailLog $log): array
}
$mailer->send($email);
// Save log if we modified BCC
if (! empty($log->getAttribute('bcc'))) {
$log->save();
}
$headers = $email->getHeaders();
$messageIdHeader = $headers->get('Message-ID');
$messageId = $messageIdHeader ? $messageIdHeader->getBodyAsString() : null;
@@ -150,11 +191,24 @@ public function sendFromLog(EmailLog $log): array
if (! empty($toList)) {
$message->to($toList);
} else {
$singleTo = trim((string) $log->to_email);
if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
}
$toName = (string) ($log->to_name ?? '');
if ($toName !== '') {
$message->to($log->to_email, $toName);
$message->to($singleTo, $toName);
} else {
$message->to($log->to_email);
$message->to($singleTo);
}
}
// BCC the sender mailbox if resolvable and not already in To
$fromAddr = (string) ($log->from_email ?: (config('mail.from.address') ?? ''));
if ($fromAddr !== '' && filter_var($fromAddr, FILTER_VALIDATE_EMAIL)) {
$lowerTo = array_map(fn($v) => strtolower(trim((string) $v)), (array) ($log->to_recipients ?? [$log->to_email]));
if (! in_array(strtolower($fromAddr), $lowerTo, true)) {
$message->bcc($fromAddr);
$log->bcc = [$fromAddr];
}
}
$message->subject($subject);
@@ -172,11 +226,24 @@ public function sendFromLog(EmailLog $log): array
if (! empty($toList)) {
$message->to($toList);
} else {
$singleTo = trim((string) $log->to_email);
if ($singleTo === '' || ! filter_var($singleTo, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('No valid recipient email found for EmailLog #'.$log->id);
}
$toName = (string) ($log->to_name ?? '');
if ($toName !== '') {
$message->to($log->to_email, $toName);
$message->to($singleTo, $toName);
} else {
$message->to($log->to_email);
$message->to($singleTo);
}
}
// BCC the sender mailbox if resolvable and not already in To
$fromAddr = (string) ($log->from_email ?: (config('mail.from.address') ?? ''));
if ($fromAddr !== '' && filter_var($fromAddr, FILTER_VALIDATE_EMAIL)) {
$lowerTo = array_map(fn($v) => strtolower(trim((string) $v)), (array) ($log->to_recipients ?? [$log->to_email]));
if (! in_array(strtolower($fromAddr), $lowerTo, true)) {
$message->bcc($fromAddr);
$log->bcc = [$fromAddr];
}
}
$message->subject($subject);