You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony Mailer:捕获MessageSent事件时邮件上下文数据丢失

Symfony 6.3中SentMessageEvent无法获取TemplatedEmail上下文的解决方案

问题原因

Symfony在处理TemplatedEmail时,会在**邮件渲染完成后(发送前)**自动清空上下文、模板路径等数据——因为此时模板已经被编译成最终的HTML/文本内容,原始的上下文和模板信息已经完成使命,框架默认会清理这些冗余数据以节省内存,这是设计上的正常行为。这也是为什么你在MessageEvent(触发于渲染前)能拿到完整上下文,而SentMessageEvent(触发于发送后)里上下文为空的原因。

解决方法

方法1:通过MessageEvent提前存储上下文到邮件元数据

利用MessageEvent在渲染前触发的时机,把上下文数据存入邮件的元数据中,后续在SentMessageEvent里直接读取:

修改你的监听器代码:

public function onMessage(MessageEvent $event): void
{
    $message = $event->getMessage();
    if ($message instanceof \Symfony\Bridge\Twig\Mime\TemplatedEmail) {
        // 将上下文存入邮件元数据
        $message->setMetadata('email_context', $message->getContext());
    }
}

public function onMessageSent(SentMessageEvent $event): void
{
    $originalMessage = $event->getMessage()->getOriginalMessage();
    if ($originalMessage instanceof \Symfony\Bridge\Twig\Mime\TemplatedEmail) {
        // 从元数据中读取上下文
        $context = $originalMessage->getMetadata('email_context');
        // 这里可以使用$context了
        dump($context);
    }
}

方法2:添加自定义邮件头存储上下文

如果元数据方式不适用,也可以把上下文序列化为字符串后存入自定义邮件头,发送后再解析:

public function onMessage(MessageEvent $event): void
{
    $message = $event->getMessage();
    if ($message instanceof \Symfony\Bridge\Twig\Mime\TemplatedEmail) {
        // 序列化上下文为JSON字符串
        $contextJson = json_encode($message->getContext());
        // 添加自定义邮件头
        $message->getHeaders()->addTextHeader('X-Email-Context', $contextJson);
    }
}

public function onMessageSent(SentMessageEvent $event): void
{
    $originalMessage = $event->getMessage()->getOriginalMessage();
    if ($originalMessage instanceof \Symfony\Bridge\Twig\Mime\TemplatedEmail) {
        $contextHeader = $originalMessage->getHeaders()->get('X-Email-Context');
        if ($contextHeader) {
            // 解析JSON获取上下文
            $context = json_decode($contextHeader->getValue(), true);
            dump($context);
        }
    }
}

方法3:自定义TemplatedEmail子类(不推荐)

通过重写TemplatedEmail的updateBody方法,阻止框架清空上下文。这种方法可能会影响框架的默认行为,仅在特殊场景下使用:

  1. 创建自定义类:
namespace App\Mime;

use Symfony\Bridge\Twig\Mime\TemplatedEmail as BaseTemplatedEmail;

class TemplatedEmail extends BaseTemplatedEmail
{
    protected function updateBody(): void
    {
        // 调用父类方法完成模板渲染
        parent::updateBody();
        
        // 注释掉父类中清空上下文和模板的代码
        // 父类原方法最后会执行:$this->context = []; $this->htmlTemplate = null; $this->textTemplate = null;
        // 这里我们不执行这些清空操作,保留上下文
    }
}
  1. 发送邮件时使用这个自定义类:
use App\Mime\TemplatedEmail;

// ...

$email = (new TemplatedEmail())
    ->to('at@example.com')
    ->from('from@example.fr')
    ->subject('My subject')
    ->htmlTemplate('email/email.html.twig')
    ->context([
        'data' => [
            'key' => 'value'
        ]
    ]);

推荐方案

优先选择方法1或方法2,这两种方式利用了Symfony的扩展机制,不会破坏框架的默认行为,兼容性更好。方法3仅在必须保留原始TemplatedEmail对象结构时考虑使用。

内容的提问来源于stack exchange,提问作者beta-developper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:44:52