如何在运行时全局更改 SMTP 详细信息?

How can I change SMTP details globally at runtime?

我正在使用 Laravel 5.5。该网站的性质是 'multisite' 架构,其中多个 websites/domains 来自同一代码库 运行。

我在发送电子邮件时遇到了问题。我需要根据正在查看的网站更改 from nameaddress 以及 transport(SMTP 等)选项。我将这些详细信息存储在配置文件中。

最简单的方法是在我调用 Mail::send/Mail::queue 之前将这些详细信息拉入控制器并更新它们。然而,这又带来了 2 个问题:

  1. 每次我在代码中发送任何电子邮件时,都非常依赖于记住实际执行此操作。总之就是不遵守DRY
  2. 我将被迫使用 Mail::send 而不是 Mail::queue,因为队列从 [= 开始就不知道配置更新了31=]queued 仅在 processed .

我怎样才能以干净的方式实现我在这里想要做的事情?

我考虑过使用更新 SMTP 详细信息的自定义 class 扩展我的所有 'Mailable' classes,但看起来您无法更新 [= class启动后的48=]信息;您只能更新 from nameaddress.

我设法找到了一种方法。

我有我的可邮寄 class (ContactFormMailable) 扩展自定义 class,如下:

<?php

namespace CustomGlobal\Mail;

use CustomGlobal\Mail\CustomMailable;
use CustomGlobal\ContactForm;

class ContactFormMailable extends CustomMailable
{
    public $contact_form;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(ContactForm $contact_form)
    {
        $this->contact_form = $contact_form;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        $view = $this->get_custom_mail_view('contact_form', $this->contact_form);

        return $this->subject('Contact Form Enquiry')
            ->view($view);
    }
}

你会注意到我正在打电话 get_custom_mail_view。这在我的扩展 class 中,用于计算我需要用于邮件的视图和模板,具体取决于正在查看的网站。在这里我还设置了我的配置文件夹的位置。

<?php

namespace CustomGlobal\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Swift_Mailer;
use Swift_SmtpTransport;
use CustomGlobal\Website;
use CustomGlobal\Territory;

class CustomMailable extends Mailable
{
    use Queueable, SerializesModels;

    public $layout_view_to_serve;
    public $host_folder;

    /**
     * Override Mailable functionality to support per-user mail settings
     *
     * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
     * @return void
     */
    public function send(Mailer $mailer)
    {
        app()->call([$this, 'build']);

        $config = config($this->host_folder .'.mail');
        // Set SMTP details for this host
        $host = $config['host'];
        $port = $config['port'];
        $encryption  = $config['encryption'];

        $transport = new Swift_SmtpTransport( $host, $port, $encryption );
        $transport->setUsername($config['username']);
        $transport->setPassword($config['password']);
        $mailer->setSwiftMailer(new Swift_Mailer($transport));

        $mailer->send($this->buildView(), $this->buildViewData(), function ($message) use($config) {
            $message->from([$config['from']['address'] => $config['from']['name']]);
            $this->buildFrom($message)
                 ->buildRecipients($message)
                 ->buildSubject($message)
                 ->buildAttachments($message)
                 ->runCallbacks($message);
        });
    }

    /**
     * Calculate the template we need to serve.
     * $entity can be any object but it must contain a 
     * $website_id and $territory_id, as that is used
     * to calculate the path.
     */
    public function get_custom_mail_view($view_filename, $entity)
    {
        if(empty($view_filename)) {
            throw new Exception('The get_custom_mail_view method requires a view to be passed as parameter 1.');
        }

        if(empty($entity->website_id) || empty($entity->territory_id)) {
            throw new Exception('The get_custom_mail_view method must be passed an object containing a website_id and territory_id value.');
        }

        // Get the website and territory
        $website = Website::findOrFail($entity->website_id);
        $territory = Territory::findOrFail($entity->territory_id);

        $view_to_serve = false;
        $layout_view_to_serve = false;

        // Be sure to replace . with _, as Laravel doesn't play nice with dots in folder names
        $host_folder = str_replace('.', '_', $website->website_domain);
        $this->host_folder = $host_folder; // Used for mail config later

        /***
            Truncated for readability.  What's in this area isn't really important to this answer.
        ***/

        $this->layout_view_to_serve = $layout_view_to_serve;

        return $view_to_serve;
    }
}

重要的是要记住邮件可以排队。如果你这样做是另一种方式,比如在运行时设置配置,那么你会发现运行队列的进程没有 visibility/scope 你的运行时配置更改,你最终会发送电子邮件从您的默认值。

我找到了一些与此类似的答案,这对我有所帮助,但其中 none 完全有效,有些是 out-dated(Swift_SmtpTransport 自从那些答案)。

希望这对其他人有所帮助。