簡介 (Introduction)
傳送電子郵件不一定很複雜。Laravel 提供了一個乾淨、簡單的電子郵件 API,由流行的 Symfony Mailer 元件提供支援。Laravel 和 Symfony Mailer 為透過 SMTP、Mailgun、Postmark、Resend、Amazon SES 和 sendmail 傳送電子郵件提供了驅動程式,讓你可以快速開始透過你選擇的本地或雲端服務傳送郵件。
設定 (Configuration)
Laravel 的電子郵件服務可以透過應用程式的 config/mail.php 設定檔進行設定。在這個檔案中設定的每個郵件程式都可以有自己獨特的設定,甚至有自己獨特的「傳輸 (transport)」,允許你的應用程式使用不同的電子郵件服務來傳送特定的電子郵件訊息。例如,你的應用程式可能使用 Postmark 傳送交易性電子郵件,同時使用 Amazon SES 傳送大量電子郵件。
在你的 mail 設定檔中,你會找到一個 mailers 設定陣列。這個陣列包含了 Laravel 支援的每個主要郵件驅動程式 / 傳輸的範例設定項目,而 default 設定值決定了當你的應用程式需要傳送電子郵件訊息時,預設會使用哪個郵件程式。
驅動程式 / 傳輸先決條件 (Driver / Transport Prerequisites) (Driver Prerequisites)
基於 API 的驅動程式,如 Mailgun、Postmark 和 Resend,通常比透過 SMTP 伺服器傳送郵件更簡單、更快速。只要可能,我們建議你使用這些驅動程式之一。
Mailgun 驅動程式 (Mailgun Driver)
要使用 Mailgun 驅動程式,請透過 Composer 安裝 Symfony 的 Mailgun Mailer 傳輸:
composer require symfony/mailgun-mailer symfony/http-client
接下來,你需要對應用程式的 config/mail.php 設定檔進行兩項更改。首先,將你的預設郵件程式設定為 mailgun:
'default' => env('MAIL_MAILER', 'mailgun'),
其次,將以下設定陣列新增到你的 mailers 陣列中:
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
設定好應用程式的預設郵件程式後,將以下選項新增到你的 config/services.php 設定檔中:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
'scheme' => 'https',
],
如果你不使用美國 Mailgun 區域,你可以在 services 設定檔中定義你的區域端點:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
'scheme' => 'https',
],
Postmark 驅動程式 (Postmark Driver)
要使用 Postmark 驅動程式,請透過 Composer 安裝 Symfony 的 Postmark Mailer 傳輸:
composer require symfony/postmark-mailer symfony/http-client
接下來,將應用程式的 config/mail.php 設定檔中的 default 選項設定為 postmark。設定好應用程式的預設郵件程式後,確保你的 config/services.php 設定檔包含以下選項:
'postmark' => [
'key' => env('POSTMARK_API_KEY'),
],
如果你想指定給定郵件程式應使用的 Postmark 訊息串流,你可以將 message_stream_id 設定選項新增到郵件程式的設定陣列中。這個設定陣列可以在你的應用程式的 config/mail.php 設定檔中找到:
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],
這樣你也能夠設定多個具有不同訊息串流的 Postmark 郵件程式。
Resend 驅動程式 (Resend Driver)
要使用 Resend 驅動程式,請透過 Composer 安裝 Resend 的 PHP SDK:
composer require resend/resend-php
接下來,將應用程式的 config/mail.php 設定檔中的 default 選項設定為 resend。設定好應用程式的預設郵件程式後,確保你的 config/services.php 設定檔包含以下選項:
'resend' => [
'key' => env('RESEND_API_KEY'),
],
SES 驅動程式 (SES Driver)
要使用 Amazon SES 驅動程式,你必須先安裝 Amazon AWS SDK for PHP。你可以透過 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'),
],
要透過 session token 使用 AWS 臨時憑證,你可以將 token 鍵新增到你的應用程式的 SES 設定中:
'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 標頭:
/**
* Get the message headers.
*/
public function headers(): Headers
{
return new Headers(
text: [
'X-Ses-List-Management-Options' => 'contactListName=MyContactList;topicName=MyTopic',
],
);
}
如果你想定義 額外選項,讓 Laravel 在傳送電子郵件時傳遞給 AWS SDK 的 SendEmail 方法,你可以在你的 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',
'EmailTags' => [
['Name' => 'foo', 'Value' => 'bar'],
],
],
],
故障轉移設定 (Failover Configuration)
有時,你設定用來傳送應用程式郵件的外部服務可能會停機。在這些情況下,定義一個或多個備份郵件傳遞設定會很有用,以便在你的主要傳遞驅動程式停機時使用。
要實現這一點,你應該在應用程式的 mail 設定檔中定義一個使用 failover 傳輸的郵件程式。你的應用程式的 failover 郵件程式的設定陣列應該包含一個 mailers 陣列,該陣列參考了應選擇用於傳遞的已設定郵件程式的順序:
'mailers' => [
'failover' => [
'transport' => 'failover',
'mailers' => [
'postmark',
'mailgun',
'sendmail',
],
'retry_after' => 60,
],
// ...
],
一旦定義了故障轉移郵件程式,你應該將此郵件程式設定為應用程式使用的預設郵件程式,方法是在應用程式的 mail 設定檔中將其名稱指定為 default 設定鍵的值:
'default' => env('MAIL_MAILER', 'failover'),
輪詢設定 (Round Robin Configuration)
roundrobin 傳輸允許你將郵件工作負載分配到多個郵件程式。要開始使用,請在應用程式的 mail 設定檔中定義一個使用 roundrobin 傳輸的郵件程式。你的應用程式的 roundrobin 郵件程式的設定陣列應該包含一個 mailers 陣列,該陣列參考了應用於傳遞的已設定郵件程式:
'mailers' => [
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
'retry_after' => 60,
],
// ...
],
一旦定義了輪詢郵件程式,你應該將此郵件程式設定為應用程式使用的預設郵件程式,方法是在應用程式的 mail 設定檔中將其名稱指定為 default 設定鍵的值:
'default' => env('MAIL_MAILER', 'roundrobin'),
輪詢傳輸會從已設定的郵件程式列表中隨機選擇一個郵件程式,然後為每封後續電子郵件切換到下一個可用的郵件程式。與有助於實現 高可用性 的 failover 傳輸相反,roundrobin 傳輸提供 _負載平衡_。
產生 Mailable (Generating Mailables)
在建立 Laravel 應用程式時,應用程式傳送的每種類型的電子郵件都表示為一個「mailable」類別。這些類別儲存在 app/Mail 目錄中。如果在你的應用程式中沒有看到這個目錄,請不要擔心,因為當你使用 make:mail Artisan 指令建立第一個 mailable 類別時,它將為你產生:
php artisan make:mail OrderShipped
撰寫 Mailable (Writing Mailables)
一旦你產生了一個 mailable 類別,打開它,這樣我們就可以探索它的內容。Mailable 類別的設定是在幾個方法中完成的,包括 envelope、content 和 attachments 方法。
envelope 方法傳回一個 Illuminate\Mail\Mailables\Envelope 物件,該物件定義了訊息的主旨,有時還定義了收件者。content 方法傳回一個 Illuminate\Mail\Mailables\Content 物件,該物件定義了將用於產生訊息內容的 Blade 樣板。
設定寄件者 (Configuring the Sender)
使用 Envelope (Using the Envelope)
首先,讓我們探索設定電子郵件的寄件者。或者換句話說,電子郵件將「來自」誰。有兩種方法可以設定寄件者。首先,你可以在訊息的 envelope 上指定「from」地址:
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
subject: 'Order Shipped',
);
}
如果你願意,你也可以指定一個 replyTo 地址:
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
replyTo: [
new Address('taylor@example.com', 'Taylor Otwell'),
],
subject: 'Order Shipped',
);
使用全域 from 地址 (Using a Global from Address)
然而,如果你的應用程式的所有電子郵件都使用相同的「from」地址,那麼將其新增到你產生的每個 mailable 類別中可能會變得很麻煩。相反,你可以在 config/mail.php 設定檔中指定一個全域「from」地址。如果在 mailable 類別中沒有指定其他「from」地址,將使用此地址:
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
此外,你可以在 config/mail.php 設定檔中定義一個全域「reply_to」地址:
'reply_to' => [
'address' => 'example@example.com',
'name' => 'App Name',
],
設定視圖 (Configuring the View)
在 mailable 類別的 content 方法中,你可以定義 view,或者在渲染電子郵件內容時應使用哪個樣板。由於每封電子郵件通常使用 Blade 樣板 來渲染其內容,因此在建立電子郵件的 HTML 時,你擁有 Blade 樣板引擎的全部功能和便利性:
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
[!NOTE] 你可能希望建立一個
resources/views/mail目錄來存放所有的電子郵件樣板;但是,你可以自由地將它們放在resources/views目錄中的任何位置。
純文字電子郵件 (Plain Text Emails)
如果你想定義電子郵件的純文字版本,你可以在建立訊息的 Content 定義時指定純文字樣板。就像 view 參數一樣,text 參數應該是一個樣板名稱,將用於渲染電子郵件的內容。你可以自由地定義訊息的 HTML 和純文字版本:
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
}
為了清楚起見,html 參數可以用作 view 參數的別名:
return new Content(
html: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
視圖資料 (View Data)
透過公開屬性 (Via Public Properties)
通常,你會希望將一些資料傳遞給視圖,以便在渲染電子郵件的 HTML 時使用。有兩種方法可以讓資料在視圖中可用。首先,在 mailable 類別上定義的任何公開屬性都將自動在視圖中可用。因此,例如,你可以將資料傳遞到 mailable 類別的建構函式中,並將該資料設定為類別上定義的公開屬性:
<?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;
/**
* Create a new message instance.
*/
public function __construct(
public Order $order,
) {}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
}
一旦資料被設定為公開屬性,它將自動在你的視圖中可用,因此你可以像存取 Blade 樣板中的任何其他資料一樣存取它:
<div>
Price: {{ $order->price }}
</div>
透過 with 參數 (Via the with Parameter)
如果你想在電子郵件的資料傳送到樣板之前自訂其格式,你可以透過 Content 定義的 with 參數手動將資料傳遞給視圖。通常,你仍然會透過 mailable 類別的建構函式傳遞資料;但是,你應該將此資料設定為 protected 或 private 屬性,以便資料不會自動在樣板中可用:
<?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;
/**
* Create a new message instance.
*/
public function __construct(
protected Order $order,
) {}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
with: [
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
],
);
}
}
一旦資料透過 with 參數傳遞,它將自動在你的視圖中可用,因此你可以像存取 Blade 樣板中的任何其他資料一樣存取它:
<div>
Price: {{ $orderPrice }}
</div>
附件 (Attachments)
要將附件新增到電子郵件中,你將附件新增到訊息的 attachments 方法傳回的陣列中。首先,你可以透過提供檔案路徑給 Attachment 類別提供的 fromPath 方法來新增附件:
use Illuminate\Mail\Mailables\Attachment;
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file'),
];
}
將檔案附加到訊息時,你還可以使用 as 和 withMime 方法指定附件的顯示名稱和 / 或 MIME 類型:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
從磁碟附加檔案 (Attaching Files From Disk)
如果你已將檔案儲存在其中一個 檔案系統磁碟 上,你可以使用 fromStorage 附件方法將其附加到電子郵件中:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file'),
];
}
當然,你也可以指定附件的名稱和 MIME 類型:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
如果你需要指定預設磁碟以外的儲存磁碟,可以使用 fromStorageDisk 方法:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorageDisk('s3', '/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
原始資料附件 (Raw Data Attachments)
fromData 附件方法可用於將原始位元組字串作為附件附加。例如,如果你在記憶體中產生了一個 PDF,並且想將其附加到電子郵件中而不將其寫入磁碟,則可以使用此方法。fromData 方法接受一個閉包,該閉包解析原始資料位元組以及應指派給附件的名稱:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromData(fn () => $this->pdf, 'Report.pdf')
->withMime('application/pdf'),
];
}
內嵌附件 (Inline Attachments)
將內嵌圖片嵌入電子郵件通常很麻煩;然而,Laravel 提供了一種方便的方法將圖片附加到電子郵件中。要嵌入內嵌圖片,請在電子郵件樣板中的 $message 變數上使用 embed 方法。Laravel 會自動將 $message 變數提供給所有的電子郵件樣板,因此你不需要擔心手動傳入它:
<body>
Here is an image:
<img src="{{ $message->embed($pathToImage) }}" />
</body>
[!WARNING] >
$message變數在純文字訊息樣板中不可用,因為純文字訊息不使用內嵌附件。
嵌入原始資料附件 (Embedding Raw Data Attachments)
如果你已經有一個希望嵌入到電子郵件樣板中的原始圖片資料字串,你可以在 $message 變數上呼叫 embedData 方法。呼叫 embedData 方法時,你需要提供一個應指派給嵌入圖片的檔案名稱:
<body>
Here is an image from raw data:
<img src="{{ $message->embedData($data, 'example-image.jpg') }}" />
</body>
可附加物件 (Attachable Objects)
雖然透過簡單的字串路徑將檔案附加到訊息通常就足夠了,但在許多情況下,應用程式中的可附加實體由類別表示。例如,如果你的應用程式將照片附加到訊息中,你的應用程式可能也有一個表示該照片的 Photo 模型。在這種情況下,簡單地將 Photo 模型傳遞給 attach 方法不是很方便嗎?可附加物件允許你這樣做。
要開始使用,請在將可附加到訊息的物件上實作 Illuminate\Contracts\Mail\Attachable 介面。此介面規定你的類別定義一個 toMailAttachment 方法,該方法傳回一個 Illuminate\Mail\Attachment 實例:
<?php
namespace App\Models;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Attachment;
class Photo extends Model implements Attachable
{
/**
* Get the attachable representation of the model.
*/
public function toMailAttachment(): Attachment
{
return Attachment::fromPath('/path/to/file');
}
}
一旦你定義了可附加物件,你可以在建立電子郵件訊息時從 attachments 方法傳回該物件的實例:
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [$this->photo];
}
當然,附件資料可能儲存在遠端檔案儲存服務上,例如 Amazon S3。因此,Laravel 也允許你從儲存在應用程式的其中一個 檔案系統磁碟 上的資料產生附件實例:
// Create an attachment from a file on your default disk...
return Attachment::fromStorage($this->path);
// Create an attachment from a file on a specific disk...
return Attachment::fromStorageDisk('backblaze', $this->path);
此外,你可以透過記憶體中的資料建立附件實例。要實現這一點,請向 fromData 方法提供一個閉包。閉包應傳回表示附件的原始資料:
return Attachment::fromData(fn () => $this->content, 'Photo Name');
Laravel 還提供了額外的方法,你可以用來自訂附件。例如,你可以使用 as 和 withMime 方法來自訂檔案的名稱和 MIME 類型:
return Attachment::fromPath('/path/to/file')
->as('Photo Name')
->withMime('image/jpeg');
標頭 (Headers)
有時你可能需要將額外的標頭附加到外寄訊息中。例如,你可能需要設定自訂 Message-Id 或其他任意文字標頭。
要實現這一點,請在你的 mailable 上定義一個 headers 方法。headers 方法應傳回一個 Illuminate\Mail\Mailables\Headers 實例。此類別接受 messageId、references 和 text 參數。當然,你可以只提供你的特定訊息所需的參數:
use Illuminate\Mail\Mailables\Headers;
/**
* Get the message 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',
],
);
}
標籤與中繼資料 (Tags and Metadata)
一些第三方電子郵件提供商(如 Mailgun 和 Postmark)支援訊息「標籤」和「中繼資料」,可用於分組和追蹤應用程式傳送的電子郵件。你可以透過 Envelope 定義將標籤和中繼資料新增到電子郵件訊息中:
use Illuminate\Mail\Mailables\Envelope;
/**
* Get the message 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 "tags" 附加到訊息中。
自訂 Symfony 訊息 (Customizing the Symfony Message)
Laravel 的郵件功能由 Symfony Mailer 提供支援。Laravel 允許你註冊自訂回呼,這些回呼將在傳送訊息之前使用 Symfony Message 實例呼叫。這讓你有機會在傳送訊息之前對其進行深度自訂。要實現這一點,請在你的 Envelope 定義上定義一個 using 參數:
use Illuminate\Mail\Mailables\Envelope;
use Symfony\Component\Mime\Email;
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Order Shipped',
using: [
function (Email $message) {
// ...
},
]
);
}
Markdown Mailable (Markdown Mailables)
Markdown mailable 訊息允許你在 mailable 中利用 郵件通知 的預建樣板和元件。由於訊息是用 Markdown 撰寫的,Laravel 能夠為訊息渲染漂亮、響應式的 HTML 樣板,同時自動產生純文字對應版本。
產生 Markdown Mailable (Generating Markdown Mailables)
要產生具有相應 Markdown 樣板的 mailable,你可以使用 make:mail Artisan 指令的 --markdown 選項:
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
然後,在 content 方法中設定 mailable Content 定義時,使用 markdown 參數代替 view 參數:
use Illuminate\Mail\Mailables\Content;
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.orders.shipped',
with: [
'url' => $this->orderUrl,
],
);
}
撰寫 Markdown 訊息 (Writing Markdown Messages)
Markdown mailable 使用 Blade 元件和 Markdown 語法的組合,這允許你輕鬆建構郵件訊息,同時利用 Laravel 的預建電子郵件 UI 元件:
<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 解析器會將縮排的內容渲染為程式碼區塊。
按鈕元件 (Button Component)
按鈕元件渲染一個置中的按鈕連結。該元件接受兩個參數,一個 url 和一個可選的 color。支援的顏色有 primary、success 和 error。你可以根據需要向訊息新增任意數量的按鈕元件:
<x-mail::button :url="$url" color="success">
View Order
</x-mail::button>
面板元件 (Panel Component)
面板元件在一個背景顏色與訊息其餘部分略有不同的面板中渲染給定的文字區塊。這允許你引起對給定文字區塊的注意:
<x-mail::panel>
This is the panel content.
</x-mail::panel>
表格元件 (Table Component)
表格元件允許你將 Markdown 表格轉換為 HTML 表格。該元件接受 Markdown 表格作為其內容。表格欄位對齊支援使用預設的 Markdown 表格對齊語法:
<x-mail::table>
| Laravel | Table | Example |
| ------------- | :-----------: | ------------: |
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
</x-mail::table>
自訂元件 (Customizing the Components)
你可以將所有的 Markdown 郵件元件匯出到你自己的應用程式以進行自訂。要匯出元件,請使用 vendor:publish Artisan 指令發佈 laravel-mail 資產標籤:
php artisan vendor:publish --tag=laravel-mail
此指令將把 Markdown 郵件元件發佈到 resources/views/vendor/mail 目錄。mail 目錄將包含一個 html 和一個 text 目錄,每個目錄都包含每個可用元件的相應表示。你可以自由地自訂這些元件。
自訂 CSS (Customizing the CSS)
匯出元件後,resources/views/vendor/mail/html/themes 目錄將包含一個 default.css 檔案。你可以自訂此檔案中的 CSS,你的樣式將自動轉換為 Markdown 郵件訊息的 HTML 表示中的內嵌 CSS 樣式。
如果你想為 Laravel 的 Markdown 元件建立一個全新的主題,你可以將 CSS 檔案放在 html/themes 目錄中。命名並儲存 CSS 檔案後,更新應用程式的 config/mail.php 設定檔中的 theme 選項以符合新主題的名稱。
要為個別 mailable 自訂主題,你可以將 mailable 類別的 $theme 屬性設定為傳送該 mailable 時應使用的主題名稱。
傳送郵件 (Sending Mail)
要傳送訊息,請使用 Mail facade 上的 to 方法。to 方法接受電子郵件地址、使用者實例或使用者集合。如果你傳遞一個物件或物件集合,郵件程式在確定電子郵件的收件者時將自動使用它們的 email 和 name 屬性,因此請確保這些屬性在你的物件上可用。一旦你指定了收件者,你可以將 mailable 類別的實例傳遞給 send 方法:
<?php
namespace App\Http\Controllers;
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
{
/**
* Ship the given order.
*/
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
// Ship the order...
Mail::to($request->user())->send(new OrderShipped($order));
return redirect('/orders');
}
}
在傳送訊息時,你不僅限於指定「to」收件者。你可以透過鏈接各自的方法來自由設定「to」、「cc」和「bcc」收件者:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
遍歷收件者 (Looping Over Recipients)
偶爾,你可能需要透過遍歷收件者 / 電子郵件地址陣列將 mailable 傳送給收件者列表。然而,由於 to 方法將電子郵件地址附加到 mailable 的收件者列表中,因此循環的每次迭代都會向每個先前的收件者傳送另一封電子郵件。因此,你應該始終為每個收件者重新建立 mailable 實例:
foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}
透過特定郵件程式傳送郵件 (Sending Mail via a Specific Mailer)
預設情況下,Laravel 將使用應用程式 mail 設定檔中設定為 default 郵件程式的郵件程式傳送電子郵件。但是,你可以使用 mailer 方法使用特定的郵件程式設定傳送訊息:
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
佇列郵件 (Queueing Mail)
佇列郵件訊息 (Queueing a Mail Message)
由於傳送電子郵件訊息可能會對應用程式的回應時間產生負面影響,因此許多開發人員選擇將電子郵件訊息排入佇列以進行背景傳送。Laravel 使用其內建的 統一佇列 API 讓這變得容易。要將郵件訊息排入佇列,請在指定訊息的收件者後,使用 Mail facade 上的 queue 方法:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
此方法將自動處理將工作推送到佇列中,以便在背景傳送訊息。在使用此功能之前,你需要 設定你的佇列。
延遲訊息佇列 (Delayed Message Queueing)
如果你希望延遲傳送已排入佇列的電子郵件訊息,可以使用 later 方法。作為其第一個參數,later 方法接受一個 DateTime 實例,指示應何時傳送訊息:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later(now()->addMinutes(10), new OrderShipped($order));
推送到特定佇列 (Pushing to Specific Queues)
由於使用 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);
預設佇列 (Queueing by Default)
如果你有希望始終排入佇列的 mailable 類別,可以在類別上實作 ShouldQueue contract。現在,即使你在郵寄時呼叫 send 方法,mailable 仍將被排入佇列,因為它實作了 contract:
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
佇列 Mailable 與資料庫交易 (Queued Mailables and Database Transactions)
當佇列 mailable 在資料庫交易中被分派時,它們可能會在資料庫交易提交之前由佇列處理。發生這種情況時,你在資料庫交易期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,在交易中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果你的 mailable 依賴這些模型,則在處理傳送佇列 mailable 的工作時可能會發生意外錯誤。
如果你的佇列連線的 after_commit 設定選項設定為 false,你仍然可以透過在傳送郵件訊息時呼叫 afterCommit 方法來指示特定的佇列 mailable 應在所有開啟的資料庫交易提交後分派:
Mail::to($request->user())->send(
(new OrderShipped($order))->afterCommit()
);
或者,你可以從 mailable 的建構函式中呼叫 afterCommit 方法:
<?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;
/**
* Create a new message instance.
*/
public function __construct()
{
$this->afterCommit();
}
}
[!NOTE] 要了解有關解決這些問題的更多資訊,請查看有關 佇列工作和資料庫交易 的文件。
佇列電子郵件失敗 (Queued Email Failures)
當佇列電子郵件失敗時,如果已定義,將呼叫佇列 mailable 類別上的 failed 方法。導致佇列電子郵件失敗的 Throwable 實例將傳遞給 failed 方法:
<?php
namespace App\Mail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Throwable;
class OrderDelayed extends Mailable implements ShouldQueue
{
use SerializesModels;
/**
* Handle a queued email's failure.
*/
public function failed(Throwable $exception): void
{
// ...
}
}
渲染 Mailable (Rendering Mailables)
有時你可能希望擷取 mailable 的 HTML 內容而不傳送它。要實現這一點,你可以呼叫 mailable 的 render 方法。此方法將以字串形式傳回 mailable 的評估 HTML 內容:
use App\Mail\InvoicePaid;
use App\Models\Invoice;
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))->render();
在瀏覽器中預覽 Mailable (Previewing Mailables in the Browser)
在設計 mailable 的樣板時,能夠像典型的 Blade 樣板一樣在瀏覽器中快速預覽渲染的 mailable 是很方便的。因此,Laravel 允許你直接從路由閉包或控制器傳回任何 mailable。當傳回 mailable 時,它將被渲染並顯示在瀏覽器中,允許你快速預覽其設計,而無需將其傳送到實際的電子郵件地址:
Route::get('/mailable', function () {
$invoice = App\Models\Invoice::find(1);
return new App\Mail\InvoicePaid($invoice);
});
在地化 Mailable (Localizing Mailables)
Laravel 允許你以請求的目前語言環境以外的語言環境傳送 mailable,如果郵件已排入佇列,甚至會記住此語言環境。
要實現這一點,Mail facade 提供了一個 locale 方法來設定所需的語言。當評估 mailable 的樣板時,應用程式將切換到此語言環境,然後在評估完成後恢復到先前的語言環境:
Mail::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
使用者偏好語言環境 (User Preferred Locales)
有時,應用程式會儲存每個使用者的偏好語言環境。透過在一個或多個模型上實作 HasLocalePreference contract,你可以指示 Laravel 在傳送郵件時使用此儲存的語言環境:
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Get the user's preferred locale.
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
一旦你實作了介面,Laravel 將在向模型傳送 mailable 和通知時自動使用偏好的語言環境。因此,使用此介面時無需呼叫 locale 方法:
Mail::to($request->user())->send(new OrderShipped($order));
測試 (Testing)
測試 Mailable 內容 (Testing Mailable Content)
Laravel 提供了各種方法來檢查你的 mailable 的結構。此外,Laravel 提供了幾個方便的方法來測試你的 mailable 是否包含你期望的內容:
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->assertDontSeeInHtml('Invoice Not Paid');
$mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
$mailable->assertSeeInText($user->email);
$mailable->assertDontSeeInText('Invoice Not Paid');
$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']);
});
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->assertDontSeeInHtml('Invoice Not Paid');
$mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
$mailable->assertSeeInText($user->email);
$mailable->assertDontSeeInText('Invoice Not Paid');
$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']);
}
正如你所期望的,「HTML」斷言斷言你的 mailable 的 HTML 版本包含給定的字串,而「text」斷言斷言你的 mailable 的純文字版本包含給定的字串。
測試 Mailable 傳送 (Testing Mailable Sending)
我們建議將 mailable 內容的測試與斷言給定 mailable 已「傳送」給特定使用者的測試分開。通常,mailable 的內容與你正在測試的程式碼無關,只需斷言 Laravel 已被指示傳送給定的 mailable 就足夠了。
你可以使用 Mail facade 的 fake 方法來防止傳送郵件。呼叫 Mail facade 的 fake 方法後,你可以斷言 mailable 已被指示傳送給使用者,甚至檢查 mailable 接收到的資料:
<?php
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
test('orders can be shipped', function () {
Mail::fake();
// Perform order shipping...
// Assert that no mailables were sent...
Mail::assertNothingSent();
// Assert that a mailable was sent...
Mail::assertSent(OrderShipped::class);
// Assert a mailable was sent twice...
Mail::assertSent(OrderShipped::class, 2);
// Assert a mailable was sent to an email address...
Mail::assertSent(OrderShipped::class, 'example@laravel.com');
// Assert a mailable was sent to multiple email addresses...
Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);
// Assert a mailable was not sent...
Mail::assertNotSent(AnotherMailable::class);
// Assert a mailable was sent twice...
Mail::assertSentTimes(OrderShipped::class, 2);
// Assert 3 total mailables were sent...
Mail::assertSentCount(3);
});
<?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();
// Perform order shipping...
// Assert that no mailables were sent...
Mail::assertNothingSent();
// Assert that a mailable was sent...
Mail::assertSent(OrderShipped::class);
// Assert a mailable was sent twice...
Mail::assertSent(OrderShipped::class, 2);
// Assert a mailable was sent to an email address...
Mail::assertSent(OrderShipped::class, 'example@laravel.com');
// Assert a mailable was sent to multiple email addresses...
Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);
// Assert a mailable was not sent...
Mail::assertNotSent(AnotherMailable::class);
// Assert a mailable was sent twice...
Mail::assertSentTimes(OrderShipped::class, 2);
// Assert 3 total mailables were sent...
Mail::assertSentCount(3);
}
}
如果你正在將 mailable 排入佇列以在背景傳遞,你應該使用 assertQueued 方法而不是 assertSent:
Mail::assertQueued(OrderShipped::class);
Mail::assertNotQueued(OrderShipped::class);
Mail::assertNothingQueued();
Mail::assertQueuedCount(3);
你可以將閉包傳遞給 assertSent、assertNotSent、assertQueued 或 assertNotQueued 方法,以斷言傳送的 mailable 通過了給定的「真值測試」。如果至少有一個傳送的 mailable 通過了給定的真值測試,則斷言將成功:
Mail::assertSent(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
當呼叫 Mail facade 的斷言方法時,提供的閉包接受的 mailable 實例公開了有用的方法來檢查 mailable:
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...') &&
$mail->hasReplyTo('...') &&
$mail->hasFrom('...') &&
$mail->hasSubject('...') &&
$mail->usesMailer('ses');
});
mailable 實例還包含幾個有用的方法來檢查 mailable 上的附件:
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 方法:
Mail::assertNothingOutgoing();
Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
郵件與本地開發 (Mail and Local Development)
在開發傳送電子郵件的應用程式時,你可能不希望將電子郵件實際傳送到真實的電子郵件地址。Laravel 提供了幾種方法在本地開發期間「停用」電子郵件的實際傳送。
Log 驅動程式 (Log Driver)
log 郵件驅動程式不會傳送你的電子郵件,而是將所有電子郵件訊息寫入你的日誌檔案以供檢查。通常,此驅動程式僅在本地開發期間使用。有關依環境設定應用程式的更多資訊,請查看 設定文件。
HELO / Mailtrap / Mailpit
或者,你可以使用像 HELO 或 Mailtrap 這樣的服務以及 smtp 驅動程式將你的電子郵件訊息傳送到「虛擬」信箱,你可以在其中在真正的電子郵件客戶端中查看它們。這種方法的好處是允許你在 Mailtrap 的訊息檢視器中實際檢查最終的電子郵件。
如果你使用 Laravel Sail,你可以使用 Mailpit 預覽你的訊息。當 Sail 執行時,你可以透過 http://localhost:8025 存取 Mailpit 介面。
使用全域 to 地址 (Using a Global to Address)
最後,你可以透過呼叫 Mail facade 提供的 alwaysTo 方法來指定一個全域「to」地址。通常,此方法應從應用程式的其中一個服務提供者的 boot 方法中呼叫:
use Illuminate\Support\Facades\Mail;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
if ($this->app->environment('local')) {
Mail::alwaysTo('taylor@example.com');
}
}
使用 alwaysTo 方法時,郵件訊息上的任何其他「cc」或「bcc」地址都將被移除。
事件 (Events)
Laravel 在傳送郵件訊息時會分派兩個事件。MessageSending 事件在訊息傳送之前分派,而 MessageSent 事件在訊息傳送之後分派。請記住,這些事件是在郵件被 傳送 時分派的,而不是在排入佇列時分派的。你可以在應用程式中為這些事件建立 事件監聽器:
use Illuminate\Mail\Events\MessageSending;
// use Illuminate\Mail\Events\MessageSent;
class LogMessage
{
/**
* Handle the event.
*/
public function handle(MessageSending $event): void
{
// ...
}
}
自訂傳輸 (Custom Transports)
Laravel 包含各種郵件傳輸;但是,你可能希望編寫自己的傳輸以透過 Laravel 開箱即用不支援的其他服務傳遞電子郵件。要開始使用,請定義一個繼承 Symfony\Component\Mailer\Transport\AbstractTransport 類別的類別。然後,在你的傳輸上實作 doSend 和 __toString 方法:
<?php
namespace App\Mail;
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
{
/**
* Create a new Mailchimp transport instance.
*/
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(),
]]);
}
/**
* Get the string representation of the transport.
*/
public function __toString(): string
{
return 'mailchimp';
}
}
一旦你定義了自訂傳輸,你可以透過 Mail facade 提供的 extend 方法註冊它。通常,這應該在應用程式的 AppServiceProvider 的 boot 方法中完成。$config 參數將傳遞給提供給 extend 方法的閉包。此參數將包含在應用程式的 config/mail.php 設定檔中為郵件程式定義的設定陣列:
use App\Mail\MailchimpTransport;
use Illuminate\Support\Facades\Mail;
use MailchimpTransactional\ApiClient;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Mail::extend('mailchimp', function (array $config = []) {
$client = new ApiClient;
$client->setApiKey($config['key']);
return new MailchimpTransport($client);
});
}
一旦你的自訂傳輸被定義並註冊,你可以在應用程式的 config/mail.php 設定檔中建立一個使用新傳輸的郵件程式定義:
'mailchimp' => [
'transport' => 'mailchimp',
'key' => env('MAILCHIMP_API_KEY'),
// ...
],
額外的 Symfony 傳輸 (Additional Symfony Transports)
Laravel 包含對一些現有的 Symfony 維護的郵件傳輸(如 Mailgun 和 Postmark)的支援。但是,你可能希望擴充 Laravel 以支援其他 Symfony 維護的傳輸。你可以透過 Composer 請求必要的 Symfony mailer 並向 Laravel 註冊傳輸來做到這一點。例如,你可以安裝並註冊 "Brevo" (前身為 "Sendinblue") Symfony mailer:
composer require symfony/brevo-mailer symfony/http-client
一旦安裝了 Brevo mailer 套件,你可以將 Brevo API 憑證的項目新增到應用程式的 services 設定檔中:
'brevo' => [
'key' => env('BREVO_API_KEY'),
],
接下來,你可以使用 Mail facade 的 extend 方法向 Laravel 註冊傳輸。通常,這應該在服務提供者的 boot 方法中完成:
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory;
use Symfony\Component\Mailer\Transport\Dsn;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Mail::extend('brevo', function () {
return (new BrevoTransportFactory)->create(
new Dsn(
'brevo+api',
'default',
config('services.brevo.key')
)
);
});
}
一旦你的傳輸被註冊,你可以在應用程式的 config/mail.php 設定檔中建立一個使用新傳輸的郵件程式定義:
'brevo' => [
'transport' => 'brevo',
// ...
],