Appearance
邮件
简介
发送邮件不必很复杂。Laravel 提供了一个简洁、简单的邮件 API,该 API 由流行的 Symfony Mailer 组件提供支持。Laravel 和 Symfony Mailer 为通过 SMTP、Mailgun、Postmark、Resend、Amazon SES 和 sendmail
发送邮件提供了驱动程序,允许你通过本地或基于云的服务快速开始发送邮件。
配置
Laravel 的邮件服务可以通过应用程序的 config/mail.php
配置文件进行配置。此文件中配置的每个邮件程序都可以有自己独特的配置,甚至可以有自己独特的"传输",允许你的应用程序使用不同的邮件服务来发送某些电子邮件消息。例如,你的应用程序可能使用 Postmark 发送交易性电子邮件,而使用 Amazon SES 发送批量电子邮件。
在你的 mail
配置文件中,你会找到一个 mailers
配置数组。此数组包含 Laravel 支持的每个主要邮件驱动程序/传输的示例配置条目,而 default
配置值确定当你的应用程序需要发送电子邮件消息时默认使用哪个邮件程序。
驱动程序 / 传输前提条件
基于 API 的驱动程序(如 Mailgun、Postmark、Resend 和 MailerSend)通常比通过 SMTP 服务器发送邮件更简单、更快速。如果可能,我们建议你使用这些驱动程序之一。
Mailgun 驱动程序
要使用 Mailgun 驱动程序,请通过 Composer 安装 Symfony 的 Mailgun Mailer 传输:
shell
composer require symfony/mailgun-mailer symfony/http-client
接下来,在应用程序的 config/mail.php
配置文件中将 default
选项设置为 mailgun
,并将以下配置数组添加到 mailers
数组中:
php
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
配置应用程序的默认邮件程序后,在 config/services.php
配置文件中添加以下选项:
php
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
'scheme' => 'https',
],
如果你没有使用美国 Mailgun 区域,你可以在 services
配置文件中定义你区域的端点:
php
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
'scheme' => 'https',
],
Postmark 驱动程序
要使用 Postmark 驱动程序,请通过 Composer 安装 Symfony 的 Postmark Mailer 传输:
shell
composer require symfony/postmark-mailer symfony/http-client
接下来,在应用程序的 config/mail.php
配置文件中将 default
选项设置为 postmark
。配置应用程序的默认邮件程序后,确保 config/services.php
配置文件包含以下选项:
php
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
如果你想指定给定邮件程序应使用的 Postmark 消息流,你可以在应用程序的 config/mail.php
配置文件中的邮件程序配置数组中添加 message_stream_id
配置选项。
php
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],
这样,你还可以设置使用不同消息流的多个 Postmark 邮件程序。
Resend 驱动程序
要使用 Resend 驱动程序,请通过 Composer 安装 Resend 的 PHP SDK:
shell
composer require resend/resend-php
接下来,在应用程序的 config/mail.php
配置文件中将 default
选项设置为 resend
。配置应用程序的默认邮件程序后,确保 config/services.php
配置文件包含以下选项:
php
'resend' => [
'key' => env('RESEND_KEY'),
],
SES 驱动程序
要使用 Amazon SES 驱动程序,你首先需要安装 Amazon AWS SDK for PHP。你可以通过 Composer 包管理器安装此库:
shell
composer require aws/aws-sdk-php
接下来,在 config/mail.php
配置文件中将 default
选项设置为 ses
,并验证你的 config/services.php
配置文件包含以下选项:
php
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
要利用 AWS 临时凭据 通过会话令牌进行交互,你可以在应用程序的 SES 配置中添加 token
键:
php
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'token' => env('AWS_SESSION_TOKEN'),
],
要与 SES 的 订阅管理功能 进行交互,你可以在邮件消息的 headers
方法返回的数组中返回 X-Ses-List-Management-Options
标头:
php
/**
* 获取消息标头。
*/
public function headers(): Headers
{
return new Headers(
text: [
'X-Ses-List-Management-Options' => 'contactListName=MyContactList;topicName=MyTopic',
],
);
}
如果你想定义 Laravel 在发送电子邮件时应传递给 AWS SDK 的 SendEmail
方法的 其他选项,你可以在 ses
配置中定义一个 options
数组:
php
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'options' => [
'ConfigurationSetName' => 'MyConfigurationSet',
'EmailTags' => [
['Name' => 'foo', 'Value' => 'bar'],
],
],
],
MailerSend 驱动程序
MailerSend,一种交易性电子邮件和短信服务,维护了他们自己的基于 API 的邮件驱动程序,用于 Laravel。包含驱动程序的包可以通过 Composer 包管理器安装:
shell
composer require mailersend/laravel-driver
安装包后,将 MAILERSEND_API_KEY
环境变量添加到应用程序的 .env
文件中。此外,MAIL_MAILER
环境变量应定义为 mailersend
:
shell
MAIL_MAILER=mailersend
MAIL_FROM_ADDRESS=app@yourdomain.com
MAIL_FROM_NAME="App Name"
MAILERSEND_API_KEY=your-api-key
最后,在应用程序的 config/mail.php
配置文件中的 mailers
数组中添加 MailerSend:
php
'mailersend' => [
'transport' => 'mailersend',
],
要了解有关 MailerSend 的更多信息,包括如何使用托管模板,请参阅 MailerSend 驱动程序文档。
故障转移配置
有时,你配置的外部服务可能无法发送应用程序的邮件。在这种情况下,定义一个或多个备用邮件发送配置可能会有用,这些配置将在主发送驱动程序不可用时用于发送。
要实现这一点,你应该在应用程序的 mail
配置文件中定义一个使用 failover
传输的邮件程序。你应用程序的 failover
邮件程序的配置数组应包含一个 mailers
数组,该数组引用了应该用于发送的配置邮件程序的顺序:
php
'mailers' => [
'failover' => [
'transport' => 'failover',
'mailers' => [
'postmark',
'mailgun',
'sendmail',
],
],
// ...
],
一旦定义了故障转移邮件程序,你应该将此邮件程序设置为应用程序默认使用的邮件程序,方法是将其名称指定为应用程序 mail
配置文件中 default
配置键的值:
php
'default' => env('MAIL_MAILER', 'failover'),
轮询配置
roundrobin
传输允许你在多个邮件程序之间分配邮件发送工作负载。要开始使用,在应用程序的 mail
配置文件中定义一个使用 roundrobin
传输的邮件程序。你应用程序的 roundrobin
邮件程序的配置数组应包含一个 mailers
数组,该数组引用了应该用于发送的配置邮件程序:
php
'mailers' => [
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
// ...
],
一旦定义了轮询邮件程序,你应该将此邮件程序设置为应用程序默认使用的邮件程序,方法是将其名称指定为应用程序 mail
配置文件中 default
配置键的值:
php
'default' => env('MAIL_MAILER', 'roundrobin'),
轮询传输会从配置邮件程序列表中随机选择一个邮件程序,然后对每个后续电子邮件切换到下一个可用的邮件程序。与 failover
传输相反,failover
传输可以实现*高可用性,而 roundrobin
传输提供负载均衡*。
生成可邮寄类
在构建 Laravel 应用程序时,每种类型的电子邮件都由应用程序表示为一个"可邮寄"类。这些类存储在 app/Mail
目录中。如果你在应用程序中看不到这个目录,不要担心,因为当你使用 make:mail
Artisan 命令生成你的第一个可邮寄类时,它会为你生成:
shell
php artisan make:mail OrderShipped
编写可邮寄类
一旦你生成了一个可邮寄类,打开它以便我们探索其内容。可邮寄类的配置是在几个方法中完成的,包括 envelope
、content
和 attachments
方法。
envelope
方法返回一个 Illuminate\Mail\Mailables\Envelope
对象,该对象定义了消息的主题,有时还定义了消息的收件人。content
方法返回一个 Illuminate\Mail\Mailables\Content
对象,该对象定义了用于生成消息内容的 Blade 模板。
配置发送者
使用信封
首先,让我们探讨如何配置电子邮件的发送者。或者说,电子邮件是"从"谁发送的。有两种方式可以配置发送者。首先,你可以在消息的信封上指定"from"地址:
php
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
/**
* 获取消息信封。
*/
public function envelope(): Envelope
{
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
subject: 'Order Shipped',
);
}
如果你愿意,你还可以指定 replyTo
地址:
php
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
replyTo: [
new Address('taylor@example.com', 'Taylor Otwell'),
],
subject: 'Order Shipped',
);
使用全局 from
地址
但是,如果你的应用程序对所有电子邮件使用相同的"from"地址,在每个生成的可邮寄类中添加它可能会变得很麻烦。相反,你可以在应用程序的 config/mail.php
配置文件中指定全局"from"地址。如果在可邮寄类中没有指定其他"from"地址,则将使用此地址:
php
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
此外,你还可以在应用程序的 config/mail.php
配置文件中定义全局"reply_to"地址:
php
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
配置视图
在可邮寄类的 content
方法中,你可以定义 view
,或者在渲染电子邮件内容时应使用哪个模板。由于每封电子邮件通常使用 Blade 模板 来渲染其内容,因此你在构建电子邮件的 HTML 时拥有 Blade 模板引擎的全部功能和便利性:
php
/**
* 获取消息内容定义。
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
NOTE
你可能希望创建一个 resources/views/emails
目录来存放所有的电子邮件模板;但是,你可以随意将它们放在 resources/views
目录的任何位置。
纯文本电子邮件
如果你想定义电子邮件的纯文本版本,你可以在创建消息的 Content
定义时指定纯文本模板。与 view
参数一样,text
参数应该是一个模板名称,该名称将用于渲染电子邮件的内容。你可以同时定义 HTML 和纯文本版本的消息:
php
/**
* 获取消息内容定义。
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
}
为了清晰起见,html
参数可以用作 view
参数的别名:
php
return new Content(
html: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
视图数据
通过公共属性
通常,你会希望将一些数据传递给你的视图,以便在渲染电子邮件的 HTML 时使用。有两种方式可以使数据可用于视图。首先,任何在可邮寄类上定义为公共属性的数据都将自动可用于视图。因此,例如,你可以通过可邮寄类的构造函数传递数据,并将该数据设置为类上定义的公共属性:
php
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 创建一个新的消息实例。
*/
public function __construct(
public Order $order,
) {}
/**
* 获取消息内容定义。
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
}
一旦数据设置为公共属性,它将自动可用于你的视图,因此你可以像访问任何其他 Blade 模板中的数据一样访问它:
php
<div>
Price: {{ $order->price }}
</div>
通过 with
参数
如果你想在发送给模板之前自定义电子邮件数据的格式,你可以手动通过 Content
定义的 with
参数将数据传递给视图。通常,你仍然会通过可邮寄类的构造函数传递数据;但是,你应该将此数据设置为 protected
或 private
属性,这样数据就不会自动可用于模板:
php
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 创建一个新的消息实例。
*/
public function __construct(
protected Order $order,
) {}
/**
* 获取消息内容定义。
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
with: [
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
],
);
}
}
一旦数据传递给 with
方法,它将自动可用于你的视图,因此你可以像访问任何其他 Blade 模板中的数据一样访问它:
php
<div>
Price: {{ $orderPrice }}
</div>
附件
要向电子邮件添加附件,你可以将附件添加到消息的 attachments
方法返回的数组中。首先,你可以通过 Attachment
类的 fromPath
方法提供文件路径来添加附件:
php
use Illuminate\Mail\Mailables\Attachment;
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file'),
];
}
在向消息添加附件时,你还可以指定附件的显示名称和/或 MIME 类型,使用 as
和 withMime
方法:
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
从磁盘附加文件
如果你在应用程序的 文件系统磁盘 上存储了文件,你可以使用 fromStorage
附件方法将其附加到电子邮件上。
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file'),
];
}
当然,你也可以指定附件的名称和 MIME 类型:
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
如果你需要指定除默认磁盘之外的存储磁盘,可以使用 fromStorageDisk
方法:
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorageDisk('backblaze', '/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
原始数据附件
fromData
附件方法可用于将原始字节字符串作为附件附加到电子邮件上。例如,如果你在内存中生成了 PDF,并希望将其附加到电子邮件上而不将其写入磁盘,你可以使用此方法。fromData
方法接受一个闭包,该闭包解析原始数据字节以及应分配给附件的名称:
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromData(fn () => $this->pdf, 'Report.pdf')
->withMime('application/pdf'),
];
}
内联附件
将图像嵌入到电子邮件中通常很麻烦;但是,Laravel 提供了一种方便的方式来将图像附加到电子邮件上。要嵌入内联图像,在电子邮件模板上使用 $message
变量的 embed
方法。Laravel 会自动将 $message
变量提供给所有电子邮件模板,因此你无需手动传递它:
blade
<body>
Here is an image:
<img src="{{ $message->embed($pathToImage) }}">
</body>
WARNING
纯文本消息模板不使用内联附件,因此 $message
变量在纯文本消息模板中不可用。
嵌入原始数据附件
如果你已经有一个原始图像数据字符串要嵌入到电子邮件模板中,你可以在 $message
变量上调用 embedData
方法。调用 embedData
方法时,你需要提供应分配给嵌入图像的文件名:
blade
<body>
Here is an image from raw data:
<img src="{{ $message->embedData($data, 'example-image.jpg') }}">
</body>
可附加对象
虽然通过简单的字符串路径附加文件到消息通常足够,但在许多情况下,消息中的可附加实体由应用程序表示为类。例如,如果你的应用程序正在附加一张照片到一封消息上,你的应用程序可能还有一个表示该照片的 Photo
模型。在这种情况下,不是很方便吗,只需将 Photo
模型传递给 attach
方法?可附加对象允许你这样做。
要开始,在应用程序的模型上实现 Illuminate\Contracts\Mail\Attachable
接口。此接口规定你的类定义一个 toMailAttachment
方法,该方法返回一个 Illuminate\Mail\Attachment
实例:
php
<?php
namespace App\Models;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Attachment;
class Photo extends Model implements Attachable
{
/**
* 获取模型的可附加表示形式。
*/
public function toMailAttachment(): Attachment
{
return Attachment::fromPath('/path/to/file');
}
}
一旦你定义了可附加对象,你可以在构建电子邮件消息时从 attachments
方法返回该对象的实例:
php
/**
* 获取消息的附件。
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [$this->photo];
}
当然,附件数据可能存储在远程文件存储服务(如 Amazon S3)上。因此,Laravel 还允许你从应用程序的 文件系统磁盘 生成附件实例:
php
// 从默认磁盘创建附件...
return Attachment::fromStorage($this->path);
// 从特定磁盘创建附件...
return Attachment::fromStorageDisk('backblaze', $this->path);
此外,你还可以使用内存中的数据创建附件实例。要实现这一点,请为 fromData
方法提供一个闭包。该闭包应返回表示附件的原始数据:
php
return Attachment::fromData(fn () => $this->content, 'Photo Name');
Laravel 还提供了其他方法,你可以使用这些方法来自定义你的附件。例如,你可以使用 as
和 withMime
方法来自定义文件的名称和 MIME 类型:
php
return Attachment::fromPath('/path/to/file')
->as('Photo Name')
->withMime('image/jpeg');
标头
有时你可能需要向传出消息附加其他标头。例如,你可能需要设置自定义 Message-Id
或其他任意文本标头。
要实现这一点,在可邮寄类上定义一个 headers
方法。headers
方法应返回一个 Illuminate\Mail\Mailables\Headers
实例。此类接受 messageId
、references
和 text
参数。当然,你只需要为你的特定消息提供所需的参数:
php
use Illuminate\Mail\Mailables\Headers;
/**
* 获取消息标头。
*/
public function headers(): Headers
{
return new Headers(
messageId: 'custom-message-id@example.com',
references: ['previous-message@example.com'],
text: [
'X-Custom-Header' => 'Custom Value',
],
);
}
标签和元数据
一些第三方电子邮件提供商(如 Mailgun 和 Postmark)支持消息"标签"和"元数据",你可以在应用程序中使用这些标签和元数据来对应用程序发送的电子邮件进行分组和跟踪。你可以通过 Envelope
定义向电子邮件消息添加标签和元数据:
php
use Illuminate\Mail\Mailables\Envelope;
/**
* 获取消息信封。
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Order Shipped',
tags: ['shipment'],
metadata: [
'order_id' => $this->order->id,
],
);
}
如果你的应用程序使用 Mailgun 驱动程序,你可以参考 Mailgun 的文档,了解有关 标签 和 元数据 的更多信息。同样,你还可以参考 Postmark 的文档,了解他们对 标签 和 元数据 的支持。
如果你的应用程序使用 Amazon SES 发送电子邮件,你应该使用 metadata
方法附加 SES "标签" 到消息上。
自定义 Symfony 消息
Laravel 的邮件功能由 Symfony Mailer 提供支持。Laravel 允许你注册自定义回调,这些回调将在发送消息之前调用 Symfony Message 实例。这为你提供了在发送消息之前深度自定义消息的机会。要实现这一点,在 Envelope
定义上定义一个 using
参数:
php
use Illuminate\Mail\Mailables\Envelope;
use Symfony\Component\Mime\Email;
/**
* 获取消息信封。
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Order Shipped',
using: [
function (Email $message) {
// ...
},
]
);
}
Markdown 可邮寄类
Markdown 可邮寄消息允许你利用 邮件通知 中预构建的模板和组件来构建可邮寄类。由于消息是用 Markdown 编写的,因此 Laravel 能够为消息渲染漂亮的、响应式的 HTML 模板,同时还会自动生成纯文本版本。
生成 Markdown 可邮寄类
要使用相应的 Markdown 模板生成 mailable,你可以使用 make:mail
Artisan 命令的 --markdown
选项:
shell
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
然后,当在其 content
方法中配置可邮件内容
定义时,请使用 markdown
参数而不是 view
参数:
php
use Illuminate\Mail\Mailables\Content;
/**
* 获取消息内容定义
*/
public function content(): Content
{
return new Content(
markdown: 'mail.orders.shipped',
with: [
'url' => $this->orderUrl,
],
);
}
编写 Markdown 消息
Markdown 邮件组件使用 Blade 组件和 Markdown 语法的组合,允许您在利用 Laravel 预构建的电子邮件 UI 组件的同时轻松构建邮件消息:
blade
<x-mail::message>
# Order Shipped
Your order has been shipped!
<x-mail::button :url="$url">
View Order
</x-mail::button>
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
NOTE
编写 Markdown 电子邮件时不要使用过多的缩进。根据 Markdown 标准,Markdown 解析器会将缩进内容呈现为代码块。
按钮组件
按钮组件呈现居中的按钮链接。该组件接受两个参数,一个 url
和一个可选的 color
。支持的颜色包括 primary
、success
和 error
。您可以根据需要向消息中添加任意数量的按钮组件:
blade
<x-mail::button :url="$url" color="success">
View Order
</x-mail::button>
面板组件
面板组件在面板中呈现给定的文本块,该面板的背景颜色与消息的其余部分略有不同。这使您可以引起对给定文本块的注意:
blade
<x-mail::panel>
This is the panel content.
</x-mail::panel>
Table 组件
表格组件允许您将 Markdown 表格转换为 HTML 表格。该组件接受 Markdown 表作为其内容。使用默认的 Markdown 表格对齐语法支持表格列对齐:
blade
<x-mail::table>
| Laravel | Table | Example |
| ------------- | :-----------: | ------------: |
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
</x-mail::table>
自定义组件
您可以将所有 Markdown 邮件组件导出到您自己的应用程序中进行自定义。要导出组件,请使用 vendor:publish
Artisan 命令发布 laravel-mail
资产标签:
shell
php artisan vendor:publish --tag=laravel-mail
此命令会将 Markdown 邮件组件发布到 resources/views/vendor/mail
目录。mail
目录将包含一个 html
和一个 text
目录,每个目录都包含每个可用组件的各自表示形式。您可以随意自定义这些组件。
自定义 CSS
导出组件后, resources/views/vendor/mail/html/themes
该目录将包含一个 default.css
文件。您可以自定义此文件中的 CSS,您的样式将自动转换为 Markdown 邮件的 HTML 表示中的内联 CSS 样式。
如果您想为 Laravel 的 Markdown 组件构建一个全新的主题,您可以在 html/themes
目录中放置一个 CSS 文件。命名并保存 CSS 文件后,更新应用程序的 config/mail.php
配置文件的 theme
选项以匹配新主题的名称。
要为单个 mailable 自定义主题,您可以将 mailable 类的 $theme
属性设置为发送该 mailable 时应使用的主题名称。
发送邮件
要发送消息,请使用 Mail
Facade上的 to
方法。to
方法接受电子邮件地址、用户实例或用户集合。如果您传递一个对象或对象集合,则邮件程序在确定电子邮件的收件人时将自动使用其 email
和 name
属性,因此请确保这些属性在您的对象上可用。指定收件人后,可以将 mailable 类的实例传递给 send
方法:
php
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderShipmentController extends Controller
{
/**
* 发货给定的订单
*/
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
// 发货订单...
Mail::to($request->user())->send(new OrderShipped($order));
return redirect('/orders');
}
}
您不仅限于在发送消息时指定 “to” 收件人。你可以通过将 “to”、“cc” 和 “bcc” 收件人各自的方法链接在一起来自由设置它们:
php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
循环访问收件人
有时,您可能需要通过迭代一组收件人/电子邮件地址来将 mailable 发送到收件人列表。但是,由于 to
方法将电子邮件地址附加到 mailable 的收件人列表中,因此循环中的每次迭代都会向每个先前的收件人发送另一封电子邮件。因此,您应该始终为每个收件人重新创建 mailable 实例:
php
foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}
通过特定邮件程序发送邮件
默认情况下,Laravel 将使用在应用程序的 mail
配置文件中配置为默认
mailer 的 mailer 发送电子邮件。但是,您可以使用 mailer
方法通过特定的 mailer 配置发送消息:
php
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
邮件队列
邮件消息队列
由于发送电子邮件可能会对应用程序的响应时间产生负面影响,因此许多开发人员选择将电子邮件排队以进行后台发送。Laravel 使用其内置的统一队列 API 使这变得简单。要将邮件消息排队,请在指定消息的收件人后使用 Mail
Facade上的 queue
方法:
php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
此方法将自动将作业推送到队列中,以便在后台发送消息。在使用此功能之前,您需要配置队列。
延迟消息排队
如果您希望延迟排队电子邮件的传递,您可以使用later
。作为其第一个参数,later
接受一个 DateTime
实例,指示何时应发送消息:
php
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later(now()->addMinutes(10), new OrderShipped($order));
推送到特定队列
由于使用 make:mail
命令生成的所有可邮件类都使用了 Illuminate\Bus\Queueable
trait,因此您可以在任何可邮件类实例上调用 onQueue
和 onConnection
方法,从而允许您指定消息的连接和队列名称:
php
$message = (new OrderShipped($order))
->onConnection('sqs')
->onQueue('emails');
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue($message);
默认排队
如果你有希望始终排队的可邮寄类,你可以在该类上实现 ShouldQueue
协定。现在,即使你在 mail 时调用 send
方法,mailable 仍然会排队,因为它实现了 Contract:
php
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
排队的 Mailables 和数据库事务
当在数据库事务中调度排队的 mailables 时,它们可能会在数据库事务提交之前由队列处理。发生这种情况时,您在数据库事务期间对模型或数据库记录所做的任何更新可能尚未反映在数据库中。此外,在事务中创建的任何模型或数据库记录都可能不存在于数据库中。如果您的 mailable 依赖于这些模型,则在处理发送排队的 mailable 的作业时,可能会发生意外错误。
如果您的队列连接的 after_commit
配置选项设置为 false
,您仍然可以在发送邮件消息时通过调用 afterCommit
方法来指示在所有打开的数据库事务都已提交后应调度特定的排队邮件:
php
Mail::to($request->user())->send(
(new OrderShipped($order))->afterCommit()
);
或者,您可以从 mailable 的构造函数中调用 afterCommit
方法:
php
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* 创建新的消息实例
*/
public function __construct()
{
$this->afterCommit();
}
}
NOTE
要了解有关解决这些问题的更多信息,请查看有关排队作业和数据库事务的文档。
显示邮件
有时您可能希望捕获 mailable 的 HTML 内容而不发送它。为此,您可以调用 mailable 的 render
方法。此方法将以字符串形式返回 mailable 的评估 HTML 内容:
php
use App\Mail\InvoicePaid;
use App\Models\Invoice;
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))->render();
在浏览器中预览 Mailables
在设计 mailable 的模板时,可以像典型的 Blade 模板一样在浏览器中快速预览渲染的 mailable 是很方便的。出于这个原因,Laravel 允许你直接从 route closure 或 controller 返回任何 mailable。当一个 mailable 被返回时,它将被渲染并显示在浏览器中,允许你快速预览它的设计,而无需将其发送到实际的电子邮件地址:
php
Route::get('/mailable', function () {
$invoice = App\Models\Invoice::find(1);
return new App\Mail\InvoicePaid($invoice);
});
本地化 Mailable
Laravel 允许你在请求当前 locale 以外的 locale 中发送 mailables,如果邮件排队,它甚至会记住这个 locale。
为此,Mail
Facade提供了一种 locale
方法来设置所需的语言。当 mailable 的模板被评估时,应用程序将更改为此 locale,然后在评估完成时恢复到以前的 locale:
php
Mail::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
用户首选区域设置
有时,应用程序会存储每个用户的首选区域设置。通过在一个或多个模型上实现 HasLocalePreference
契约,您可以指示 Laravel 在发送邮件时使用此存储的 locale:
php
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* 获取用户的首选区域设置
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
一旦你实现了这个接口,Laravel 将在向模型发送 mailables 和 notifications 时自动使用首选的 locale。因此,使用此接口时无需调用 locale
方法:
php
Mail::to($request->user())->send(new OrderShipped($order));
测试
测试可邮寄内容
Laravel 提供了多种方法来检查您的 mailable 的结构。此外,Laravel 还提供了几种方便的方法来测试你的 mailable 是否包含你期望的内容。这些方法是:assertSeeInHtml
、assertDontSeeInHtml
、assertSeeInOrderInHtml
、assertSeeInText
、assertDontSeeInText
、assertSeeInOrderInText
、assertHasAttachment
、assertHasAttachedData
assertHasAttachmentFromStorage
和 assertHasAttachmentFromStorageDisk
。
如您所料,“HTML” 断言 mailable 的 HTML 版本包含给定的字符串,而 “text” 断言您的 mailable 的纯文本版本包含给定的字符串:
php
use App\Mail\InvoicePaid;
use App\Models\User;
test('mailable content', function () {
$user = User::factory()->create();
$mailable = new InvoicePaid($user);
$mailable->assertFrom('jeffrey@example.com');
$mailable->assertTo('taylor@example.com');
$mailable->assertHasCc('abigail@example.com');
$mailable->assertHasBcc('victoria@example.com');
$mailable->assertHasReplyTo('tyler@example.com');
$mailable->assertHasSubject('Invoice Paid');
$mailable->assertHasTag('example-tag');
$mailable->assertHasMetadata('key', 'value');
$mailable->assertSeeInHtml($user->email);
$mailable->assertSeeInHtml('Invoice Paid');
$mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
$mailable->assertSeeInText($user->email);
$mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
$mailable->assertHasAttachment('/path/to/file');
$mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
$mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
});
php
use App\Mail\InvoicePaid;
use App\Models\User;
public function test_mailable_content(): void
{
$user = User::factory()->create();
$mailable = new InvoicePaid($user);
$mailable->assertFrom('jeffrey@example.com');
$mailable->assertTo('taylor@example.com');
$mailable->assertHasCc('abigail@example.com');
$mailable->assertHasBcc('victoria@example.com');
$mailable->assertHasReplyTo('tyler@example.com');
$mailable->assertHasSubject('Invoice Paid');
$mailable->assertHasTag('example-tag');
$mailable->assertHasMetadata('key', 'value');
$mailable->assertSeeInHtml($user->email);
$mailable->assertSeeInHtml('Invoice Paid');
$mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
$mailable->assertSeeInText($user->email);
$mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
$mailable->assertHasAttachment('/path/to/file');
$mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
$mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
}
测试 Mailable Sending
我们建议将 mailable 的内容与断言给定的 mailable 已 “发送” 给特定用户的测试分开测试。通常,mailables 的内容与你正在测试的代码无关,只需断言 Laravel 被指示发送给定的 mailable 就足够了。
您可以使用 Mail
Facade的 fake
方法来阻止邮件发送。在调用 Mail
Facade的 fake
方法之后,你可以断言 mailables 被指示发送给用户,甚至检查 mailables 收到的数据:
php
<?php
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
test('orders can be shipped', function () {
Mail::fake();
// 执行订单发货...
// 断言没有发送任何邮件...
Mail::assertNothingSent();
// 断言已发送可邮寄对象...
Mail::assertSent(OrderShipped::class);
// 断言 mailable 已发送两次...
Mail::assertSent(OrderShipped::class, 2);
// 断言已将 mailable 发送到电子邮件地址...
Mail::assertSent(OrderShipped::class, 'example@laravel.com');
// 断言已将 mailable 发送到多个电子邮件地址...
Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);
// 断言未发送 mailable...
Mail::assertNotSent(AnotherMailable::class);
// 断言总共发送了 3 个邮件...
Mail::assertSentCount(3);
});
php
<?php
namespace Tests\Feature;
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped(): void
{
Mail::fake();
// 执行订单发货...
// 断言没有发送任何邮件...
Mail::assertNothingSent();
// 断言已发送可邮寄对象...
Mail::assertSent(OrderShipped::class);
// 断言 mailable 已发送两次...
Mail::assertSent(OrderShipped::class, 2);
// 断言已将 mailable 发送到电子邮件地址...
Mail::assertSent(OrderShipped::class, 'example@laravel.com');
// 断言已将 mailable 发送到多个电子邮件地址...
Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);
// 断言未发送 mailable...
Mail::assertNotSent(AnotherMailable::class);
// 断言总共发送了 3 个邮件...
Mail::assertSentCount(3);
}
}
如果要在后台将 mailables 排队以进行传递,则应使用 assertQueued
方法而不是 assertSent
:
php
Mail::assertQueued(OrderShipped::class);
Mail::assertNotQueued(OrderShipped::class);
Mail::assertNothingQueued();
Mail::assertQueuedCount(3);
你可以将一个闭包传递给 assertSent
、assertNotSent
、assertQueued
或 assertNotQueued
方法,以断言已发送通过给定“真值测试”的 mailable。如果发送了至少一个通过给定真度测试的 mailable,则断言将成功:
php
Mail::assertSent(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
当调用 Mail
Facade的断言方法时,提供的闭包接受的 mailable 实例公开了检查 mailable 的有用方法:
php
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...') &&
$mail->hasReplyTo('...') &&
$mail->hasFrom('...') &&
$mail->hasSubject('...');
});
mailable 实例还包括几个有用的方法来检查 mailable 上的附件:
php
use Illuminate\Mail\Mailables\Attachment;
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) {
return $mail->hasAttachment(
Attachment::fromPath('/path/to/file')
->as('name.pdf')
->withMime('application/pdf')
);
});
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) {
return $mail->hasAttachment(
Attachment::fromStorageDisk('s3', '/path/to/file')
);
});
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) {
return $mail->hasAttachment(
Attachment::fromData(fn () => $pdfData, 'name.pdf')
);
});
您可能已经注意到,有两种方法可以断言邮件未发送:assertNotSent
和 assertNotQueued
。有时您可能希望断言没有邮件发送或排队。为此,您可以使用 assertNothingOutgoing
和 assertNotOutgoing
方法:
php
Mail::assertNothingOutgoing();
Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
邮件和本地开发
在开发发送电子邮件的应用程序时,您可能不希望实际将电子邮件发送到实时电子邮件地址。Laravel 提供了几种在本地开发期间 “禁用” 实际发送电子邮件的方法。
日志驱动程序
日志
邮件驱动程序不会发送电子邮件,而是将所有电子邮件消息写入您的日志文件以供检查。通常,此驱动程序仅在本地开发期间使用。有关按环境配置应用程序的更多信息,请查看配置文档。
HELO / Mailtrap / Mailpit
或者,您可以使用 HELO 或 Mailtrap 等服务以及 smtp
驱动程序将您的电子邮件发送到“虚拟”邮箱,在那里您可以在真正的电子邮件客户端中查看它们。这种方法的好处是允许您在 Mailtrap 的邮件查看器中实际检查最终电子邮件。
如果您使用的是 Laravel Sail,则可以使用 Mailpit 预览您的消息。当 Sail 运行时,您可以在以下位置访问 Mailpit 界面:http://localhost:8025
。
使用全局地址
最后,您可以通过调用 Mail
Facade提供的 alwaysTo
方法来指定全局 “to” 地址。通常,应从应用程序的某个服务提供商的 boot
方法调用此方法:
php
use Illuminate\Support\Facades\Mail;
/**
* 引导任何应用程序服务
*/
public function boot(): void
{
if ($this->app->environment('local')) {
Mail::alwaysTo('taylor@example.com');
}
}
事件
Laravel 在发送邮件消息时调度两个事件。MessageSending
事件在发送消息之前调度,而 MessageSent
事件在发送消息之后调度。请记住,这些事件是在邮件发送时分派的,而不是在邮件排队时分派的。您可以在应用程序中为这些事件创建事件侦听器:
php
use Illuminate\Mail\Events\MessageSending;
// use Illuminate\Mail\Events\MessageSent;
class LogMessage
{
/**
* 处理给定的事件
*/
public function handle(MessageSending $event): void
{
// ...
}
}
自定义传输
Laravel 包括各种邮件运输;但是,您可能希望编写自己的传输方式,以通过 Laravel 不支持的其他服务立即发送电子邮件。首先,定义一个扩展该类的 Symfony\Component\Mailer\Transport\AbstractTransport
类。然后,在您的传输上实现 doSend
和 __toString()
方法:
php
use MailchimpTransactional\ApiClient;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\MessageConverter;
class MailchimpTransport extends AbstractTransport
{
/**
* 创建新的 Mailchimp 传输实例
*/
public function __construct(
protected ApiClient $client,
) {
parent::__construct();
}
/**
* {@inheritDoc}
*/
protected function doSend(SentMessage $message): void
{
$email = MessageConverter::toEmail($message->getOriginalMessage());
$this->client->messages->send(['message' => [
'from_email' => $email->getFrom(),
'to' => collect($email->getTo())->map(function (Address $email) {
return ['email' => $email->getAddress(), 'type' => 'to'];
})->all(),
'subject' => $email->getSubject(),
'text' => $email->getTextBody(),
]]);
}
/**
* 获取 transport 的字符串表示形式
*/
public function __toString(): string
{
return 'mailchimp';
}
}
定义自定义传输后,您可以通过 Mail
Facade提供的 extend
方法注册它。通常,这应该在应用程序的 AppServiceProvider
服务提供商的 boot
方法中完成。$config
参数将传递给提供给 extend
方法的闭包。此参数将包含在应用程序的 config/mail.php
配置文件中为 mailer 定义的配置数组:
php
use App\Mail\MailchimpTransport;
use Illuminate\Support\Facades\Mail;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Mail::extend('mailchimp', function (array $config = []) {
return new MailchimpTransport(/* ... */);
});
}
定义并注册自定义传输后,您可以在应用程序的 config/mail.php
配置文件中创建一个使用新传输的邮件定义:
php
'mailchimp' => [
'transport' => 'mailchimp',
// ...
],
其他 Symfony 传输
Laravel 包括对一些现有的 Symfony 维护的邮件传输的支持,比如 Mailgun 和 Postmark。但是,您可能希望扩展 Laravel 以支持额外的 Symfony 维护的传输。你可以通过 Composer 要求必要的 Symfony 邮件程序,并在 Laravel 中注册传输。例如,您可以安装并注册 “Brevo” (以前称为 “Sendinblue”) Symfony 邮件程序:
php
composer require symfony/brevo-mailer symfony/http-client
安装 Brevo 邮件包后,您可以将 Brevo API 凭证的条目添加到应用程序的服务
配置文件中:
php
'brevo' => [
'key' => 'your-api-key',
],
接下来,你可以使用 Mail
Facade的 extend
方法向 Laravel 注册传输。通常,这应该在服务提供商的 boot
方法中完成:
php
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory;
use Symfony\Component\Mailer\Transport\Dsn;
/**
* 引导任何应用程序服务
*/
public function boot(): void
{
Mail::extend('brevo', function () {
return (new BrevoTransportFactory)->create(
new Dsn(
'brevo+api',
'default',
config('services.brevo.key')
)
);
});
}
注册传输后,您可以在应用程序的 config/mail.php 配置文件中创建一个使用新传输的邮件管理器定义:
php
'brevo' => [
'transport' => 'brevo',
// ...
],