Laravel 5.4 支持多个邮箱和Mail Driver并能够任意切换队列发送的方法

2018-12-11 16:22:05


参看地址点击  Laravel 5.4 支持多个邮箱和Mail Driver并能够任意切换队列发送的方法



我们的平台需要发送注册邮件,比如说我们有200个商户,每个商户有10000个用户,每个商户修改发送邮件配置,每个商户的发送邮件的服务商和账号密码都不相同。

但是现在Laravel 5.4只支持config中定义的一个mail driver,没有办法切换邮箱。

首先,我想到的时候重置config,通过配置config来发送邮件。

1
2
3
4
5
6
Config::set('mail.encryption','ssl');Config::set('mail.host','smtps.example.com');Config::set('mail.port','465');Config::set('mail.username','youraddress@example.com');Config::set('mail.password','password');Config::set('mail.from',  ['address' => 'youraddress@example.com' , 'name' => 'Your Name here']);

发送邮件有两种方法,一是即时发送,另外一种是队列发送。

1
2
Mail::send(new CustomMailable());Mail::queue(new CustomMailable());

即时发送没有问题,但是队列发送还是以默认的邮箱发送,失败。

后面我想到了创建一个新的Swift_Mailer实例并使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Backup your default mailer$backup = Mail::getSwiftMailer(); // Setup your gmail mailer$transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 465, 'ssl');$transport->setUsername('your_gmail_username');$transport->setPassword('your_gmail_password');// Any other mailer configuration stuff needed... $gmail = new Swift_Mailer($transport); // Set the mailer as gmailMail::setSwiftMailer($gmail); // Send your messageMail::send(); // Restore your original mailerMail::setSwiftMailer($backup);

即时发送成功,队列发送失败。

通过查看Mailer的queue方法,它是依赖注入了一个全局单例的mailer,那意味着queue使用的mailer是系统启动时就创建的单例,后续修改了config,这个单例不会被重建。

原来队列正在单独的进程上运行,因此Mail :: setSwiftMailer完全不会影响它。

它只是提取默认设置。因此,配置更改必须在电子邮件的初始化进行修改,而不是在排队时修改。

我的解决方案是扩展Mailable类如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
< ?php
 namespace App\Mail; use Illuminate\Container\Container;use Illuminate\Contracts\Mail\Mailer;use Illuminate\Mail\Mailable;use Swift_Mailer;use Swift_SmtpTransport; class ConfigurableMailable extends Mailable{
    /**
     * Override Mailable functionality to support per-user mail settings
     *
     * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
     * @return void
     */
    public function send(Mailer $mailer)
    {
        $host      = env('MAIL_HOST');
        $port      = env('MAIL_PORT');
        $security  = env('MAIL_ENCRYPTION');
        // 配置邮件
        $transport = Swift_SmtpTransport::newInstance( $host, $port, $security);
        // 注意看,这里是修改为用户邮箱的配置,$this->config是用户传的数据。
        $transport->setUsername($this->config->username);
        $transport->setPassword($this->config->password);
        $mailer->setSwiftMailer(new Swift_Mailer($transport));         // 重启Swift_SmtpTransport中ssl的连接要不然会报错
        $mailer->getSwiftMailer()->getTransport()->stop(); 
        Container::getInstance()->call([$this, 'build']);         $mailer->send($this->buildView(), $this->buildViewData(), function ($message) {
            $this->buildFrom($message)
                 ->buildRecipients($message)
                 ->buildSubject($message)
                 ->buildAttachments($message)
                 ->runCallbacks($message);
        });
    }}

然后改为CustomMail扩展ConfigurableMailable而不是Mailable:

1
class CustomMail extends ConfigurableMailable {}

这可以确保Mail::queue(new CustomMail())在发送之前,即使队列中也能设置每个用户的邮件设置。

当然,你将不得不在当前用户注入CustomMail的时候Mail::queue(new CustomMail(Auth::user()))

虽然这可能不是理想的解决方案,如果尝试发送批量电子邮件,最好配置一次邮件,而不是每发送一封邮件再进行配置。

但是上面的代码解决了问题,目前我们就是用上面的代码来进行邮件发送的。

下面来看看我来发送邮件的mail类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
< ?php
 namespace App\Mail; use Illuminate\Bus\Queueable;use Illuminate\Mail\Mailable;use Illuminate\Queue\SerializesModels;use Illuminate\Contracts\Queue\ShouldQueue; use App\Email_record; class Template extends ConfigurableMailable{
    use Queueable, SerializesModels;     public $config;
    public $data;
    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($config,$data)
    {
        // 这里是传给父类的参数
        $this->config    =   $config;
        $this->data      =   $data;
    }     /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        // 保存发送的邮件
        $email_record=new Email_record;
        $email_record->site_id=$this->data->site_id;
        $email_record->code=$this->data->code;
        $email_record->type=$this->data->type;
        $email_record->title=$this->data->title;
        $email_record->from=$this->data->from;
        $email_record->to=$this->data->to;
        $email_record->content=$this->data->content;
        $email_record->save();
        // 发送邮件
        return $this->from($this->config->from,$this->config->name)->subject($this->data->title)->view('emails.template');
    }}

再来看看我们发送邮件的控制器。

1
2
3
4
5
6
7
8
9
10
11
12
// 根据返回的地址设置配置选项config(['mail.from.address' => $username]);config(['mail.username'     => $username]);config(['mail.password'     => $password]);   // 发送给指定的邮箱    $to=$username; try{
    Mail::to($to)->send(new Test());
    $result['msg']=1;}catch(\Exception $e){
    $result['msg']=2;       }

我提供的只是一个思路,大家有这个需求,可以参考我的代码进行配置。

  • 2019-05-29 15:47:51

    撤销commit

    在git push的时候,有时候我们会想办法撤销git commit的内容

  • 2019-06-03 00:07:32

    Android程序Crash时的异常上报

    家都知道,android应用不可避免的会发生crash,无论你的程序写的多完美,总是无法完全避免crash的发生,可能是由于android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络状况。当crash发生时,系统会kill掉你的程序,表现就是闪退或者程序已停止运行,这对用户来说是很不友好的,也是开发者所不愿意看到的,更糟糕的是,当用户发生了crash,开发者却无法得知程序为何crash,即便你想去解决这个crash,但是由于你无法知道用户当时的crash信息,所以你也无能为力。是否真的这样呢,其实android中有处理这类问题的方法,请看下面Thread类中的一个方法#setDefaultUncaughtExceptionHandler

  • 2019-06-04 16:40:30

    为了美观当网页图片不存在时不显示叉叉图片

    当在页面显示的时候,万一图片被移动了位置或者丢失的话,将会在页面显示一个带X的图片,很是影响用户的体验。即使使用alt属性给出了”图片XX”的提示信息,也起不了多大作用。

  • 2019-06-04 17:38:44

    PHP时间转换今天昨天前天几天前

    实际情况应该是,昨天任何时间都算一天前,前天任意时间都算2天前,所以自己琢磨了一番,去动态更新时间与今天23:59:59相差的时间秒数与86400(24 x 3600)相除后,向下取整,这样就得到了相差的天数,比如昨天00:00~昨天23:59:59的任何时间与今天的23:59:59,都相差 86400~(86400 x 2) 天,也就是2天。

  • 2019-06-04 17:44:14

    【VUE】图片预览放大缩小插件

    在看项目时,突然看到预览图片的弹窗,感觉好僵硬,不能放大,不能切换,于是便在网上找下关于图片预览的插件,有找到三个插件,具体的优劣势的看自己的使用吧,我目前只是在电脑查看效果,分别是viewerjs插件、基于photoswipe的vue-photo-preview插件以及vue-picture-preview插件 作者:北极星丶超帅的 链接:https://www.jianshu.com/p/e3350aa1b0d0 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。