跳至內容

郵件

簡介

傳送電子郵件不必複雜。Laravel 提供由流行的 Symfony Mailer 元件驅動的簡潔、簡單的電子郵件 API。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 傳輸

composer require symfony/mailgun-mailer symfony/http-client

接下來,將您應用程式的 config/mail.php 設定檔中的 default 選項設定為 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 驅動程式,請透過 Composer 安裝 Symfony 的 Postmark Mailer 傳輸

composer require symfony/postmark-mailer symfony/http-client

接下來,將您應用程式的 config/mail.php 設定檔中的 default 選項設定為 postmark。設定應用程式的預設郵件程式後,請確保您的 config/services.php 設定檔包含以下選項

'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],

如果您想要指定給定郵件程式應使用的 Postmark 訊息串流,您可以將 message_stream_id 設定選項新增至郵件程式的設定陣列。此設定陣列可以在您應用程式的 config/mail.php 設定檔中找到

'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],

這樣您還可以設定具有不同訊息串流的多個 Postmark 郵件程式。

Resend 驅動程式

若要使用 Resend 驅動程式,請透過 Composer 安裝 Resend 的 PHP SDK

composer require resend/resend-php

接下來,將您應用程式的 config/mail.php 設定檔中的 default 選項設定為 resend。設定應用程式的預設郵件程式後,請確保您的 config/services.php 設定檔包含以下選項

'resend' => [
'key' => env('RESEND_KEY'),
],

SES 驅動程式

若要使用 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'),
],

若要透過工作階段權杖使用 AWS 臨時憑證,您可以在應用程式的 SES 設定中新增 token 金鑰

'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'],
],
],
],

MailerSend 驅動程式

MailerSend,一種交易電子郵件和簡訊服務,為 Laravel 維護他們自己的基於 API 的郵件驅動程式。包含驅動程式的套件可以使用 Composer 套件管理員安裝

composer require mailersend/laravel-driver

安裝套件後,將 MAILERSEND_API_KEY 環境變數新增至應用程式的 .env 檔案。此外,MAIL_MAILER 環境變數應定義為 mailersend

MAIL_MAILER=mailersend
MAIL_FROM_ADDRESS[email protected]
MAIL_FROM_NAME="App Name"
 
MAILERSEND_API_KEY=your-api-key

最後,將 MailerSend 新增至應用程式的 config/mail.php 設定檔中的 mailers 陣列

'mailersend' => [
'transport' => 'mailersend',
],

若要深入瞭解 MailerSend,包括如何使用託管範本,請參閱 MailerSend 驅動程式文件

容錯移轉設定

有時,您設定為傳送應用程式郵件的外部服務可能會關閉。在這些情況下,定義一個或多個備份郵件傳遞設定可能會很有用,以便在您的主要傳遞驅動程式關閉時使用。

若要達成此目的,您應該在應用程式的 mail 設定檔中定義一個使用 failover 傳輸的郵件程式。您應用程式 failover 郵件程式的設定陣列應包含一個 mailers 陣列,其中參照應選擇設定的郵件程式的傳遞順序

'mailers' => [
'failover' => [
'transport' => 'failover',
'mailers' => [
'postmark',
'mailgun',
'sendmail',
],
],
 
// ...
],

定義容錯移轉郵件程式後,您應該將此郵件程式設定為應用程式使用的預設郵件程式,方法是在應用程式的 mail 設定檔中指定其名稱作為 default 設定金鑰的值

'default' => env('MAIL_MAILER', 'failover'),

循環配置

roundrobin 傳輸允許您跨多個郵件程式分配郵件工作負載。若要開始使用,請在應用程式的 mail 設定檔中定義一個使用 roundrobin 傳輸的郵件程式。您應用程式的 roundrobin 郵件程式的設定陣列應包含一個 mailers 陣列,其中參照應使用哪個設定的郵件程式進行傳遞

'mailers' => [
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
 
// ...
],

定義循環郵件程式後,您應該將此郵件程式設定為應用程式使用的預設郵件程式,方法是在應用程式的 mail 設定檔中指定其名稱作為 default 設定金鑰的值

'default' => env('MAIL_MAILER', 'roundrobin'),

循環傳輸會從設定的郵件程式清單中選取隨機郵件程式,然後針對每個後續電子郵件切換到下一個可用的郵件程式。與有助於實現高可用性failover 傳輸相比,roundrobin 傳輸提供負載平衡

產生可郵寄物件

在建構 Laravel 應用程式時,您的應用程式傳送的每一種電子郵件都會表示為「可郵寄」類別。這些類別儲存在 app/Mail 目錄中。如果您在應用程式中沒有看到此目錄,請不要擔心,因為當您使用 make:mail Artisan 命令建立第一個可郵寄類別時,就會為您產生它

php artisan make:mail OrderShipped

撰寫可郵寄物件

一旦您產生了一個可郵寄的類別,請將其打開,以便我們可以探索其內容。可郵寄類別的設定是在幾個方法中完成的,包括 envelopecontentattachments 方法。

envelope 方法會回傳一個 Illuminate\Mail\Mailables\Envelope 物件,該物件定義了郵件的主旨,有時也定義了收件者。content 方法會回傳一個 Illuminate\Mail\Mailables\Content 物件,該物件定義了將用於產生郵件內容的 Blade 範本

設定寄件者

使用 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('[email protected]', 'Jeffrey Way'),
subject: 'Order Shipped',
);
}

如果您願意,也可以指定一個 replyTo 地址

return new Envelope(
from: new Address('[email protected]', 'Jeffrey Way'),
replyTo: [
new Address('[email protected]', 'Taylor Otwell'),
],
subject: 'Order Shipped',
);

使用全域的 from 地址

但是,如果您的應用程式對其所有電子郵件都使用相同的「from」地址,那麼將其添加到您產生的每個可郵寄類別中可能會很麻煩。相反,您可以在 config/mail.php 設定檔中指定一個全域的「from」地址。如果在可郵寄類別中沒有指定其他「from」地址,則將使用此地址

'from' => [
'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],

此外,您可以在 config/mail.php 設定檔中定義一個全域的「reply_to」地址

'reply_to' => ['address' => '[email protected]', 'name' => 'App Name'],

設定視圖

在可郵寄類別的 content 方法中,您可以定義 view,即在呈現電子郵件內容時應使用的範本。由於每封電子郵件通常都使用 Blade 範本 來呈現其內容,因此在建構電子郵件的 HTML 時,您可以完全使用 Blade 範本引擎的強大功能和便利性

/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
lightbulb

您可能希望建立一個 resources/views/emails 目錄來存放所有電子郵件範本;但是,您可以將它們放置在 resources/views 目錄中的任何位置。

純文字電子郵件

如果您想定義電子郵件的純文字版本,您可以在建立郵件的 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'
);

視圖資料

透過公開屬性

通常,您會希望將一些資料傳遞給您的視圖,以便您在呈現電子郵件的 HTML 時可以使用。有兩種方法可以將資料提供給您的視圖。首先,在您的可郵寄類別上定義的任何公開屬性都將自動提供給視圖。因此,例如,您可以將資料傳遞到可郵寄類別的建構子中,並將該資料設定為類別上定義的公開屬性

<?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 參數

如果您想在將資料傳送到範本之前自訂電子郵件資料的格式,您可以透過 Content 定義的 with 參數手動將資料傳遞到視圖。通常,您仍然會透過可郵寄類別的建構子傳遞資料;但是,您應該將此資料設定為 protectedprivate 屬性,以便資料不會自動提供給範本

<?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 方法回傳的陣列中。首先,您可以透過提供 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'),
];
}

在將檔案附加到郵件時,您也可以使用 aswithMime 方法來指定附件的顯示名稱和/或 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'),
];
}

從磁碟附加檔案

如果您將檔案儲存在您的其中一個 檔案系統磁碟 上,您可以使用 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'),
];
}

原始資料附件

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'),
];
}

內嵌附件

將內嵌圖片嵌入到您的電子郵件中通常很麻煩;但是,Laravel 提供了一種方便的方法來將圖片附加到您的電子郵件中。要嵌入內嵌圖片,請在您的電子郵件範本中使用 $message 變數上的 embed 方法。Laravel 會自動使 $message 變數可用於您所有的電子郵件範本,因此您不必擔心手動傳入它

<body>
Here is an image:
 
<img src="{{ $message->embed($pathToImage) }}">
</body>
exclamation

$message 變數在純文字郵件範本中不可用,因為純文字郵件不使用內嵌附件。

嵌入原始資料附件

如果您已經有一個想要嵌入到電子郵件範本中的原始圖片資料字串,您可以在 $message 變數上呼叫 embedData 方法。當呼叫 embedData 方法時,您需要提供一個應指派給嵌入圖片的檔案名稱

<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
 
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 也提供其他方法,您可以利用這些方法來自訂您的附件。例如,您可以使用 aswithMime 方法來自訂檔案的名稱和 MIME 類型

return Attachment::fromPath('/path/to/file')
->as('Photo Name')
->withMime('image/jpeg');

標頭

有時您可能需要將額外的標頭附加到外寄郵件。例如,您可能需要設定自訂的 Message-Id 或其他任意文字標頭。

若要完成此操作,請在您的可郵寄類別上定義 headers 方法。headers 方法應回傳一個 Illuminate\Mail\Mailables\Headers 實例。此類別接受 messageIdreferencestext 參數。當然,您只能提供您的特定郵件所需的參數

use Illuminate\Mail\Mailables\Headers;
 
/**
* Get the message headers.
*/
public function headers(): Headers
{
return new Headers(
messageId: '[email protected]',
references: ['[email protected]'],
text: [
'X-Custom-Header' => 'Custom Value',
],
);
}

標籤和中繼資料

某些第三方電子郵件提供商(例如 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「標籤」附加到郵件。

自訂 Symfony 訊息

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 可郵寄物件

Markdown 可郵寄郵件允許您利用 郵件通知 中預先建立的範本和元件。由於郵件是用 Markdown 編寫的,因此 Laravel 可以為郵件呈現美觀、回應式的 HTML 範本,同時也自動產生純文字對應項。

產生 Markdown 可郵寄物件

若要產生帶有對應 Markdown 範本的可郵寄郵件,您可以使用 make:mail Artisan 命令的 --markdown 選項

php artisan make:mail OrderShipped --markdown=mail.orders.shipped

然後,在設定可郵寄郵件的 content 方法內的 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 訊息

Markdown 可郵寄郵件結合使用 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>
lightbulb

撰寫 Markdown 電子郵件時,請勿使用過多的縮排。根據 Markdown 標準,Markdown 剖析器會將縮排內容呈現為程式碼區塊。

按鈕元件

按鈕元件會呈現一個置中的按鈕連結。該元件接受兩個引數,一個 url 和一個可選的 color。支援的顏色為 primarysuccesserror。您可以根據需要向郵件新增多個按鈕元件

<x-mail::button :url="$url" color="success">
View Order
</x-mail::button>

面板元件

面板元件會在面板中呈現給定的文字區塊,該面板的背景顏色與郵件的其餘部分略有不同。這可讓您將注意力集中在給定的文字區塊上

<x-mail::panel>
This is the panel content.
</x-mail::panel>

表格元件

表格元件允許您將 Markdown 表格轉換為 HTML 表格。該元件接受 Markdown 表格作為其內容。使用預設的 Markdown 表格對齊語法支援表格欄對齊

<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 資產標籤

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 元件建立一個全新的主題,您可以將 CSS 檔案放置在 html/themes 目錄中。在命名並儲存您的 CSS 檔案後,請更新您應用程式的 config/mail.php 設定檔中的 theme 選項,以符合您新主題的名稱。

若要自訂個別郵件的樣式,您可以將郵件類別的 $theme 屬性設定為寄送該郵件時應使用的主題名稱。

傳送郵件

要傳送訊息,請在 Mail 外觀上使用 to 方法。to 方法接受電子郵件地址、使用者實例或使用者集合。如果您傳遞一個物件或物件集合,郵件程式會在決定電子郵件的收件人時自動使用其 emailname 屬性,因此請確保這些屬性在您的物件上可用。指定收件人後,您可以將郵件類別的實例傳遞給 send 方法。

<?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
{
/**
* 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));

迴圈遍歷收件人

有時,您可能需要透過迭代收件人/電子郵件地址陣列來將郵件傳送給收件人列表。但是,由於 to 方法會將電子郵件地址附加到郵件的收件人清單中,因此每次迴圈迭代都會向每個先前的收件人傳送另一封電子郵件。因此,您應該為每個收件人重新建立郵件實例。

foreach (['[email protected]', '[email protected]'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}

透過特定郵件程式傳送郵件

預設情況下,Laravel 將使用您應用程式的 mail 設定檔中設定為 default 郵件程式的郵件程式來傳送電子郵件。但是,您可以使用 mailer 方法來使用特定郵件程式設定傳送訊息。

Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));

將郵件加入佇列

將郵件訊息加入佇列

由於傳送電子郵件訊息可能會對應用程式的響應時間產生負面影響,許多開發人員選擇將電子郵件訊息加入佇列以進行背景傳送。Laravel 使用其內建的統一佇列 API 使這變得容易。若要將郵件訊息加入佇列,請在指定訊息的收件人後,在 Mail 外觀上使用 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 命令產生所有郵件類別都使用 Illuminate\Bus\Queueable 特徵,您可以在任何郵件類別實例上呼叫 onQueueonConnection 方法,讓您可以為訊息指定連線和佇列名稱。

$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
{
// ...
}

已加入佇列的郵件和資料庫交易

當已加入佇列的郵件在資料庫交易內被派送時,它們可能會在資料庫交易提交之前由佇列處理。當發生這種情況時,您在資料庫交易期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,在交易中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果您的郵件依賴這些模型,則在處理傳送已加入佇列的郵件的作業時可能會發生非預期的錯誤。

如果您的佇列連線的 after_commit 設定選項設定為 false,您仍然可以透過在傳送郵件訊息時呼叫 afterCommit 方法,指示特定的已加入佇列的郵件應在所有開啟的資料庫交易都已提交之後派送。

Mail::to($request->user())->send(
(new OrderShipped($order))->afterCommit()
);

或者,您可以從郵件的建構子呼叫 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();
}
}
lightbulb

若要深入了解如何解決這些問題,請檢閱關於已加入佇列的作業和資料庫交易的文件。

彩現可郵寄物件

有時,您可能希望在不傳送郵件的情況下擷取郵件的 HTML 內容。若要達成此目的,您可以呼叫郵件的 render 方法。此方法會以字串形式傳回郵件的已評估 HTML 內容。

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);
});

本地化可郵寄物件

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.
*/
public function preferredLocale(): string
{
return $this->locale;
}
}

一旦您實作了介面,Laravel 會在傳送郵件和通知給模型時自動使用偏好的地區設定。因此,在使用此介面時,無需呼叫 locale 方法。

Mail::to($request->user())->send(new OrderShipped($order));

測試

測試可郵寄物件內容

Laravel 提供了多種方法來檢查您的郵件結構。此外,Laravel 還提供了幾種方便的方法來測試您的郵件是否包含您預期的內容。這些方法是:assertSeeInHtmlassertDontSeeInHtmlassertSeeInOrderInHtmlassertSeeInTextassertDontSeeInTextassertSeeInOrderInTextassertHasAttachmentassertHasAttachedDataassertHasAttachmentFromStorageassertHasAttachmentFromStorageDisk

正如您可能預期的那樣,「HTML」斷言會斷言您郵件的 HTML 版本包含給定的字串,而「文字」斷言會斷言您郵件的純文字版本包含給定的字串。

use App\Mail\InvoicePaid;
use App\Models\User;
 
test('mailable content', function () {
$user = User::factory()->create();
 
$mailable = new InvoicePaid($user);
 
$mailable->assertFrom('[email protected]');
$mailable->assertTo('[email protected]');
$mailable->assertHasCc('[email protected]');
$mailable->assertHasBcc('[email protected]');
$mailable->assertHasReplyTo('[email protected]');
$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']);
});
use App\Mail\InvoicePaid;
use App\Models\User;
 
public function test_mailable_content(): void
{
$user = User::factory()->create();
 
$mailable = new InvoicePaid($user);
 
$mailable->assertFrom('[email protected]');
$mailable->assertTo('[email protected]');
$mailable->assertHasCc('[email protected]');
$mailable->assertHasBcc('[email protected]');
$mailable->assertHasReplyTo('[email protected]');
$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']);
}

測試可郵寄物件傳送

我們建議將郵件的內容與斷言已將特定郵件「傳送」給特定使用者的測試分開測試。通常,郵件的內容與您正在測試的程式碼無關,只需斷言已指示 Laravel 傳送給定的郵件即可。

您可以使用 Mail 外觀的 fake 方法來防止傳送郵件。在呼叫 Mail 外觀的 fake 方法後,您可以斷言已指示將郵件傳送給使用者,甚至可以檢查郵件收到的資料。

<?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, '[email protected]');
 
// Assert a mailable was sent to multiple email addresses...
Mail::assertSent(OrderShipped::class, ['[email protected]', '...']);
 
// Assert a mailable was not sent...
Mail::assertNotSent(AnotherMailable::class);
 
// 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, '[email protected]');
 
// Assert a mailable was sent to multiple email addresses...
Mail::assertSent(OrderShipped::class, ['[email protected]', '...']);
 
// Assert a mailable was not sent...
Mail::assertNotSent(AnotherMailable::class);
 
// Assert 3 total mailables were sent...
Mail::assertSentCount(3);
}
}

如果您正在將郵件加入佇列以便在背景中傳送,您應該使用 assertQueued 方法而不是 assertSent

Mail::assertQueued(OrderShipped::class);
Mail::assertNotQueued(OrderShipped::class);
Mail::assertNothingQueued();
Mail::assertQueuedCount(3);

您可以將閉包傳遞給 assertSentassertNotSentassertQueuedassertNotQueued 方法,以便斷言傳送了通過給定「真值測試」的郵件。如果至少傳送了一封通過給定真值測試的郵件,則斷言將成功。

Mail::assertSent(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});

當呼叫 Mail 外觀的斷言方法時,提供的閉包接受的郵件實例會公開檢查郵件的實用方法。

Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...') &&
$mail->hasReplyTo('...') &&
$mail->hasFrom('...') &&
$mail->hasSubject('...');
});

郵件實例還包含幾個有用的方法,用於檢查郵件上的附件。

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')
);
});

您可能已經注意到有兩種方法可以斷言未傳送郵件:assertNotSentassertNotQueued。有時,您可能希望斷言未傳送加入佇列任何郵件。若要達成此目的,您可以使用 assertNothingOutgoingassertNotOutgoing 方法。

Mail::assertNothingOutgoing();
 
Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});

郵件和本地開發

當開發傳送電子郵件的應用程式時,您可能不希望實際將電子郵件傳送到實際的電子郵件地址。Laravel 提供了幾種方法來在本地開發期間「停用」電子郵件的實際傳送。

記錄驅動程式

log 郵件驅動程式不會傳送您的電子郵件,而是會將所有電子郵件訊息寫入您的記錄檔以供檢查。通常,此驅動程式只會在本地開發期間使用。有關根據環境設定應用程式的詳細資訊,請查看設定文件

HELO / Mailtrap / Mailpit

或者,您可以使用像 HELOMailtrap 這樣的服務和 smtp 驅動程式將您的電子郵件訊息傳送到「虛擬」信箱,您可以在真正的電子郵件用戶端中檢視這些訊息。此方法的優點是可以讓您在 Mailtrap 的訊息檢視器中實際檢查最終的電子郵件。

如果您正在使用 Laravel Sail,您可以使用 Mailpit 預覽您的訊息。當 Sail 執行時,您可以存取 Mailpit 介面:https://127.0.0.1:8025

使用全域 to 位址

最後,您可以透過呼叫 Mail 外觀提供的 alwaysTo 方法來指定全域「to」位址。通常,此方法應從您的應用程式服務提供者的 boot 方法中呼叫。

use Illuminate\Support\Facades\Mail;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
if ($this->app->environment('local')) {
Mail::alwaysTo('[email protected]');
}
}

事件

Laravel 在傳送郵件訊息時會派送兩個事件。MessageSending 事件在傳送訊息之前派送,而 MessageSent 事件在傳送訊息之後派送。請記住,這些事件是在傳送郵件時派送,而不是在加入佇列時派送。您可以在應用程式中為這些事件建立事件偵聽器

use Illuminate\Mail\Events\MessageSending;
// use Illuminate\Mail\Events\MessageSent;
 
class LogMessage
{
/**
* Handle the given event.
*/
public function handle(MessageSending $event): void
{
// ...
}
}

自訂傳輸

Laravel 包含多種郵件傳輸方式;但是,您可能希望編寫自己的傳輸方式,透過 Laravel 不支援的其他服務來傳遞電子郵件。若要開始,請定義一個繼承 Symfony\Component\Mailer\Transport\AbstractTransport 類別的類別。然後,在您的傳輸方式上實作 doSend__toString() 方法。

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 外觀提供的 extend 方法來註冊它。通常,這應在您的應用程式 AppServiceProvider 服務提供者的 boot 方法中完成。$config 引數將會傳遞給提供給 extend 方法的閉包。此引數將包含在應用程式的 config/mail.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 設定檔中建立一個郵件程式定義,該定義使用新的傳輸方式。

'mailchimp' => [
'transport' => 'mailchimp',
// ...
],

其他 Symfony 傳輸

Laravel 支援一些現有的 Symfony 維護的郵件傳輸方式,例如 Mailgun 和 Postmark。但是,您可能希望擴充 Laravel 以支援其他 Symfony 維護的傳輸方式。您可以透過 Composer 安裝必要的 Symfony 郵件程式,並在 Laravel 中註冊傳輸方式來完成此操作。例如,您可以安裝和註冊「Brevo」(先前為「Sendinblue」)Symfony 郵件程式。

composer require symfony/brevo-mailer symfony/http-client

安裝 Brevo 郵件程式套件後,您可以將 Brevo API 憑證的條目新增至您應用程式的 services 設定檔。

'brevo' => [
'key' => 'your-api-key',
],

接下來,您可以使用 Mail 外觀的 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',
// ...
],