郵件發(fā)送
郵件發(fā)送
簡介
Laravel 基于 SwiftMailer 庫提供了一套干凈、清爽的郵件 API。Laravel 為 SMTP、Mailgun、SparkPost、Amazon SES、PHP 的 mail
函數(shù),以及 sendmail
提供了驅(qū)動,從而允許你快速通過本地或云服務(wù)發(fā)送郵件。
郵件驅(qū)動預(yù)備知識
基于 API 的驅(qū)動如 Mailgun 和 SparkPost 通常比 SMTP 服務(wù)器更簡單、更快,所以如果可以的話,盡可能使用這些服務(wù)。所有的 API 驅(qū)動要求應(yīng)用已經(jīng)安裝 Guzzle HTTP 庫,你可以通過 Composer 包管理器來安裝它:
composer require guzzlehttp/guzzle
Mailgun 驅(qū)動
要使用 Mailgun 驅(qū)動(Mailgun 前 10000 封郵件免費(fèi),后續(xù)收費(fèi)),首先安裝 Guzzle,然后在配置文件 config/mail.php
中設(shè)置 driver
選項(xiàng)為 mailgun
。接下來,驗(yàn)證配置文件 config/services.php
包含如下選項(xiàng):
'mailgun' => [ 'domain' => 'your-mailgun-domain', 'secret' => 'your-mailgun-key', ],
如果您沒有使用 “US” Mailgun 地區(qū) ,您可以在 services
中定義您所在地區(qū)的終端 配置文件:
'mailgun' => [ 'domain' => 'your-mailgun-domain', 'secret' => 'your-mailgun-key', 'endpoint' => 'api.eu.mailgun.net', ],
Postmark 驅(qū)動程序
要使用 Postmark 驅(qū)動程序,請通過 Composer 安裝 Postmark 的 SwiftMailer:
composer require wildbit/swiftmailer-postmark
接下來,安裝 Guzzle 并將 config / mail.php
配置文件中的 driver
選項(xiàng)設(shè)置為 postmark
。 最后,驗(yàn)證您的 config / services.php
配置文件包含以下選項(xiàng):
'postmark' => [ 'token' => 'your-postmark-token', ],
SparkPost 驅(qū)動
要使用 SparkPost 驅(qū)動,首先安裝 Guzzle,然后再配置文件 config/mail.php
中設(shè)置 driver
選項(xiàng)值為 sparkpost
。接下來,驗(yàn)證配置文件 config/services.php
包含如下選項(xiàng):
'sparkpost' => [ 'secret' => 'your-sparkpost-key', ],
如果有必要的話,你還可以設(shè)置 API 端點(diǎn)使用:
'sparkpost' => [ 'secret' => 'your-sparkpost-key', 'options' => [ 'endpoint' => 'https://api.eu.sparkpost.com/api/v1/transmissions', ], ],
SES 驅(qū)動
要使用 Amazon SES 驅(qū)動(收費(fèi)),先安裝 Amazon AWS 的 PHP SDK,你可以通過添加如下行到 composer.json
文件的 require
部分然后運(yùn)行 composer update
命令來安裝該庫:
"aws/aws-sdk-php": "~3.0"
接下來,設(shè)置配置文件 config/mail.php
中的 driver
選項(xiàng)為 ses
。然后,驗(yàn)證配置文件 config/services.php
包含如下選項(xiàng):
'ses' => [ 'key' => 'your-ses-key', 'secret' => 'your-ses-secret', 'region' => 'ses-region', // e.g. us-east-1 ],
如果您在執(zhí)行 SES 時需要包含 附加選項(xiàng) SendRawEmail
請求,您可以在 ses
配置中定義 options
數(shù)組:
'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 中,應(yīng)用發(fā)送的每一封郵件都可以表示為 “可郵寄” 類,這些類都存放在 app/Mail
目錄。如果沒看到這個目錄,別擔(dān)心,它將會在你使用 make:mail
命令創(chuàng)建第一個可郵寄類時生成:
php artisan make:mail OrderShipped
編寫可郵寄類
所有的可郵寄類配置都在 build
方法中完成,在這個方法中,你可以調(diào)用多個方法,例如 from
,subject
, view
, 和 attach
來配置郵件的內(nèi)容和發(fā)送。
配置發(fā)件人
使用 from
方法
我們來看一下郵件發(fā)件人的配置,或者,換句話說,郵件來自于誰。有兩種方式來配置發(fā)送者,第一種方式是在可郵寄類的 build
方法方法中調(diào)用 from
方法:
/** * 構(gòu)建消息. * * @return $this */ public function build(){ return $this->from('example@example.com') ->view('emails.orders.shipped'); }
使用全局的 from
地址
不過,如果你的應(yīng)用在所有郵件中都使用相同的發(fā)送地址,在每個生成的可郵寄類中都調(diào)用 from
方法就顯得很累贅。取而代之地,你可以在配置文件 config/mail.php
中指定一個全局的發(fā)送地址, 如果在 mailable 類中未指定其他 from
地址,則將使用此地址:
'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
此外,您可以在 config / mail.php
配置文件中定義全局 reply_to
地址:
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
配置視圖
你可以在可郵寄類的 build
方法中使用 view
方法來指定渲染郵件內(nèi)容時使用哪個視圖模板,由于每封郵件通常使用 Blade 模板來渲染內(nèi)容,所以你可以在構(gòu)建郵件 HTML 時使用 Blade 模板引擎提供的所有功能:
/** * 構(gòu)建消息. * * @return $this */ public function build(){ return $this->view('emails.orders.shipped'); }
{注:} 你可以創(chuàng)建一個
resources/views/emails
目錄來存放所有郵件模板,當(dāng)然,你也可以將郵件模板放到resources/views
目錄下任意其它位置。
純文本郵件
如果你想要定義一個純文本格式的郵件,可以使用 text
方法。和 view
方法一樣, text
方法接收一個用于渲染郵件內(nèi)容的模板名,你既可以定義純文本消息也可以定義 HTML 消息:
/** * 構(gòu)建消息. * * @return $this */ public function build(){ return $this->view('emails.orders.shipped') ->text('emails.orders.shipped_plain'); }
視圖數(shù)據(jù)
通過公共屬性
通常,我們需要傳遞一些數(shù)據(jù)到渲染郵件的 HTML 視圖以供使用。有兩種方式將數(shù)據(jù)傳遞到視圖,首先,您的 mailable 類中定義的任何公共屬性將自動傳遞給視圖。 因此,您可以將數(shù)據(jù)傳遞到 mailable 類的構(gòu)造函數(shù),并將該數(shù)據(jù)設(shè)置為類的公共屬性:
<?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; /** * 訂單實(shí)例. * * @var Order */ public $order; /** * 創(chuàng)建一個新的消息實(shí)例. * * @return void */ public function __construct(Order $order) { $this->order = $order; } /** * 構(gòu)建消息. * * @return $this */ public function build() { return $this->view('emails.orders.shipped'); } }
數(shù)據(jù)被設(shè)置給公共屬性后,將會在視圖中自動生效,所以你可以像在 Blade 模板中訪問其它數(shù)據(jù)一樣訪問它們:
<div> Price: {{ $order->price }} </div>
通過 with
方法
如果你想要在數(shù)據(jù)發(fā)送到模板之前自定義郵件數(shù)據(jù)的格式,可以通過 with
方法手動傳遞數(shù)據(jù)到視圖。一般情況下,你還是需要通過可郵寄類的構(gòu)造器傳遞數(shù)據(jù),不過,這次你需要設(shè)置數(shù)據(jù)為 protected
或 private
屬性,這樣,這些數(shù)據(jù)就不會在視圖中自動生效。然后,當(dāng)調(diào)用 with
方法時,傳遞數(shù)組數(shù)據(jù)到該方法以便數(shù)據(jù)在視圖模板中生效:
<?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; /** * 訂單實(shí)例. * * @var Order */ protected $order; /** * 創(chuàng)建一個新的實(shí)例. * * @return void */ public function __construct(Order $order) { $this->order = $order; } /** * 構(gòu)建消息. * * @return $this */ public function build() { return $this->view('emails.orders.shipped') ->with([ 'orderName' => $this->order->name, 'orderPrice' => $this->order->price, ]); } }
數(shù)據(jù)通過 with
方法傳遞到視圖后,將會在視圖中自動生效,因此你也可以像在 Blade 模板訪問其它數(shù)據(jù)一樣訪問傳遞過來的數(shù)據(jù):
<div> Price: {{ $orderPrice }} </div>
附件
要在郵件中加入附件,在 build
方法中使用 attach
方法。attach
方法接受文件的絕對路徑作為它的第一個參數(shù):
/** * Build the message. * * @return $this */ public function build() { return $this->view('emails.orders.shipped') ->attach('/path/to/file'); }
附加文件到消息時,你也可以傳遞 數(shù)組
給 attach
方法作為第二個參數(shù),以指定顯示名稱和 / 或是 MIME 類型:
/** * Build the message. * * @return $this */ public function build() { return $this->view('emails.orders.shipped') ->attach('/path/to/file', [ 'as' => 'name.pdf', 'mime' => 'application/pdf', ]); }
從磁盤中添加附件
如果您已在文件存儲上存儲了一個文件,則可以使用 attachFromStorage
方法將其附加到電子郵件中:
/** * 構(gòu)建消息。 * * @return $this */ public function build(){ return $this->view('email.orders.shipped') ->attachFromStorage('/path/to/file'); }
如有必要,您可以使用 attachFromStorage
方法的第二個和第三個參數(shù)指定文件的附件名稱和其他選項(xiàng):
/** * 構(gòu)建消息。 * * @return $this */ public function build(){ return $this->view('email.orders.shipped') ->attachFromStorage('/path/to/file', 'name.pdf', [ 'mime' => 'application/pdf' ]); }
如果需要指定默認(rèn)磁盤以外的存儲磁盤,可以使用 attachFromStorageDisk
方法:
/** * 構(gòu)建消息。 * * @return $this */ public function build(){ return $this->view('email.orders.shipped') ->attachFromStorageDisk('s3', '/path/to/file'); }
原始數(shù)據(jù)附件
attachData
可以使用字節(jié)數(shù)據(jù)作為附件。例如,你可以使用這個方法將內(nèi)存中生成而沒有保存到磁盤中的 PDF 附加到郵件中。attachData
方法第一個參數(shù)接收原始字節(jié)數(shù)據(jù),第二個參數(shù)為文件名,第三個參數(shù)接受一個數(shù)組以指定其他參數(shù):
/** * Build the message. * * @return $this */ public function build() { return $this->view('emails.orders.shipped') ->attachData($this->pdf, 'name.pdf', [ 'mime' => 'application/pdf', ]); }
內(nèi)聯(lián)附件
在郵件中嵌入內(nèi)聯(lián)圖片通常都很麻煩;不過,Laravel 提供了向郵件中附加圖片并獲取適當(dāng)?shù)?CID 的簡便方法??梢允褂绵]件模板中 $message
變量的 embed
方法來嵌入內(nèi)聯(lián)圖片。Laravel 自動使 $message
變量在全部郵件模板中可用,不需要擔(dān)心如何手動傳遞它:
<body> Here is an image: <img src="{{ $message->embed($pathToImage) }}"> </body>
{note}
$message
在文本消息中不可用,因?yàn)槲谋鞠⒉荒苁褂脙?nèi)聯(lián)附件。
嵌入原始數(shù)據(jù)附件
如果已經(jīng)有了希望嵌入郵件模板的原始數(shù)據(jù)串,可以使用 $message
變量的 embedData
方法:
<body> Here is an image from raw data: <img src="{{ $message->embedData($data, $name) }}"> </body>
自定義 SwiftMailer 消息
Mailable
基類的 withSwiftMessage
方法允許你注冊一個回調(diào),它將在發(fā)送消息之前被調(diào)用,原始的 SwiftMailer 消息將作為該回調(diào)的參數(shù):
/** * 構(gòu)建消息。 * * @return $this */ public function build(){ $this->view('emails.orders.shipped'); $this->withSwiftMessage(function ($message) { $message->getHeaders() ->addTextHeader('Custom-Header', 'HeaderValue'); }); }
Markdown 格式的 Mailables 類
Markdown 格式 mailable 消息允許你從預(yù)構(gòu)建模板和 mailable 類中的郵件通知組件獲益。由于消息使用 Markdown 書寫,Laravel 能夠渲染出美觀的、響應(yīng)式的 HTML 模板消息,還能自動生成文本副本。
生成 Markdown 格式的 Mailables 類
要生成一個適用 Markdown 模板的 mailable,可以使用帶有 --markdown
選項(xiàng)的 make:mail
Artisan 命令:
php artisan make:mail OrderShipped --markdown=emails.orders.shipped
然后,在它的 build
方法中配置 mailable,調(diào)用 markdown
方法代替 view
方法。 markdown
方法接受 Markdown 模板名和一個可選的在模板中可用的數(shù)組:
/** * Build the message. * * @return $this */ public function build(){ return $this->from('example@example.com') ->markdown('emails.orders.shipped'); }
編寫 Markdown 消息
Markdown mailable 使用 Blade 組件和 Markdown 語法組合,讓你可以更方便地利用 Laravel 預(yù)制組件構(gòu)建郵件消息:
@component('mail::message') # 訂單已發(fā)貨 Your order has been shipped! @component('mail::button', ['url' => $url]) View Order @endcomponent Thanks,<br> {{ config('app.name') }} @endcomponent
{tip} 編寫 Markdown 郵件時不要使用額外的縮進(jìn)。Markdown 解析器將把縮進(jìn)內(nèi)容渲染成代碼塊。
按鈕組件
按鈕組件渲染一個居中按鈕鏈接。此組建接受兩個參數(shù), url
和可選的 color
。顏色選項(xiàng)支持 primary
、 success
和 error
。你可以隨心所欲地向消息添加任意數(shù)量的按鈕組件:
@component('mail::button', ['url' => $url, 'color' => 'success']) View Order @endcomponent
面板組件
面板組件在面板內(nèi)渲染給定的文字塊,面板與其他消息的背景色略有不同。能讓你繪制一個警示文字塊:
@component('mail::panel') This is the panel content. @endcomponent
表格組件
表格組件允許你將 Markdown 表格轉(zhuǎn)換成 HTML 表格。此組建接受 Markdown 表格作為其內(nèi)容。列對齊支持默認(rèn)的 Markdown 表格對齊語法:
@component('mail::table') | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | @endcomponent
自定義組件
可以將所有 Markdown 郵件組件導(dǎo)出到自己的應(yīng)用,用作自定義組件的模板。若要導(dǎo)出這些組件,使用帶有 laravel-mail
資產(chǎn)標(biāo)簽的 vendor:publish
Artisan 命令:
php artisan vendor:publish --tag=laravel-mail
此命令將 Markdown 郵件組件導(dǎo)出到 resources/views/vendor/mail
目錄。 mail
目錄包含 html
和 text
子目錄, 分別包含各自對應(yīng)的可用組件描述??梢园凑兆约旱囊庠缸远x這些組件。
自定義 CSS
組建導(dǎo)出以后,resources/views/vendor/mail/html/themes
目錄有一個 default.css
文件??梢宰源宋募凶远x CSS,這些樣式將自動內(nèi)聯(lián)到 Markdown 郵件消息的 HTML 表示中。
{tip} 如果想為 Markdown 組件創(chuàng)建完整的新主題,可以在
html/themes
目錄新建一個 CSS 文件,并修改theme
選項(xiàng)。
發(fā)送郵件
若要發(fā)送郵件,使用 Mail
facade 的 to
方法。 to
方法接受 郵件地址、用戶實(shí)例或用戶集合。如果傳遞一個對象或者對象集合,mailer 在設(shè)置收件人時將自動使用它們的 email
和 name
屬性,因此請確保對象的這些屬性可用。一旦制定了收件人,就可以將 mailable 類實(shí)例傳遞給 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{ /** * 發(fā)送給定的訂單。 * * @param Request $request * @param int $orderId * @return Response */ public function ship(Request $request, $orderId) { $order = Order::findOrFail($orderId); // 發(fā)送訂單... Mail::to($request->user())->send(new OrderShipped($order)); } }
在發(fā)送消息時不止可以指定收件人。還可以通過鏈?zhǔn)秸{(diào)用「to」、「cc」、「bcc」一次性指定抄送和密送收件人:
Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->send(new OrderShipped($order));
渲染 Mailable
有時可能希望捕獲 mailable 的 HTML 內(nèi)容,而不發(fā)送它??梢哉{(diào)用 mailable 的 render
方法實(shí)現(xiàn)此目的。此方法返回 mailable 渲染計(jì)算后的字符串:
$invoice = App\Invoice::find(1); return (new App\Mail\InvoicePaid($invoice))->render();
在瀏覽器中預(yù)覽 Mailable
設(shè)計(jì) mailable 模板時,像 Blade 模板一樣在瀏覽器中預(yù)覽和渲染 mailable 是很方便的。這種情況下,Laravel 允許你在路由閉包或控制其中直接返回任意 mailable。返回的 mailable 將在瀏覽器中渲染和顯示,你可以快速預(yù)覽設(shè)計(jì)效果,而不需要將其發(fā)送到真實(shí)的郵件地址:
Route::get('mailable', function () { $invoice = App\Invoice::find(1); return new App\Mail\InvoicePaid($invoice); });
郵件隊(duì)列
將郵件消息加入隊(duì)列
由于發(fā)送郵件消息可能大幅度延長應(yīng)用的響應(yīng)時間,許多開發(fā)者選擇將郵件消息加入隊(duì)列放在后臺發(fā)送。Laravel 使用內(nèi)置的 統(tǒng)一隊(duì)列 API 簡化了這一工作。若要將郵件消息加入隊(duì)列,可以在制定消息的接收者后,使用 Mail
facade 的 queue
方法:
Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->queue(new OrderShipped($order));
此方法自動將作業(yè)推送到隊(duì)列中以便消息在后臺發(fā)送。使用此特性之前,需要 配置隊(duì)列 :
延遲消息隊(duì)列
想要延遲發(fā)送隊(duì)列化的郵件消息,可以使用 later
方法。later
方法的第一個參數(shù)的第一個參數(shù)是標(biāo)示消息何時發(fā)送的 DateTime
實(shí)例:
$when = now()->addMinutes(10);Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->later($when, new OrderShipped($order));
推送到指定隊(duì)列
由于所有使用 make:mail
命令生成的 mailable 類都是用了 Illuminate\Bus\Queueable
trait,因此你可以在任何 mailable 類實(shí)例上調(diào)用 onQueue
和 onConnection
方法來指定消息的連接和隊(duì)列名:
$message = (new OrderShipped($order)) ->onConnection('sqs') ->onQueue('emails');Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->queue($message);
默認(rèn)隊(duì)列
如果一個 mailable 類終是要隊(duì)列化,可以在此類上實(shí)現(xiàn) ShouldQueue
契約。這樣一來,即使你在發(fā)送時調(diào)用了 send
方法, mailable 也將被序列化:
use Illuminate\Contracts\Queue\ShouldQueue; class OrderShipped extends Mailable implements ShouldQueue{ // }
本地化 Mailable
Laravel 允許你使用有別于當(dāng)前語言的區(qū)域設(shè)置發(fā)送 mailable,即使被加入到隊(duì)列中也保留該區(qū)域設(shè)置。
為達(dá)到此目的, Mail
facade 提供了 locale
方法設(shè)置目標(biāo)語言。應(yīng)用在格式化 mailable 是將切換到該區(qū)域設(shè)置,并在格式化完成后恢復(fù)到原來的區(qū)域設(shè)置:
Mail::to($request->user())->locale('es')->send( new OrderShipped($order) );
用戶首選區(qū)域設(shè)置
有時候,應(yīng)用存儲每個用戶的首選區(qū)域設(shè)置。通過在一個或多個模型上實(shí)現(xiàn) HasLocalePreference
契約,可以通知 Laravel 再發(fā)送郵件時使用預(yù)存的區(qū)域設(shè)置:
use Illuminate\Contracts\Translation\HasLocalePreference; class User extends Model implements HasLocalePreference{ /** * 獲取用戶首選區(qū)域設(shè)置。 * * @return string */ public function preferredLocale() { return $this->locale; } }
一旦實(shí)現(xiàn)了此接口,Laravel 在向此模型發(fā)送 mailable 和通知時,將自動使用首選區(qū)域設(shè)置。因此在使用此接口時不需要調(diào)用 locale
方法:
Mail::to($request->user())->send(new OrderShipped($order));
郵件 & 本地開發(fā)
在開發(fā)發(fā)送郵件的應(yīng)用時,你也許不想真的向?qū)崟r郵件地址發(fā)送郵件。Laravel 為本地開發(fā)期間提供了幾個 「禁用」真實(shí)發(fā)送的途徑。
日志驅(qū)動
log
郵件驅(qū)動采取將郵件消息寫入日志取代發(fā)送郵件,已備查看。應(yīng)用環(huán)境配置的更多消息,請查閱 配置文檔。
通用配置
Laravel 為通過框架發(fā)送的郵件提供了指定常用收件人的其他解決方案。通過此方法,應(yīng)用生成的郵件都將發(fā)送到指定地址,以取代發(fā)送消息時指定的真實(shí)地址。可以借助 config/mail.php
配置文件的 to
選項(xiàng)實(shí)現(xiàn)此目的:
'to' => [ 'address' => 'example@example.com', 'name' => 'Example' ],
Mailtrap
最后,你可以使用 Mailtrap 服務(wù)和 smtp
驅(qū)動發(fā)送郵件消息到 「虛擬」郵箱,這樣就可以在真實(shí)的郵件客戶端查看郵件消息。此方法的好處是允許你在 Mailtrap 的消息閱覽器中實(shí)際查看最終的郵件。
事件
Laravel 在處理郵件消息發(fā)送時觸發(fā)兩個事件。MessageSending
事件在消息發(fā)送前觸發(fā),MessageSent
事件則在消息發(fā)送后觸發(fā)。切記,這些事件是在郵件被 發(fā)送 時觸發(fā),而不是在隊(duì)列化的時候??梢栽? EventServiceProvider
中注冊此事件的偵聽器:
/** * 為應(yīng)用映射事件偵聽器。 * * @var array */ protected $listen = [ 'Illuminate\Mail\Events\MessageSending' => [ 'App\Listeners\LogSendingMessage', ], 'Illuminate\Mail\Events\MessageSent' => [ 'App\Listeners\LogSentMessage', ], ];