Symfony Mailer循环发件时如何重置收件人及附件?
问题描述
在Symfony 6.x项目中,我需要通过命令批量发送邮件,因此编写了一个主命令循环调用发送邮件的子命令,但出现了邮件内容累积的问题。
主命令代码:
protected function execute(InputInterface $input, OutputInterface $output): int { ... foreach($params as $param) { $cmdParams = ['command' => 'my:first:command', 'arg1' => $param['arg1'], 'arg2' => $param['arg2']]; $cmdInput = new ArrayInput($cmdParams); $this->getApplication()->doRun($cmdInput, $output); } ... }
发送邮件的子命令代码:
public function __construct(readonly MailerInterface $mailer) { ... } protected function execute(InputInterface $input, OutputInterface $output): int { ... $email = (new Email()) ->from(...) ->replyTo(...) ->addTo([...]) ->addCc([...]) ->addPart(...) ; $this->mailer->send($email); ... }
具体问题:每次循环发送的邮件会累积之前的收件人、抄送地址及附件:
- 第一封邮件
- to: to-1@domain.tld
- cc: cc-1@domain.tld
- attachment: file-1.ext
- 第二封邮件
- to: to-1@domain.tld, to-2@domain.tld
- cc: cc-1@domain.tld, cc-2@domain.tld
- attachment: file-1.ext, file-2.ext
- 第n封邮件
- to: to-1@domain.tld, to-2@domain.tld, ..., to-nth@domain.tld
- cc: cc-1@domain.tld, cc-2@domain.tld, ..., cc-nth@domain.tld
- attachment: file-1.ext, file-2.ext, ..., file-nth.ext
请问该如何重置收件人、抄送地址及附件?
解决方案
问题根源是Symfony命令循环调用时,服务容器会复用子命令实例,若Email对象或相关数据未重新生成,就会导致内容累积。以下是两种解决方法:
方法一:子命令内每次创建全新Email实例
确保子命令的execute方法中,每次执行都新建Email对象,所有收件人、抄送、附件都从当前参数获取,不复用任何类属性或外部缓存数据:
protected function execute(InputInterface $input, OutputInterface $output): int { ... // 每次执行都创建全新的Email实例 $email = (new Email()) ->from('your-sender@domain.tld') ->replyTo('reply-to@domain.tld') ->addTo($input->getArgument('arg1')) // 从当前命令参数获取收件人 ->addCc($input->getArgument('arg2')) // 从当前命令参数获取抄送地址 ->addPart('邮件正文内容', 'text/plain') ->attachFromPath($this->getCurrentAttachmentPath($input)); // 动态获取当前邮件的附件路径 $this->mailer->send($email); ... }
方法二:主命令中使用CommandRunner确保子命令重新初始化
避免直接调用$this->getApplication()->doRun(),改用CommandRunner每次循环都重新加载子命令实例:
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Runner\CommandRunner; protected function execute(InputInterface $input, OutputInterface $output): int { ... $commandLoader = $this->getContainer()->get(CommandLoaderInterface::class); foreach($params as $param) { $cmdParams = ['command' => 'my:first:command', 'arg1' => $param['arg1'], 'arg2' => $param['arg2']]; $cmdInput = new ArrayInput($cmdParams); // 每次循环新建CommandRunner,保证子命令重新初始化 $runner = new CommandRunner($commandLoader); $runner->run($cmdInput, $output); } ... }
额外注意点
- 不要在子命令的类属性中存储
Email对象、收件人列表或附件路径,所有邮件相关数据都应在execute方法内动态生成。 - 若子命令依赖其他服务(如模板引擎),需确保这些服务不会缓存之前的请求数据,必要时在
execute方法末尾清理状态。
内容的提问来源于stack exchange,提问作者Nadim Bundhoo
相关产品推荐
相关产品推荐

