Symfony中如何在发送前获取TemplatedEmail的渲染后主体内容?
问题根因
TemplatedEmail 属于延迟渲染的邮件实现,仅配置htmlTemplate和context参数时,消息实体本身不会预先将模板编译为可发送的HTML内容。直接调用getBody()时,因为没有找到已组装的文本、HTML部件或附件,自然会抛出你遇到的A message must have a text or an HTML part or attachments.错误。
你之前考虑的在服务类中调用模板渲染方法获取编译内容的方案,完全适配存数据库的业务场景,不存在场景兼容性问题。
可落地实现方案
方案1:直接注入同源模板渲染器提前渲染
这是最直接、可控性最高的方式,Symfony 中邮件组件内部用来渲染模板的实例和独立调用的渲染逻辑完全同源,服务类中直接注入即可,拿到的渲染结果和实际发件内容没有差异。
示例代码:
// 在你的业务服务中注入渲染器,Symfony容器会自动完成依赖解析 public function __construct( private TemplateRendererInterface $mailerRenderer ) {} // 业务逻辑中提前渲染完整HTML $finalHtml = $this->mailerRenderer->render( $htmlTemplate, $context ); // 直接将$finalHtml存入数据库即可 // 后续组装TemplatedEmail时,甚至可以直接传入已渲染的HTML,避免框架重复编译 $email = (new TemplatedEmail()) ->from($from) ->to(...$to) ->subject($subject) ->html($finalHtml) ->context($context);
方案2:通过邮件发送钩子自动捕获渲染结果
如果不想手动写渲染逻辑,可以监听Mailer组件的MessageEvent事件。框架执行到这个事件钩子时,已经自动完成了TemplatedEmail的模板渲染、上下文变量替换的全流程,这个时机拿到的HTML内容和用户实际收到的内容100%匹配,不会出现手动渲染带来的参数偏差问题。
示例代码:
class RenderedEmailCaptureListener { public function onMailerMessage(MessageEvent $event): void { $message = $event->getMessage(); // 匹配待发送的TemplatedEmail类型消息 if ($message instanceof TemplatedEmail) { // 此时框架已完成模板渲染,直接取HTML内容即可 $renderedContent = $message->getHtmlBody(); // 在此处将内容写入数据库 } } }
注意事项
- 采用手动渲染方案时,一定要保证传入渲染器的模板路径、上下文变量,和后续给TemplatedEmail配置的参数完全一致,避免出现数据库存储内容和实际发送内容不匹配的问题。
getBody()方法仅用于读取已组装完成的邮件实体部件,不能主动触发模板渲染流程,直接调用一定会触发你遇到的错误。- 无论在控制器、命令脚本还是独立服务类中调用模板渲染器,返回的编译结果都和Mailer内部的渲染逻辑完全一致,不存在跨场景不兼容的问题。
内容的提问来源于stack exchange,提问作者TikTaZ
相关产品推荐
相关产品推荐

