Laravel 8 队列场景下通知Job配置自定义SMTP参数的实现问询
Laravel 多租户动态SMTP配置实现方案
核心逻辑说明
所有配置注入操作必须在队列任务的handle执行阶段完成,不能在控制器分发任务或通知类构造阶段修改配置:
- 队列任务是异步执行的,控制器阶段修改的配置仅作用于当前请求进程,对独立运行的队列worker进程无效
- 同一worker进程会顺序处理多个任务,只有在任务执行时动态修改/重置配置,才能避免不同租户的SMTP参数互相串用
具体实现步骤
1. 修改队列任务类 SendNewAnnouncementNotificationJob.php
在任务执行逻辑前先获取当前公告所属公司的SMTP配置,动态覆盖邮件服务配置,重置已初始化的邮件实例后再发送通知:
public function handle() { // 备份全局默认邮件配置,用于任务结束后恢复 $originalMailConfig = config('mail'); // 获取当前公告关联的公司SMTP配置,按需调整取数逻辑 $companySmtp = $this->announcement->company->smtp_config; try { // 动态覆盖邮件配置 config([ 'mail.mailers.smtp.host' => $companySmtp['host'], 'mail.mailers.smtp.port' => $companySmtp['port'], 'mail.mailers.smtp.username' => $companySmtp['username'], 'mail.mailers.smtp.password' => $companySmtp['password'], 'mail.mailers.smtp.encryption' => $companySmtp['encryption'], 'mail.from.address' => $companySmtp['from_address'], 'mail.from.name' => $companySmtp['from_name'], ]); // 清除已缓存的邮件实例,强制用新配置初始化 app()->forgetInstance('mailer'); app()->forgetInstance(\Illuminate\Mail\Mailer::class); // Laravel 7及以下使用SwiftMailer的话额外加一行 // app()->forgetInstance('swift.mailer'); // 原有通知发送逻辑 foreach ($this->announcement->subscribers as $subscriber) { Notification::route('mail', $subscriber->email) ->notify(new NewAnnouncement($this->announcement)); } } finally { // 恢复全局默认配置,避免影响同worker下的后续任务 config($originalMailConfig); app()->forgetInstance('mailer'); } }
2. 可选优化:独立自定义邮件器
如果不想修改默认SMTP配置,也可以动态创建独立的自定义邮件器,再指定通知用该邮件器发送:
// 在Job的handle方法中动态创建自定义邮件器 $customMailer = app('mail.manager')->mailer([ 'transport' => 'smtp', 'host' => $companySmtp['host'], 'port' => $companySmtp['port'], 'username' => $companySmtp['username'], 'password' => $companySmtp['password'], 'encryption' => $companySmtp['encryption'], 'from' => [ 'address' => $companySmtp['from_address'], 'name' => $companySmtp['from_name'], ], ]); // 修改NewAnnouncement通知类的toMail方法,指定使用对应邮件器 public function toMail($notifiable) { return (new MailMessage) ->mailer($this->customMailerName) // 传入自定义邮件器标识 ->from($this->announcement['sender_name']) ->subject('New Announcement at '.$this->announcement['pagename']) ->line($this->announcement['text']) ->line('Thank you for using our application!'); }
注意事项
- 通知类的构造方法是在控制器分发任务时执行的,不要在构造方法内修改配置,修改结果不会同步到队列执行环境
- 如果队列worker配置为单次执行单个任务(
--once参数),可以省略配置恢复的逻辑,更稳妥的方案还是保留恢复逻辑避免边界问题 - 数据库存储SMTP密码时建议做加密处理,不要明文存储
内容的提问来源于stack exchange,提问作者marius
相关产品推荐
相关产品推荐

