邮件
简介
Laravel 基于 SwiftMailer 库提供了一套干净、清爽的邮件 API。Laravel 为 SMTP、Mailgun、Postmark、SparkPost、Amazon SES、以及 sendmail
提供了相应的驱动,从而允许你快速通过本地或云服务发送邮件。
邮件驱动预备知识
基于 API 的驱动如 Mailgun、SparkPost 和 Postmark 通常比 SMTP 服务器更简单、更快,所以如果可以的话,尽可能使用这些服务。所有的 API 驱动要求应用已经安装 Guzzle HTTP 库,你可以通过 Composer 包管理器来安装它:
composer require guzzlehttp/guzzle
Mailgun 驱动
要使用 Mailgun 驱动(Mailgun 前10000封邮件免费,后续收费),首先安装 Guzzle,然后在配置文件 config/mail.php
中设置 driver
选项为 mailgun
。接下来,验证配置文件 config/services.php
包含如下选项:
'mailgun' => [
'domain' => 'your-mailgun-domain',
'secret' => 'your-mailgun-key',
],
如果你不是使用「美国」区域的 Mailgun,可以在 services
配置文件中定义区域端点:
'mailgun' => [
'domain' => 'your-mailgun-domain',
'secret' => 'your-mailgun-key',
'endpoint' => 'api.eu.mailgun.net',
],
Postmark 驱动
要使用 Postmark 驱动,需要先通过 Composer 安装 Postmark 的 SwiftMailer 传输包:
composer require wildbit/swiftmailer-postmark
接下来,安装 Guzzle 并在配置文件 config/mail.php
中设置 driver
选项值为 postmark
。最后,确保配置文件 config/services.php
中包含以下选项:
'postmark' => [
'token' => 'your-postmark-token',
],
SparkPost 驱动
要使用 SparkPost 驱动,首先安装 Guzzle,然后在配置文件 config/mail.php
中设置 driver
选项值为 sparkpost
。接下来,验证配置文件 config/services.php
包含如下选项:
'sparkpost' => [
'secret' => 'your-sparkpost-key',
],
如果需要的话,还可以配置使用哪个 API 端点:
'sparkpost' => [
'secret' => 'your-sparkpost-key',
'options' => [
'endpoint' => 'https://api.eu.sparkpost.com/api/v1/transmissions',
],
],
SES 驱动
要使用 Amazon SES 驱动(收费),先安装 Amazon AWS 的 PHP SDK,你可以通过添加如下行到 composer.json
文件的 require
部分然后运行 composer update
命令来安装该库:
"aws/aws-sdk-php": "~3.0"
接下来,设置配置文件 config/mail.php
中的 driver
选项为 ses
。然后,验证配置文件 config/services.php
包含如下选项:
'ses' => [
'key' => 'your-ses-key',
'secret' => 'your-ses-secret',
'region' => 'ses-region', // e.g. us-east-1
],
如果你需要在执行 SES SendRawEmail
请求时引入额外的选项,可以在 ses
配置中通过定义 options
数组来完成:
'ses' => [
'key' => 'your-ses-key',
'secret' => 'your-ses-secret',
'region' => 'ses-region', // e.g. us-east-1
'options' => [
'ConfigurationSetName' => 'MyConfigurationSet',
'Tags' => [
[
'Name' => 'foo',
'Value' => 'bar',
],
],
],
],
学院君注:坏消息是以上驱动均不适用于国内开发者开发服务于国内用户的应用,所以基本可以忽略。
生成可邮寄类
在 Laravel 中,应用发送的每一封邮件都可以表示为“可邮寄”类,这些类都存放在 app/Mail
目录。如果没看到这个目录,别担心,它将会在你使用 make:mail
命令创建第一个可邮寄类时生成:
php artisan make:mail OrderShipped
默认生成的模板代码如下:
编写可邮寄类
所有的可邮寄类配置都在 build
方法中完成,在这个方法中,你可以调用多个方法,例如 from
、subject
、view
和 attach
来配置邮件的内容和发送。
配置发件人
使用 from
方法
首先,我们来看一下邮件发件人的配置,或者,换句话说,邮件来自于谁。有两种方式来配置发送者,第一种方式是在可邮寄类的 build
方法方法中调用 from
方法:
/**
* 构建消息.
*
* @return $this
*/
public function build()
{
return $this->from('example@example.com')
->view('emails.orders.shipped');
}
使用全局的 from
地址
不过,如果你的应用在所有邮件中都使用相同的发送地址,在每个生成的可邮寄类中都调用 from
方法就显得很累赘。取而代之地,你可以在配置文件 config/mail.php
中指定一个全局的发送地址,该地址可用于在所有可邮寄类中没有指定其它发送地址的场景下(即作为默认发件人):
'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
此外,你可以在配置文件 config/mail.php
中定义全局的「回复」地址:
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
配置视图
你可以在可邮寄类的 build
方法中使用 view
方法来指定渲染邮件内容时使用哪个视图模板,由于每封邮件通常使用 Blade 模板来渲染内容,所以你可以在构建邮件 HTML 时使用 Blade 模板引擎提供的所有功能:
/**
* 构建消息.
*
* @return $this
* @translator laravelacademy.org
*/
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');
}
视图数据
通过公共属性
通常,我们需要传递一些数据到渲染邮件的 HTML 视图以供使用。有两种方式将数据传递到视图,第一种是可邮寄类的公共(public)属性在视图中自动生效,举个例子,我们将数据传递给可邮寄类的构造器并将数据设置给该类的公共属性:
<?php
namespace App\Mail;
use App\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 订单实例.
*
* @var Order
*/
public $order;
/**
* 创建一个新的消息实例.
*
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* 构建消息.
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped');
}
}
数据被设置给公共属性后,将会在视图中自动生效,所以你可以像在 Blade 模板中访问其它数据一样访问它们:
<div>
Price: {{ $order->price }}
</div>
通过 with
方法
如果你想要在数据发送到模板之前自定义邮件数据的格式,可以通过 with
方法手动传递数据到视图。一般情况下,你还是需要通过可邮寄类的构造器传递数据,不过,这次你需要设置数据为 protected
或 private
属性,这样,这些数据就不会在视图中自动生效。然后,当调用 with
方法时,传递数组数据到该方法以便数据在视图模板中生效:
<?php
namespace App\Mail;
use App\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* 订单实例.
*
* @var Order
*/
protected $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');
}
附加文件到消息时,还可以通过传递一个数组作为 attach
方法的第二个参数来指定文件显示名或 MIME 类型:
/**
* 构建消息.
*
* @return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
添加磁盘文件
如果你已经在某个文件系统磁盘中存储了文件,可以使用 attachFromStorage
方法将其添加到邮件:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('email.orders.shipped')
->attachFromStorage('/path/to/file');
}
如果必要的话,你可以通过传递第二个和第三个参数到 attachFromStorage
方法来分别指定文件的附件名以及额外的选项:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('email.orders.shipped')
->attachFromStorage('/path/to/file', 'name.pdf', [
'mime' => 'application/pdf'
]);
}
如果你想要指定某个存储磁盘而非默认磁盘,可以使用attachFromStorageDisk
方法:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('email.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 提供了便捷的方式附加图片到邮件并获取相应的 CID,要嵌入内联图片,在邮件视图中使用 $message
变量上的embed
方法即可。Laravel 在所有邮件视图中注入 $message
变量并使其自动有效,所以你不用关心如何将这个变量手动传入视图:
<body>
Here is an image:
<img src="{{ $message->embed($pathToFile) }}">
</body>
注:
$message
变量在纯文本消息中无效,因为纯文本消息不使用内联附件。
嵌入原生数据附件
如果你已经有一个想要嵌入邮件模板的原生数据字符串,可以使用 $message
变量上的 embedData
方法:
<body>
Here is an image from raw data:
<img src="{{ $message->embedData($data, $name) }}">
</body>
自定义 SwiftMailer 消息
Mailable
基类上的 withSwiftMessage
方法允许你注册一个在发送消息之前可以被原生 SwiftMailer 消息实例调用的回调。这赋予了我们在发送消息前定制消息的机会:
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->view('emails.orders.shipped');
$this->withSwiftMessage(function ($message) {
$message->getHeaders()
->addTextHeader('Custom-Header', 'HeaderValue');
});
}
Markdown 可邮寄类
Markdown 邮件消息允许你在可邮寄类中利用预置模板和邮件通知组件。因为这些消息以 Markdown 格式编写,Laravel 还可以为它们渲染出高颜值、响应式的 HTML 模板,同时自动生成纯文本的副本。
生成 Markdown 可邮寄类
要生成带有相应 Markdown 模板的可邮寄类,可以在使用 Artisan 命令 make:mail
时带上 --markdown
选项:
php artisan make:mail OrderShipped --markdown=emails.orders.shipped
然后,配置可邮寄类的 build
方法时,使用 markdown
方法取代 view
方法。markdown
方法接收 Markdown 模板的名称和一个可选的在模板中生效的数组数据:
/**
* 构建消息.
*
* @return $this
*/
public function build()
{
return $this->from('example@example.com')
->markdown('emails.orders.shipped');
}
编写 Markdown 消息
Markdown 邮件类组合使用了 Blade 组件和 Markdown 语法,从而让你在不脱离 Laravel 预置组件的情况下轻松构建邮件消息:
@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
和可选的 color
,支持的颜色有 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 邮件组件到自己的应用中进行自定义,使用 Artisan 命令 vendor:publish
来发布 laravel-mail
资源标签:
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 样式就会自动调整。
如果你想要为 Markdown 组件构建全新的主题,只需在 html/themes
目录中编写一个新的 CSS 文件并修改 mail
配置文件的 theme
选项即可。
要为独立的可邮寄类自定义主题,可以通过设置可邮寄类的 $theme
属性值为对应的主题名称来完成。
发送邮件
要发送一条信息,可以使用 Mail
门面上的 to
方法。to
方法接收邮箱地址、用户实例或用户集合作为参数。如果传递的是对象或对象集合,在设置邮件收件人的时候邮件会自动使用它们的 email
和 name
属性,所以事先要确保这些属性在相应类上有效。指定好收件人以后,传递一个可邮寄类的实例到 send
方法:
<?php
namespace App\Http\Controllers;
use App\Order;
use App\Mail\OrderShipped;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Controller;
class OrderController extends Controller
{
/**
* 处理传入订单.
*
* @param Request $request
* @param int $orderId
* @return Response
* @translator laravelacademy.org
*/
public function ship(Request $request, $orderId)
{
$order = Order::findOrFail($orderId);
// 处理订单...
Mail::to($request->user())->send(new OrderShipped($order));
}
}
当然,发送邮件消息时并不仅限于指定单个收件人,你可以通过单个方法链调用设置「to」、「cc」以及「bcc」:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
渲染可邮寄类
有时候你可能想要在不发送可邮寄类的情况下捕获其 HTML 内容,要实现这个功能,可以调用可邮寄类的 render
方法,该方法会将处理后的邮件内容以字符串方式返回:
$invoice = App\Invoice::find(1);
return (new App\Mail\InvoicePaid($invoice))->render();
在浏览器中预览邮件
设计邮件的模板时,如果可以像普通的 Blade 模板一样快速预览渲染后的邮件是很方便的。因此,Laravel 允许你从路由闭包或控制器中直接返回可邮寄类。当可邮寄类返回时,就会在浏览器渲染并展示,从而方便你快速预览而不必真的发送邮件后查看:
Route::get('/mailable', function () {
$invoice = App\Invoice::find(1);
return new App\Mail\InvoicePaid($invoice);
});
邮件队列
邮件消息队列
由于发送邮件消息可能会大幅度延长应用的响应时间,许多开发者选择将邮件发送放到队列中在后台发送,Laravel 中可以使用内置的统一队列 API 来实现这一功能。要将邮件消息推送到队列,可以在指定消息的接收者后使用 Mail
门面上的 queue
方法:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
该方法自动将邮件任务推送到队列以便在后台发送。当然,你需要在使用该特性前配置队列。
延迟消息队列
如果你想要延迟队列中邮件消息的发送,可以使用 later
方法。later
方法的第一个参数接收一个 DateTime
实例来表示邮件发送时间:
$when = Carbon\Carbon::now()->addMinutes(10);
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later($when, new OrderShipped($order));
推送到指定队列
由于通过 make:mail
命令生成的所有可邮寄类都使用了Illuminate\Bus\Queueable
trait,所以你可以调用可邮寄类实例上的 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
{
//
}
本地化可邮寄类
Laravel 允许你以本地化语言发送邮件而非当前默认语言,甚至在队列中也能记住本地化设置。
为了实现该功能,Mail
门面提供了一个 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 提供了几种方式“取消”邮件的实际发送。
日志驱动
一种解决方案是在本地开发时使用 log
邮件驱动。该驱动将所有邮件信息写到日志文件中以备查看,想要了解更多关于每个环境的应用配置信息,查看配置文档。
通用配置
Laravel 提供的另一种解决方案是为框架发送的所有邮件设置通用收件人,这样的话,所有应用生成的邮件将会被发送到指定地址,而不是实际发送邮件指定的地址。这可以通过在配置文件 config/mail.php
中设置 to
选项来实现:
'to' => [
'address' => 'example@example.com',
'name' => 'Example'
],
Mailtrap
最后,你可以使用 Mailtrap 服务和 smtp
驱动发送邮件信息到“虚拟”邮箱,这种方法允许你在 Mailtrap 的消息查看器中查看最终的邮件。
事件
Laravel 会在发送邮件消息前触发两个事件,MessageSending
事件在消息发送前触发,MessageSent
事件在消息发送后触发。需要注意的是这两个事件是在邮件被发送前后触发,而不是推送到队列时,你可以在 EventServiceProvider
中注册对应的事件监听器:
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'App\Listeners\LogSendingMessage',
],
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
];
No Comments