Yii2中Swiftmailer循环发件重复发给首个收件人问题求助
解决Yii2+Swiftmailer循环发邮件重复发给首个收件人的问题
看起来你遇到的是典型的邮件组件单例状态污染问题——本地生成邮件时收件人显示正常,但SMTP发送时因为Swiftmailer的内部连接状态没有被彻底重置,导致后续消息复用了初始的收件人信息。我帮你拆解问题和解决方案:
核心原因:单例Mailer的状态残留
Yii2的Yii::$app->mailer默认是单例组件,虽然每次调用compose()理论上会创建新的Swift_Message实例,但在SMTP发送场景下,底层的传输连接会保持复用,部分消息属性可能没有被完全覆盖,最终导致发送目标异常。
你提到本地生成邮件(比如用fileTransport)正常,是因为这种模式只生成文件,不会触发SMTP连接的状态保留;而实际SMTP发送时,持续的连接状态会干扰后续消息的发送目标。
解决方案一:每次发送创建全新Mailer实例
修改Invitation模型的sendMail方法,每次发送时创建独立的Mailer实例,彻底规避单例状态污染:
public function sendMail ($text){ $user = User::findOne($this->user_id); // 创建新的Mailer实例,而非复用全局单例 $mailer = Yii::createObject(Yii::$app->components['mailer']); $email = $mailer->compose('layouts/bulk-email', ['user' => $user, 'text' => $text]) ->setFrom('test@test.com') ->setTo($user['mail']) ->setSubject('Test subject'); return $email->send(); }
解决方案二:正确使用sendMultiple批量发送
你之前尝试sendMultiple时出错,大概率是因为没有创建独立的消息对象,而是复用了同一个实例(或错误地将send()的布尔返回值加入数组)。正确的批量发送方式如下:
// 控制器代码修改为批量模式 $messages = []; $errors = []; foreach ($invitations as $invitation_id) { $model = Invitation::findOne($invitation_id); if (!empty($model)) { $user = User::findOne($model->user_id); // 为每个收件人创建独立的Message对象 $message = Yii::$app->mailer->compose('layouts/bulk-email', ['user' => $user, 'text' => 'Test mail']) ->setFrom('test@test.com') ->setTo($user['mail']) ->setSubject('Test subject'); $messages[] = $message; } else { $errors[] = "Non existing $invitation_id?"; } } // 执行批量发送并统计结果 if (!empty($messages)) { $sendResults = Yii::$app->mailer->sendMultiple($messages); foreach ($sendResults as $index => $isSuccess) { if (!$isSuccess) { $targetMail = array_keys($messages[$index]->getTo())[0]; $errors[] = "Failed to send to: $targetMail"; } } }
这种方式不仅能避免状态污染,还能减少SMTP连接次数,提升发送效率。
额外排查点
- 循环中打印
$user['mail'],确认每个用户的邮箱确实不同,排除数据本身的问题。 - 检查Swiftmailer版本,旧版本可能存在类似的状态复用bug,建议升级到最新稳定版。
内容的提问来源于stack exchange,提问作者Nimer
相关产品推荐
相关产品推荐

