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

时间:2018-03-14 11:37:18

标签: laravel email laravel-5 laravel-5.5 swiftmailer

我使用的是Laravel 5.5。该网站的性质是一个多站点'从同一代码库运行多个网站/域的架构。

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

最简单的方法是在调用Mail::send / Mail::queue之前在控制器中提取这些详细信息并进行更新。但是,这会带来两个问题:

  1. 每次我在代码中发送任何电子邮件时,都非常依赖于记住实际执行此操作。简而言之,它不遵守DRY。
  2. 我被迫使用Mail::send代替Mail::queue,因为队列从 排队 仅限 处理
  3. 我怎样才能以干净的方式实现我的目标?

    我考虑过扩展我的所有“Mailable”'具有更新SMTP详细信息的自定义类的类,但看起来您可以在启动类后更新SMTP /传输信息;您只能更新from nameaddress

1 个答案:

答案 0 :(得分:2)

我设法找到了这样做的方法。

我的mailable类(ContactFormMailable)扩展了一个自定义类,如下所示:

<?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。这是在我的扩展类中,用于计算我需要用于邮件的视图和模板,具体取决于正在查看的网站。在这里我还设置了我的配置文件夹的位置。

<?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;
    }
}

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

我找到了一些与此相似的答案,这些答案帮助了我,但是没有一个完全有效,有些已经过时了(Swift_SmtpTransport自那些答案以后发生了很大的变化)。

希望这有助于其他人。