【问题标题】:How to set dynamic SMTP data in Laravel 5.4 for queued emails?如何在 Laravel 5.4 中为排队的电子邮件设置动态 SMTP 数据?
【发布时间】:2018-01-01 20:02:58
【问题描述】:

在我的应用程序中,每个用户都可以使用自己的 SMTP 服务器。因此必须提供配置。我正在使用 Laravel 通知发送电子邮件。如果我不使用队列(这意味着同步),则没有问题。

我创建了一个 CustomNotifiable Trait:

        config([
            'mail.host' => $setting->smtp_host,
            'mail.port' => $setting->smtp_port,
            'mail.username' => $setting->smtp_username,
            'mail.password' => $setting->smtp_password,
            'mail.encryption' => $setting->smtp_encryption,
            'mail.from.address' => $setting->smtp_from_address,
            'mail.from.name' => $setting->smtp_from_name,
        ]);

        (new \Illuminate\Mail\MailServiceProvider(app()))->register();

之后,我恢复原来的配置:

        config([
            'mail' => $originalMailConfig
        ]);

        (new \Illuminate\Mail\MailServiceProvider(app()))->register();

到目前为止没问题。 但是,如果它已排队,即使提供了任何其他 SMTP 配置,也只会将启动队列工作程序后的第一个配置用于所有后续电子邮件。 config/mail.php 中的默认配置将被覆盖。但这只适用于第一次。

我在 AppServiceProvider::boot 方法中做了(SMTP 配置存储在通知中):

    Queue::before(function (JobProcessing $event) {

        // Handle queued notifications before they get executed
        if (isset($event->job->payload()['data']['command']))
        {
            $payload = $event->job->payload();
            $command = unserialize($payload['data']['command']);

            // setting dynamic SMTP data if required
            if (isset($command->notification->setting))
            {
                config([
                    'mail.host' => $command->notification->setting->smtp_host,
                    'mail.port' => $command->notification->setting->smtp_port,
                    'mail.username' => $command->notification->setting->smtp_username,
                    'mail.password' => $command->notification->setting->smtp_password,
                    'mail.encryption' => $command->notification->setting->smtp_encryption,
                    'mail.from.address' => $command->notification->setting->smtp_from_address,
                    'mail.from.name' => $command->notification->setting->smtp_from_name,
                ]);

                (new \Illuminate\Mail\MailServiceProvider(app()))->register();
            }
        }

    });

当然,原来的配置会恢复:

    Queue::after(function (JobProcessed $event) use ($originalMailConfig) {

        $payload = $event->job->payload();
        $command = unserialize($payload['data']['command']);

        // restore global mail settings
        if (isset($command->notification->setting))
        {
            config([
                'mail' => $originalMailConfig
            ]);

            (new \Illuminate\Mail\MailServiceProvider(app()))->register();
        }

    });

看起来,Swift Mailer 有一个缓存或类似的东西。我注册了一个新的 MailServiceProvider,它应该只是替换旧的。因此,如果我使用新的 SMTP 数据设置配置,新注册的提供者应该接受它们。记录配置甚至在 TransportManager 中显示,在发送邮件之前设置了正确的 SMTP 数据,但邮件是使用第一个设置的配置发送的。

我找到了这个帖子并尝试了链接的解决方案,但结果相同:How to set dynamic SMTP details laravel

所以我需要一种方法来覆盖 Services / ServiceProvider / SMTP 配置。即使 Supervisor 重新启动队列,也有可能同时发送多封具有不同配置的电子邮件。

【问题讨论】:

    标签: laravel swiftmailer


    【解决方案1】:

    经过大量研究,我偶然发现了不同的队列命令。我尝试了 queue:listen (在 Laravel 5.4 文档中没有描述)而不是 queue:work 并且问题消失了。

    当然,这并不能真正解释所描述的行为,但幸运的是这并不重要,因为我可以接受这种解决方案/解决方法。

    另一个奇怪的行为是,队列工作者有时会因为数据库被锁定而抛出异常。不知道发生这种情况的时间或原因。

    这篇文章解释了一点,为什么事情会发生:What is the difference between queue:work --daemon and queue:listen

    简而言之,queue:listen 解决了我的问题以及另一个非常奇怪的数据库锁问题。

    【讨论】:

      【解决方案2】:

      在 Laravel 5.4+ 中,我看到 Mailer 类是一个单例,它包含一个 MailTransport 类,它负责 SMTP 邮件的配置,也是一个单例;我只需要使用以下方法覆盖配置:

      首先,我设置了一个 trait,这样我就可以在某些邮件上启用此功能:

      trait MailSenderChangeable
      {
          /**
           * @param array $settings
           */
          public function changeMailSender($settings)
          {
              $mailTransport = app()->make('mailer')->getSwiftMailer()->getTransport();
              if ($mailTransport instanceof \Swift_SmtpTransport) {
                  /** @var \Swift_SmtpTransport $mailTransport */
                  $mailTransport->setUsername($settings['email']);
                  $mailTransport->setPassword($settings['password']);
              }
          }
      }
      

      然后,在您的邮件类的 build() 方法中,您可以利用上述特征并调用:

          $this->changeMailSender([
              'email'=>$this->company->email,
              'password'=>$this->company->email_password,
          ]);
      

      Boom,剩下的交给 Laravel。

      【讨论】:

      • 这样可以设置端口和主机,就像邮箱和密码一样吗?
      • 当然,请阅读课程的实现。 <.project>/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php 这些方法是 $mailTransport->setHost('host'), $mailTransport->setPort('1234')
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多