我们的平台需要发送注册邮件,比如说我们有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; } |
我提供的只是一个思路,大家有这个需求,可以参考我的代码进行配置。