发送邮件
简介
Laravel 基于热门的 SwiftMailer 函数库提供了一套干净、简洁的 API ,可以为 SMTP、Mailgun、Postmark、Amazon SES 和 sendmail 提供驱动,让你可以快速从本地或云端服务自由地发送邮件。
配置
Laravel 的 email 服务可以通过 config/mail.php
配置文件进行配置。 这个文件中配置的每个邮件程序都可能有自己的配置项,甚至是独有的「传输方式」,这将允许你的应用程序使用不同的邮件服务来发送特定的邮件。例如,你的应用程序可能使用 Postmark 发送事务性邮件,而使用 Amazon SES 发送批量邮件。
在你的 mail
配置文件中,你将找到 mailers
配置数组。 该数组包含 Laravel 支持的每个邮件 驱动程序 / 传输方式 配置,而 default
配置值确定当您的应用程序需要发送电子邮件时,默认情况下将使用哪个邮件驱动。
驱动、传输的前提
基于 API 的驱动,例如 Mailgun 和 Postmark 通常比 SMTP 服务器更简单快速。如果可以的话, 你应该尽可能使用这些驱动。所有的 API 驱动都需要 Guzzle HTTP 函数库,这个函数库可以通过 Composer 包管理安装:
composer require guzzlehttp/guzzle
Mailgun 驱动
要使用 Mailgun
驱动,首先必须安装 Guzzle HTTP 库, 之后将 config/mail.php
配置文件中的 default
选项设置为 mailgun
。接下来,确认 config/services.php
配置文件包含以下选项:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
如果你未使用 “US” Mailgun 区域, 你可以在 services
配置文件中定义自己的区域终端地址:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
],
Postmark 驱动
要使用 Postmark
驱动, 需要先通过 Composer
安装 Postmark
的 SwiftMailer
函数库:
composer require wildbit/swiftmailer-postmark
然后, 安装 Guzzle HTTP
库并为 postmark
设置 config/mail.php
配置文件中的 default
选项. 最后, 确认你的 config/services.php
配置文件包含以下选项:
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
如果你要给指定邮件程序使用 Postmark message stream,则可以在配置数组中添加 message_stream_id
配置选项。 这个配置数组在应用程序的 config/mail.php
配置文件中:
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
],
这样,你还可以使用不同的 message stream 来设置多个 Postmark 邮件驱动。
SES 驱动
要使用 Amazon SES
驱动,你必须先安装 PHP
的 Amazon AWS SDK
。你可以可以通过 Composer 软件包管理器安装此库:
composer require aws/aws-sdk-php
然后,将 config/mail.php
配置文件的 default
选项设置成 ses
并确认你的 config/services.php
配置文件包含以下选项:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
如果你在执行 SES SendRawEmail
请求的时候需要包含 附加选项, 你可以在 ses
配置中定义一个 options
数组:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'options' => [
'ConfigurationSetName' => 'MyConfigurationSet',
'Tags' => [
['Name' => 'foo', 'Value' => 'bar'],
],
],
],
生成 mailables 类
在 Laravel 中,应用发送的每种邮件都被表示为 mailable
类。这些类存储于 app/Mail
目录中。如果你的应用中没有该目录,别慌,当你使用 make:mail
命令生成您的首个 mailable 类时,应用将会自动创建它:
php artisan make:mail OrderShipped
编写 Mailables 类
所有的 mailable 类的配置都在 build
方法中完成。您可以通过调用诸如 from
, subject
, view
和 attach
这样的方法来配置邮件的内容及其发送。
配置发送人
使用 from
方法
首先,让我们浏览一下邮件的发送人的配置。或者,换句话说,邮件来自谁。有两种方法配置发送人。第一种,你可以在 mailable 类的 build
方法中使用 from
方法:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->from('example@example.com')
->view('emails.orders.shipped');
}
使用全局 from
地址
当然,如果你的应用在任何邮件中使用的「发件人」地址都一致的话,在你生成的每一个 mailable 类中调用 from
方法可能会很麻烦。因此,你可以在 config/mail.php
文件中指定一个全局的「发件人」地址。当某个 mailable 类没有指定「发件人」时,它将使用该全局「发件人」:
'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
此外,你可以在 config/mail.php
配置文件中定义一个全局的「回复」地址:
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
配置视图
你可以在 mailable 类的 build
方法中使用 view
方法来指定在渲染邮件内容时要使用的模板。由于每封邮件通常使用 Blade 模板 来渲染其内容,因此在构建邮件 HTML 内容时你可以使用 Blade 模板引擎提供的所有功能及享受其带来的便利性:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped');
}
技巧:你可以创建一个
resources/views/emails
目录来存放你的所有邮件模板;当然,你也可以将其置于resources/views
目录下的任何位置。
纯文本邮件
你可以使用 text
方法来定义一个纯文本格式的邮件。和 view
方法一样,text
方法接受一个模板名,模板名指定了在渲染邮件内容时你想使用的模板。你既可以定义纯文本格式亦可定义 HTML 格式:
/**
* 构建邮件
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->text('emails.orders.shipped_plain');
}
视图数据
通过 Public 属性
通常情况下,你可能想要在渲染邮件的 HTML 内容时传递一些数据到视图中。有两种方法传递数据到视图中。第一种,你在 mailable 类中定义的所有 public 的属性都将自动传递到视图中。因此,举个例子,你可以将数据传递到你的 mailable 类的构造函数中,并将其设置为类的 public 属性:
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 订单实例
*
* @var \App\Models\Order
*/
public $order;
/**
* 创建一个消息实例
*
* @param \App\Models\Order $order
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped');
}
}
当数据被设置成为 public 属性之后,它将被自动传递到你的视图中,因此你可以像您在 Blade 模板中那样访问它们:
<div>
Price: {{ $order->price }}
</div>
通过 with
方法:
如果你想要在邮件数据发送到模板前自定义它们的格式,你可以使用 with
方法来手动传递数据到视图中。一般情况下,你还是需要通过 mailable 类的构造函数来传递数据;不过,你应该将它们定义为 protected
或 private
以防止它们被自动传递到视图中。然后,在您调用 with
方法的时候,你可以以数组的形式传递你想要传递给模板的数据:
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 订单实例
*
* @var \App\Models\Order
*/
protected $order;
/**
* 创建消息实例
*
* @param \App\Models\Order $order
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->with([
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
]);
}
}
当数据使用 with
方法传递后,你便可以在视图中使用它们,此时,便可以像 Blade 模板的方式来访问它们:
<div>
Price: {{ $orderPrice }}
</div>
附件
要在邮件中加入附件,在 build
方法中使用 attach
方法。attach
方法接受文件的绝对路径作为它的第一个参数:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attach('/path/to/file');
}
当附加文件到消息时,你也可以传递一个 array
给 attach
方法作为第二个参数,以指定显示名称和 / 或是 MIME 类型:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
从磁盘中添加附件
如果你已经在 文件存储 上存储了一个文件,则可以使用 attachFromStorage
方法将其附加到邮件中:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attachFromStorage('/path/to/file');
}
如有必要,你可以使用 attachFromStorage
方法的第二个和第三个参数指定文件的附件名称和其他选项:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attachFromStorage('/path/to/file', 'name.pdf', [
'mime' => 'application/pdf'
]);
}
如果需要指定默认磁盘以外的存储磁盘,可以使用 attachFromStorageDisk
方法:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attachFromStorageDisk('s3', '/path/to/file');
}
原始数据附件
attachData
可以使用字节数据作为附件。例如,你可以使用这个方法将内存中生成而没有保存到磁盘中的 PDF 附加到邮件中。attachData
方法第一个参数接收原始字节数据,第二个参数为文件名,第三个参数接受一个数组以指定其他参数:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attachData($this->pdf, 'name.pdf', [
'mime' => 'application/pdf',
]);
}
内联附件
在邮件中嵌入内联图片通常很麻烦;不过,Laravel 提供了一种将图像附加到邮件的便捷方法。可以使用邮件模板中 $message
变量的 embed
方法来嵌入内联图片。Laravel 自动使 $message
变量在全部邮件模板中可用,不需要担心手动传递它:
<body>
Here is an image:
<img src="{{ $message->embed($pathToImage) }}">
</body>
注意:
$message
在文本消息中不可用,因为文本消息不能使用内联附件。
嵌入原始数据附件
如果你已经有了可以嵌入邮件模板的原始图像数据字符串,可以使用 $message
变量的 embedData
方法,当调用 embedData
方法时,需要传递一个文件名:
<body>
Here is an image from raw data:
<img src="{{ $message->embedData($data, 'example-image.jpg') }}">
</body>
自定义 SwiftMailer 视图
Mailable
基类的 withSwiftMessage
方法允许你传递一个闭包,它将在发送消息之前被调用,原始的 SwiftMailer 消息将作为该闭包的参数。你可以在发消息前对其进行自定义:
/**
* 构建消息
*
* @return $this
*/
public function build()
{
$this->view('emails.orders.shipped');
$this->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader(
'Custom-Header', 'Header Value'
);
});
}
Markdown 格式邮件
Markdown 格式邮件允许你可以使用 mailable 中的预构建模板和 邮件通知 组件。由于消息是用 Markdown 编写,Laravel 能够渲染出美观的、响应式的 HTML 模板消息,同时还能自动生成纯文本副本。
生成 Markdown 邮件
你可以在执行 make:mail
的 Artisan 命令时使用 --markdown
选项来生成一个 Markdown 格式模板的 mailable 类:
php artisan make:mail OrderShipped --markdown=emails.orders.shipped
然后,在它的 build
方法中配置 mailable 类时,请使用 markdown
方法来代替 view
方法。markdown
方法接受 Markdown 模板的名称和想要传递给模板的可选的数组形式的数据:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->from('example@example.com')
->markdown('emails.orders.shipped', [
'url' => $this->orderUrl,
]);
}
编写 Markdown 邮件
Markdown mailable 类整合了 Markdown 语法和 Blade 组件,让你能够非常方便的使用 Laravel 预置的 UI 组件来构建邮件消息:
@component('mail::message')
# Order Shipped
Your order has been shipped!
@component('mail::button', ['url' => $url])
View Order
@endcomponent
Thanks,<br>
{{ config('app.name') }}
@endcomponent
技巧:在编写 Markdown 邮件的时候,请勿使用额外的缩进。Markdown 解析器会把缩进渲染成代码块。
按钮组件
按钮组件用于渲染居中的按钮链接。该组件接收两个参数,一个是 url
一个是可选的 颜色
。支持的颜色包括 primary
, success
和 error
。你可以在邮件中添加任意数量的按钮组件:
@component('mail::button', ['url' => $url, 'color' => 'success'])
View Order
@endcomponent
面板组件
面板组件在面板内渲染指定的文本块,面板与其他消息的背景色略有不同。它允许你绘制一个警示文本块:
@component('mail::panel')
This is the panel content.
@endcomponent
表格组件
表格组件允许你将 Markdown 表格转换成 HTML 表格。该组件接受 Markdown 表格作为其内容。列对齐支持默认的 Markdown 表格对齐语法:
@component('mail::table')
| Laravel | Table | Example |
| ------------- |:-------------:| --------:|
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
@endcomponent
自定义组件
可以将所有 Markdown 邮件组件导出到自己的应用,用作自定义组件的模板。若要导出组件,使用 laravel-mail
资产标签的 vendor:publish
Artisan 命令:
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 表示中。
如果想为 Laravel 的 Markdown 组件构建一个全新的主题,你可以在 html/themes
目录中新建一个 CSS 文件。 命名并保存 CSS 文件后,并更新应用程序 config/mail.php
配置文件的 theme
选项以匹配新主题的名称。
要为单个邮件自定义主题,可以将 mailable 类的 $theme
属性设置为发送 mailable 时应使用的主题名称。
发送邮件
若要发送邮件,使用 Mail
facade 的 to
方法。 to
方法接受 邮件地址、用户实例或用户集合。如果传递一个对象或者对象集合,mailer 在设置收件人时将自动使用它们的 email
和 name
属性,因此请确保对象的这些属性可用。一旦指定了收件人,就可以将 mailable 类实例传递给 send
方法:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderShipmentController extends Controller
{
/**
* 发送给定的订单。
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$order = Order::findOrFail($request->order_id);
// Ship the order...
Mail::to($request->user())->send(new OrderShipped($order));
}
}
在发送消息时不止可以指定收件人。还可以通过链式调用「to」、「cc」、「bcc」一次性指定抄送和密送收件人:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
遍历收件人列表
有时,你需要通过遍历一个收件人/邮件地址数组的方式,给一系列收件人发送邮件。但是,由于 to
方法会给 mailable 列表中的收件人追加邮件地址,因此,你应该为每个收件人重建 mailable 实例。
foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}
通过特定的 Mailer 发送邮件
默认情况下,Laravel 将使用你的 mail
配置文件中配置为 default
邮件程序。 但是,你可以使用 mailer
方法通过特定的邮件程序配置发送:
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
邮件队列l
将邮件消息加入队列
由于发送邮件消息可能大幅度延长应用的响应时间,许多开发者选择将邮件消息加入队列放在后台发送。Laravel 使用内置的 统一队列 API 简化了这一工作。若要将邮件消息加入队列,可以在指定消息的接收者后,使用 Mail
facade 的 queue
方法:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
此方法自动将作业推送到队列中以便消息在后台发送。使用此特性之前,需要 配置队列 :
延迟消息队列
想要延迟发送队列化的邮件消息,可以使用 later
方法。later
方法的第一个参数的第一个参数是标示消息何时发送的 DateTime
实例:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later(now()->addMinutes(10), new OrderShipped($order));
推送到指定队列
由于所有使用 make:mail
命令生成的 mailable 类都是用了 Illuminate\Bus\Queueable
trait,因此你可以在任何 mailable 类实例上调用 onQueue
和 onConnection
方法来指定消息的连接和队列名:
$message = (new OrderShipped($order))
->onConnection('sqs')
->onQueue('emails');
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue($message);
默认队列
如果你希望你的邮件类始终使用队列,你可以给邮件类实现 ShouldQueue
契约,现在即使你调用了 send
方法,邮件依旧使用队列的方式发送
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
//
}
Queued Mailables & Database Transactions
当在数据库事务中分发邮件队列时,队列可能在数据库事务提交之前处理邮件。 发生这种情况时,您在数据库事务期间对模型或数据库记录所做的任何更新可能都不会反映在数据库中。 另外,在事务中创建的任何模型或数据库记录都可能不存在于数据库中。 如果您的邮件基于这些模型数据,则在处理邮件发生时,可能会发生意外错误。
如果队列连接的 after_commit
配置选项设置为 false
,则仍然可以通过在 mailable 类上定义 $afterCommit
属性来设置提交所有打开的数据库事务之后再调度特定的邮件队列:
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
public $afterCommit = true;
}
技巧:要了解有关解决这些问题的更多信息,请查看有关队列和数据库事物.
获取邮件内容
有时您可能希望捕获邮件的 HTML 内容而不发送它。为此,可以调用邮件类的 render
方法。此方法将以字符串形式返回邮件类的渲染内容:
use App\Mail\InvoicePaid;
use App\Models\Invoice;
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))->render();
在浏览器中预览邮件
设计邮件模板时,可以方便地在浏览器中预览邮件,就像典型的 Blade
模板一样。因此, Laravel 允许您直接从路由闭包或控制器返回任何邮件类。当邮件返回时,它将渲染并显示在浏览器中,允许您快速预览其设计,而无需将其发送到实际的电子邮件地址
Route::get('/mailable', function () {
$invoice = App\Models\Invoice::find(1);
return new App\Mail\InvoicePaid($invoice);
});
注意:在浏览器中预览邮件时, 不会显示附件 ,如果要测试附件发送,您应该将它们发送到电子邮件测试应用程序,例如 MailHog 或者 HELO.
邮件类的本地化
Laravel 允许您以当前语言以外的语言发送邮件,如果是队列邮件,甚至会记住这个区域设置。
为了实现这一点,Mail
Facade 提供了一个 locale
方法来设置所需的语言。在格式化邮件时,应用程序将更改为该区域设置,然后在格式化完成后恢复到以前的区域设置
Mail::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
用户的个性化翻译
有时,应用程序会为每个用户存储不同的区域设置。通过在一个或多个模型上实现HasLocalePreference
锲约,可以指示 Laravel 在发送邮件时使用此存储的区域设置:
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Get the user's preferred locale.
*
* @return string
*/
public function preferredLocale()
{
return $this->locale;
}
}
一旦实现了此契约,Laravel 将在向模型发送邮件和通知时自动使用该语言环境。因此,使用此接口时不需要调用 locale
方法:
Mail::to($request->user())->send(new OrderShipped($order));
邮件测试
Laravel提供了几种方便的方法来测试您的邮件是否包含您期望的内容。这些方法是: assertSeeInHtml
, assertDontSeeInHtml
, assertSeeInText
, and assertDontSeeInText
.
如您所料,“HTML” 断言您的邮件的 HTML 版本包含给定的字符串,而 “text” 断言您的邮件的纯文本版本包含给定的字符串:
use App\Mail\InvoicePaid;
use App\Models\User;
public function test_mailable_content()
{
$user = User::factory()->create();
$mailable = new InvoicePaid($user);
$mailable->assertSeeInHtml($user->email);
$mailable->assertSeeInHtml('Invoice Paid');
$mailable->assertSeeInText($user->email);
$mailable->assertSeeInText('Invoice Paid');
}
测试邮件发送
我们建议将测试邮件的内容与断言给定邮件已“发送”给特定用户的测试分开。要了解如何测试邮件是否已发送,请查看我们在 Mail fake 文档。
本地开发
当你正在开发一个邮件的应用程序时,您可能不想实际地向真实邮件地址发送邮件。Laravel 提供了几种在本地开发过程中「禁用」实际发送电子邮件的方法
日志驱动
log
邮件驱动程序不发送邮件,而是将所有邮件消息写入日志文件用来校验。有关为每个环境配置应用程序的更多信息,请参阅配置文档
HELO / Mailtrap / MailHog
最后,您可以使用像 HELO 这样的服务或 Mailtrap 和 smtp
驱动程序将您的电子邮件发送到一个 “虚拟” 邮箱,您可以在一个真正的电子邮件客户端中查看它们。这种方法的好处是允许您在 Mailtrap 的消息查看器中实际检查最终的电子邮件。
如果您有在使用 Laravel Sail, 你可以使用 MailHog 预览您的邮件。当Sail运行时,您可以通过 http://localhost:8025
访问 MailHog。
事件
在发送邮件消息的时候,Laravel 会启动两个事件。MessageSending
事件在发送消息前触发,MessageSent
事件在消息发送完成后触发。记住,这些事件都是在邮件被 发送 时触发,而不是在队列化的时候。您可以在 EventServiceProvider
中注册一个事件监听器:
/**
* 为应用映射事件监听器
*
* @var array
*/
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'App\Listeners\LogSendingMessage',
],
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
];
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。