Laravel 9动态电子邮件配置



我来找你一个问题,在谷歌上搜索了几个小时后,我找不到解决方案。

我希望能够使用不同的SMTP电子邮件配置发送电子邮件,我可以在运行时添加或更改这些配置。我正在建立一个网站,为很多客户托管很多项目,我们需要能够代表他们发送电子邮件。我知道我可以在.env文件中设置不同的配置,但该解决方案还不够好,因为我想将配置保存在数据库中,以便轻松查询/更新等。

一个解决方案是使用本教程中的方法。它使用Swift mailer来创建一个返回新mailer对象的方法,但这在Laravel 9中似乎不起作用。显然,Swift mailer已不复存在,由Symfony mailer继任。不幸的是,我找不到像我刚才描述的那样使用新的Symfony Mailer的方法,尽管如果我能让它发挥作用,我当然更喜欢它。

我想知道Symfony Mailer是否可以使用同样的方法?当我使用与教程中相同的代码时,会出现以下错误:

Class "Swift_SmtpTransport" not found

我将类添加到命名空间中,还将语法从new Swift_SmtpTransport更改为Swift_SmtpTransport::newInstance,但这并没有解决错误。

如果有人有任何想法/建议,我将不胜感激!我真的没想到这么简单的事情会这么难。

Laravel 9.x使用的symfony/mailer可能会导致此冲突。相反,您可以使用mailer()选项动态更改邮件驱动程序。

Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));

您可以在文档中找到详细信息。

更新

我查看了symfony/mailer文档,我想这就是您想要的。您还可以使用动态SMTP详细信息动态构建自己的传输。

use SymfonyComponentMailerTransport;
use SymfonyComponentMailerMailer;
use SymfonyComponentMimeEmail;
$transport = Transport::fromDsn('smtp://localhost');
$mailer = new Mailer($transport);
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
//->cc('cc@example.com')
//->bcc('bcc@example.com')
//->replyTo('fabien@example.com')
//->priority(Email::PRIORITY_HIGH)
->subject('Time for Symfony Mailer!')
->text('Sending emails is fun again!')
->html('<p>See Twig integration for better HTML integration!</p>');
$mailer->send($email);

这对我有效…

在config/mail.php文件中,您可以定义不同的帐户,如下所示:

'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => 'smtp.google',
'port' => 465,
'encryption' => 'ssl',
'username' => 'youraccount@something.com',
'password' => 'password',
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
],
'OtherMailer' => [
'transport' => 'smtp',
'host' => 'smtp.gmail.com',
'port' => '465',
'encryption' => 'ssl',
'username' => 'otheraccount@something.com',
'password' => 'password',
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
],

接下来,在您的控制器或模型中,您可以设置要使用的邮件程序。

Mail::mailer('otherMailer')->to('person@something.com')->send(new SendLetter($sendMail));

希望这能有所帮助。

这些解决方案都不适用于我的生产(Forge)。出于某种原因,它继续使用环境中指定的FROM_USERNAME,尽管我在发送电子邮件之前试图覆盖它。我甚至从Laravel V8升级到了V9,因为邮件系统已经重新设计过了,但也不起作用。

我的解决方案不是最好的,但至少它有效。

将用户特定的SMTP凭据保存在"用户"表中。

生成可邮寄类(https://laravel.com/docs/10.x/mail#generating-mailables),并在调用sendEmail函数时将其用作第一个参数。envelope()函数在Mailable类中是相当多余的,所以您可以避免创建它。只需使用content()函数即可。

User.php

use SymfonyComponentMailerTransportDsn;
use SymfonyComponentMailerTransportSmtpEsmtpTransportFactory;
use SymfonyComponentMimeAddress;
use SymfonyComponentMimeEmail;
use IlluminateMailMailable;
public function sendEmail(Mailable $mailable, string $subject, string $to, string $cc = "")
{
$email = (new Email())->html($mailable->render())->to($to)->subject($subject)->from(new Address($this->mail_from_address, $this->name));
$mailer = new SymfonyComponentMailerMailer((new EsmtpTransportFactory)
->create(new Dsn('smtp', $this->mail_host, $this->mail_username, Crypt::decryptString($this->mail_password), $this->mail_port)));
$mailer->send($email);
}

通过以下操作发送电子邮件:

// send mail (using the User's SMTP credentials)
$user->sendEmail(new OrderShipped($this->order), "Subject", "to@example.com");

OrderShipped是Mailable类型。

Backstory

在我的公司网站上,几乎每个用户(员工)都有自己的电子邮件配置,有些型号也有自己的邮件配置。所有配置都保存到数据库表中,以便在运行时更改/添加。所有电子邮件都发送到队列,并由队列工作人员处理。

我将尝试提供一个干净的例子,说明我是如何在运行时更改配置的。我删掉了大部分不必要的代码,并更改了一些变量和文本,使其易于阅读。

逻辑

例如,如果我希望在经过身份验证的用户提交某个表格后,从他们的电子邮件中发送一封电子邮件,那么我会这样做:

$email_data = array(
'header'=>__('Info') , 
'subheader'=>__('This is an automated email. Do not reply.'),
'mail_to'=> 'test@test.test'
'content'=>   ...
);

sendEmailFromConfig(new mailTemplate($email_data), $auth_user->email_config_id);

sendEmailFromConfig-is函数来自helper类,因此可以从任何地方调用它。对于第一个参数,我传入mailTemplate,它派生自mailable,并带有我希望它使用的自定义数据。第二个参数是电子邮件配置id。

邮件模板类:

class mailTemplate extends Mailable
{
use Queueable, SerializesModels;
public $data;

public function __construct($data)
{
$this->data = $data; 
}
public function build()
{   
// optional data (for view)
$email_data['url'] = $this->data['url'];

$email_data['data1'] = $this->data['data1'];
$email_data['data2'] = $this->data['data2'];
$email_data['data3'] = $this->data['data3'];

// required
$email_data['view_name'] = 'emails.content.mailTemplate'; 
$email_data['reply_to'] = isset($this->data['reply_to']) ? $this->data['reply_to'] : '';
$email_data['cc'] = [];
$email_data['bcc'] = [];
$email_data['email_to'] = isset($this->data['email_to']);
$email_data['email_subject'] = $this->data['email_subject'];


logEmail($this->data); // Another helper function to log sent emails

return $this
->subject($email_data['email_subject'])
->to($email_data['email_to'])
->cc($email_data['cc'])
->bcc($email_data['bcc'])
->replyTo($email_data['reply_to'])
->view($email_data['view_name'], ['email_data' => $email_data]);
}

}

sendEmailFromConfig函数,以及其他不相关的东西,只是生成一个像这样的新作业:

function sendEmailFromConfig($data, $config_id) {
$config = EmailConfiguration::find($config_id); 
dispatch(new SendEmailJob($data, $config));
}

注意,$data值来自作为第一个参数传递的mailable。

SendEmailJob语法与laravel文档中的任何其他作业一样,但神奇的是:

$temp_config_name = 'smtp_rand_' . str::random(5); // every email is sent from different config to avoid weird bugs

config()->set([
'mail.mailers.' . $temp_config_name . '.transport' => 'smtp',
'mail.mailers.' . $temp_config_name . '.host' => $config->host,
'mail.mailers.' . $temp_config_name . '.port' => $config->port,
'mail.mailers.' . $temp_config_name . '.username' => $config->username,
'mail.mailers.' . $temp_config_name . '.password' => $config->password,
'mail.mailers.' . $temp_config_name . '.encryption' => $config->encryption,
'mail.mailers.' . $temp_config_name . '.from' => 
[
//FIXME TWO BOTTOM LINES MUST BE GIVEN A DO OVER PROBABLY
'address' => $config->from_address,
'name' => $config->from_name),
],
'mail.mailers.' . $temp_config_name . '.auth_mode' => $config->auth_mode,
]);
Mail::mailer($temp_config_name)->send($data); // sends email

这将在将作业发送到辅助服务(处理队列)之前在缓存中设置一个新的配置。这也应该在没有队列的情况下工作——在这种情况下,您应该首先尝试不使用$temp_config_name变量。

这个解决方案可能被认为是错误的,而且肯定不好看,但这是我设法使其正常工作的唯一方法。注意$temp_config_name在每个新作业中是如何更改的,即使从相同的电子邮件配置发送相同的数据也是如此——这修复了一个错误。错误在于,在配置中第一次成功发送电子邮件后,下一封电子邮件将不会发送出去。我不知道为什么会出现这个错误,但每次设置不同的配置名称都解决了这个问题。

我应该提到的是,每次发送电子邮件时,这些临时配置都会开始堆积在缓存中。我还没有时间找到一个好的解决方案,如果有人知道该怎么办,请告诉我(或者你是否有比我更好的解决方案)。我知道重新启动辅助服务会自动删除这些临时配置。我想一种方法是在每x个作业后重新启动工作者服务。

我还想说的是,我是PHP和Laravel的初学者,我确实认为可能有更好的解决方案,但我没能找到。我也想说,我遗漏了很多代码(例如,一些try-catch、日志函数调用、一些特定于应用程序的功能等),我只想展示核心逻辑。

谢谢,为我工作。这是作业文件的代码

$config = get_email_configuration($company->id);

$temp_config_name = 'smtp_rand_' . Str::random(5); // every email is sent from different config to avoid weird bugs
config()->set([
'mail.mailers.' . $temp_config_name . '.transport' => 'smtp',
'mail.mailers.' . $temp_config_name . '.host' => $config['host'],
'mail.mailers.' . $temp_config_name . '.port' => $config['port'],
'mail.mailers.' . $temp_config_name . '.username' => $config['username'],
'mail.mailers.' . $temp_config_name . '.password' => $config['password'],
'mail.mailers.' . $temp_config_name . '.encryption' => $config['encryption'],
'mail.mailers.' . $temp_config_name . '.from' => 
[
'address' => $config['from']['address'],
'name' => $config['from']['name'],
],
'mail.mailers.' . $temp_config_name . '.auth_mode' => true,
]);
Mail::mailer($temp_config_name)
->to($this->supervisorEmail)
->bcc($this->admin)
->send($email);

在Laravel 9+中,我能够根据每个用户的凭据,使用Symfony Mailer组件,并重复使用Laravel Mailable功能,如

$data = [
'name' => $name,
'document' => $document,
'generated_document' => $generated_document
];
$password = $user_pass; //Obtained from DB
$user_name = $user_name; //Obtained from DB
$mailer = Mail::mailer('smtp');
//SymfonyComponentMailerTransportSmtpEsmtpTransport
$transport = new EsmtpTransport( env('MAIL_HOST', 'localhost'), 465); 
$transport->setUsername($user_name)->setPassword($password);
$mailer->setSymfonyTransport($transport); //Using the component with the credentials for the current user
$mailer->to($email)->send(new SendSignedPDF($data)); //Sending the email using the Laravel facade plus a custom mailable

希望它能帮助到别人。

最新更新