如何在Laravel 5.4中为排队的电子邮件设置动态SMTP数据?

时间:2017-07-26 13:07:20

标签: laravel swiftmailer

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

我制作了一个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重新启动队列,也有可能同时发送多个具有不同配置的电子邮件。

2 个答案:

答案 0 :(得分:2)

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

首先,我设置了一个特性,所以我可以在某些邮件上启用此功能:

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,
    ]);

热潮,让Laravel完成剩下的工作。

答案 1 :(得分:1)

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

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

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

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

简而言之,队列:listen解决了我的问题和另一个非常奇怪的数据库锁定问题。