如何在 Symfony 5 中同时发送同步与异步邮件

3次阅读

如何在 Symfony 5 中同时发送同步与异步邮件

本教程详细介绍了如何在 symfony 5 应用中灵活地同时实现同步和异步邮件发送。通过创建自定义 messenger 消息和处理程序,并精确配置 messenger 路由,开发者可以区分处理不同场景下的邮件发送需求。文章涵盖了消息类、处理程序、messenger 配置以及在服务中调度邮件的完整实现,旨在提供一个结构清晰、易于理解的解决方案,避免了默认配置导致的所有邮件都被异步处理的问题。

在现代 Web 应用中,邮件发送是常见的需求,而根据邮件的重要性和时效性,可能需要同步发送(即时响应)或异步发送(后台处理,不阻塞用户请求)。Symfony 框架通过其 Mailer 和 Messenger 组件提供了强大的邮件处理能力。然而,默认的 Messenger 配置可能导致所有通过 MailerInterface::send() 发送的邮件都被路由到异步队列。本教程将指导您如何配置 Symfony 5 应用,以实现对邮件发送模式的精细控制。

理解 Symfony Mailer 与 Messenger 的默认行为

当 Symfony 的 Messenger 组件与 Mailer 组件集成时,MailerInterface::send() 方法实际上会创建一个 SymfonyComponentMailerMessengerSendEmailMessage 类型的消息,并将其调度到 Messenger 消息总线。如果在 framework.messenger.routing 配置中将此消息类型路由到异步传输,那么所有通过 MailerInterface::send() 发送的邮件都将变为异步处理。

为了实现同步和异步邮件的共存,我们需要避免将 SymfonyComponentMailerMessengerSendEmailMessage 直接路由到异步传输。相反,我们将为异步邮件创建一个自定义的消息类型和处理程序,并只将这个自定义消息类型路由到异步传输。

实现异步邮件发送

要实现异步邮件发送,我们需要定义一个自定义的消息类来承载邮件数据,一个消息处理程序来实际发送邮件,并相应地配置 Messenger。

1. 创建异步邮件消息类

首先,创建一个 php 类来封装异步邮件所需的所有数据,例如主题、内容、收件人等。这个类将作为 Messenger 消息在总线中传输。

// src/Message/EmailAsync.php namespace appMessage;  class EmailAsync {     private string $subject;     private string $bodyhtmlTemplate;     private ?string $bodyTextTemplate;     private string $recipient;     private array $context;     private string $senderEmail; // 假设发件人是固定的或通过配置获取      public function __construct(         string $subject,         string $bodyHtmlTemplate,         ?string $bodyTextTemplate,         string $recipient,         array $context = [],         string $senderEmail = 'noreply@example.com' // 示例发件人     ) {         $this->subject = $subject;         $this->bodyHtmlTemplate = $bodyHtmlTemplate;         $this->bodyTextTemplate = $bodyTextTemplate;         $this->recipient = $recipient;         $this->context = $context;         $this->senderEmail = $senderEmail;     }      public function getSubject(): string     {         return $this->subject;     }      public function getBodyHtmlTemplate(): string     {         return $this->bodyHtmlTemplate;     }      public function getBodyTextTemplate(): ?string     {         return $this->bodyTextTemplate;     }      public function getRecipient(): string     {         return $this->recipient;     }      public function getContext(): array     {         return $this->context;     }      public function getSenderEmail(): string     {         return $this->senderEmail;     } }

2. 创建异步邮件处理程序

接下来,创建一个消息处理程序来处理 EmailAsync 消息。当 Messenger 收到一个 EmailAsync 消息时,它将调用这个处理程序,由处理程序构建 TemplatedEmail 并使用 MailerInterface 发送。

// src/MessageHandler/EmailAsyncHandler.php namespace AppMessageHandler;  use AppMessageEmailAsync; use SymfonyComponentMimeAddress; use SymfonyBridgeTwigMimeTemplatedEmail; use SymfonyComponentMailerMailerInterface; use SymfonyComponentMessengerHandlerMessageHandlerInterface;  class EmailAsyncHandler implements MessageHandlerInterface {     protected MailerInterface $mailer;      public function __construct(MailerInterface $mailer)     {         $this->mailer = $mailer;     }      public function __invoke(EmailAsync $emailAsync): void     {         $emailToSend = (new TemplatedEmail())             ->from(new Address($emailAsync->getSenderEmail()))             ->to(new Address($emailAsync->getRecipient()))             ->subject($emailAsync->getSubject())             ->htmlTemplate($emailAsync->getBodyHtmlTemplate())             ->textTemplate($emailAsync->getBodyTextTemplate())             ->context($emailAsync->getContext());          $this->mailer->send($emailToSend);     } }

3. 配置 Messenger 路由

在 config/packages/messenger.yaml 或 config/packages/prod/messenger.yaml 中配置 Messenger,将 AppMessageEmailAsync 消息路由到异步传输。请确保您已经配置了一个异步传输(例如使用 MESSENGER_TRANSPORT_DSN 环境变量)。

# config/packages/messenger.yaml framework:     messenger:         # 配置异步传输,例如使用 rabbitmq, redis, 或 Doctrine         transports:             async: '%env(MESSENGER_TRANSPORT_DSN)%'          routing:             # 将自定义的 EmailAsync 消息路由到异步传输             'AppMessageEmailAsync': async             # 确保 SymfonyComponentMailerMessengerSendEmailMessage 没有被路由到 async             # 如果不在这里指定,它将默认走同步路径,这是我们希望的。             # 如果你需要一个完全同步的 MailerInterface::send(),请不要在此处添加 SendEmailMessage 的路由。

重要提示: 确保 SymfonyComponentMailerMessengerSendEmailMessage 没有被路由到 async。如果您的 Messenger 配置中没有关于 SymfonyComponentMailerMessengerSendEmailMessage 的路由条目,那么通过 MailerInterface::send() 发送的邮件将默认以同步方式处理,这正是我们实现同步邮件所需要的。

如何在 Symfony 5 中同时发送同步与异步邮件

小云雀

剪映出品的AI视频和图片创作助手

如何在 Symfony 5 中同时发送同步与异步邮件 1949

查看详情 如何在 Symfony 5 中同时发送同步与异步邮件

4. 在服务中调度异步邮件

现在,您可以在任何服务中注入 MessageBusInterface 并调度 EmailAsync 消息来发送异步邮件。

// src/Service/MailManagerAsync.php namespace AppService;  use AppMessageEmailAsync; use SymfonyComponentMessengerMessageBusInterface;  class MailManagerAsync {     protected MessageBusInterface $bus;     private string $defaultSenderEmail; // 可以通过构造函数注入或配置获取      public function __construct(MessageBusInterface $bus, string $defaultSenderEmail = 'noreply@example.com')     {         $this->bus = $bus;         $this->defaultSenderEmail = $defaultSenderEmail;     }      public function sendAsyncMessage(         string $subject,         string $htmlTemplate,         ?string $textTemplate,         string $to,         array $context = []     ): void {         $emailAsync = new EmailAsync(             $subject,             $htmlTemplate,             $textTemplate,             $to,             $context,             $this->defaultSenderEmail         );         $this->bus->dispatch($emailAsync);     } }

现在,当您调用 MailManagerAsync::sendAsyncMessage() 时,邮件将被封装成 EmailAsync 消息并发送到 Messenger 总线,然后由异步传输处理。

实现同步邮件发送

对于同步邮件发送,您可以继续使用 Symfony 提供的 MailerInterface 服务,直接调用其 send() 方法。由于我们没有将 SymfonyComponentMailerMessengerSendEmailMessage 路由到异步传输,因此这些邮件将立即发送,不会进入消息队列。

// src/Service/MailManagerSync.php namespace AppService;  use SymfonyComponentMimeAddress; use SymfonyBridgeTwigMimeTemplatedEmail; use SymfonyComponentMailerMailerInterface;  class MailManagerSync {     protected MailerInterface $mailer;     private string $defaultSenderEmail; // 可以通过构造函数注入或配置获取      public function __construct(MailerInterface $mailer, string $defaultSenderEmail = 'noreply@example.com')     {         $this->mailer = $mailer;         $this->defaultSenderEmail = $defaultSenderEmail;     }      public function sendSyncMessage(         string $subject,         string $htmlTemplate,         ?string $textTemplate,         string $to,         array $context = []     ): void {         $email = (new TemplatedEmail())             ->from(new Address($this->defaultSenderEmail))             ->to(new Address($to))             ->subject($subject)             ->htmlTemplate($htmlTemplate)             ->textTemplate($textTemplate)             ->context($context);          $this->mailer->send($email);     } }

当您调用 MailManagerSync::sendSyncMessage() 时,邮件将立即发送,而不会经过 Messenger 队列。

总结与注意事项

通过上述步骤,您已经在 Symfony 5 应用中成功实现了同步和异步邮件的并行发送:

  • 异步邮件: 通过自定义 EmailAsync 消息和 EmailAsyncHandler 处理程序,并将其路由到 Messenger 的异步传输。在服务中,您需要注入 MessageBusInterface 来调度这些消息。
  • 同步邮件: 直接使用 MailerInterface 服务发送 TemplatedEmail。由于 SymfonyComponentMailerMessengerSendEmailMessage 没有被路由到异步传输,这些邮件将直接发送。

注意事项:

  1. Messenger 传输配置: 确保您的 async 传输已正确配置并能够正常工作(例如,RabbitMQ 服务器正在运行,或者 Doctrine 传输的数据库连接正常)。
  2. 错误处理: 对于异步邮件,处理程序中的错误不会立即反馈给用户。您需要为 Messenger 配置失败策略(如重试、死信队列)来处理邮件发送失败的情况。
  3. 发件人地址: 示例代码中发件人地址是硬编码的,实际应用中应通过配置文件环境变量注入。
  4. 模板路径: htmlTemplate 和 textTemplate 应指向实际的 Twig 模板文件路径。
  5. 代码可维护性: 将同步和异步邮件发送逻辑封装在不同的服务中,有助于提高代码的可读性和可维护性。

遵循本教程的方法,您将能够更灵活地管理 Symfony 应用中的邮件发送流程,根据业务需求选择最合适的发送模式。

以上就是如何在 Symfony 5 中同时发送同步与异步邮件的详细内容,更多请关注php中文网其它相关文章!

text=ZqhQzanResources