跳到主要內容

郵件

📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本

郵件

簡介

發送電子郵件不必複雜。Laravel 提供了一個乾淨、簡單的電子郵件 API,由流行的 Symfony Mailer 元件提供支援。Laravel 和 Symfony Mailer 提供了透過 SMTP、Cloudflare、Mailgun、Postmark、Resend、Amazon SES 和 sendmail 發送電子郵件的驅動程式,讓您可以快速開始透過您選擇的本地或雲端服務發送郵件。

配置

Laravel 的電子郵件服務可以透過應用程式的 config/mail.php 配置檔案進行配置。在此檔案中配置的每個郵件程式都可以有其自己獨特的配置,甚至可以有自己獨特的「傳輸」,允許您的應用程式使用不同的電子郵件服務來傳送特定的電子郵件。例如,您的應用程式可能會使用 Postmark 來傳送交易郵件,同時使用 Amazon SES 來傳送大量郵件。

在您的 mail 配置檔案中,您會找到一個 mailers 配置陣列。此陣列包含 Laravel 支援的每個主要郵件驅動程式/傳輸的範例配置條目,而 default 配置值決定了當您的應用程式需要傳送電子郵件時預設使用哪個郵件程式。

驅動程式/傳輸先決條件

基於 API 的驅動程式(例如 Mailgun、Postmark 和 Resend)通常比透過 SMTP 伺服器發送郵件更簡單、更快。如果可能,我們建議您使用這些驅動程式之一。

Cloudflare 驅動程式

要使用 Cloudflare 驅動程式,請透過 Composer 安裝 Symfony 的 HTTP 客戶端:

composer require symfony/http-client

接下來,您需要在應用程式的 config/mail.php 配置檔案中進行兩項更改。首先,將預設郵件程式設定為 cloudflare

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

其次,將以下配置陣列添加到您的 mailers 陣列中:

'cloudflare' => [
    'transport' => 'cloudflare',
],

配置好應用程式的預設郵件程式後,將以下選項添加到 config/services.php 配置檔案中:

'cloudflare' => [
    'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
    'key' => env('CLOUDFLARE_KEY'),
],

Mailgun 驅動程式

要使用 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 驅動程式,請透過 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 驅動程式,請透過 Composer 安裝 Resend 的 PHP SDK:

composer require resend/resend-php

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

'resend' => [
    'key' => env('RESEND_API_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 頁頭:

/**
 * 獲取訊息頁頭。
 */
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'],
        ],
    ],
],

故障轉移配置

有時,您配置用於傳送應用程式郵件的外部服務可能會停機。在這些情況下,定義一個或多個備份郵件傳遞配置會很有用,以防主要傳遞驅動程式停機。

為此,您應該在應用程式的 mail 配置檔案中定義一個使用 failover 傳輸的郵件程式。應用程式的 failover 郵件程式的配置陣列應包含一個 mailers 陣列,該陣列引用應按順序選擇用於傳遞的配置郵件程式:

'mailers' => [
    'failover' => [
        'transport' => 'failover',
        'mailers' => [
            'postmark',
            'mailgun',
            'sendmail',
        ],
        'retry_after' => 60,
    ],

    // ...
],

配置好使用 failover 傳輸的郵件程式後,您需要在應用程式的 .env 檔案中將故障轉移郵件程式設定為預設郵件程式,以使用故障轉移功能:

MAIL_MAILER=failover

循環配置

roundrobin 傳輸允許您將郵件工作負載分配到多個郵件程式。首先,在應用程式的 mail 配置檔案中定義一個使用 roundrobin 傳輸的郵件程式。應用程式的 roundrobin 郵件程式的配置陣列應包含一個 mailers 陣列,該陣列引用哪些配置的郵件程式應用於傳遞:

'mailers' => [
    'roundrobin' => [
        'transport' => 'roundrobin',
        'mailers' => [
            'ses',
            'postmark',
        ],
        'retry_after' => 60,
    ],

    // ...
],

定義好循環郵件程式後,您應該將此郵件程式設定為應用程式使用的預設郵件程式,方法是在應用程式的 mail 配置檔案中將其名稱指定為 default 配置鍵的值:

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

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

生成可郵寄物件

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

php artisan make:mail OrderShipped

編寫可郵寄物件

生成可郵寄類別後,打開它以便我們探索其內容。可郵寄類別配置在多個方法中完成,包括 envelopecontentattachments 方法。

envelope 方法返回一個 Illuminate\Mail\Mailables\Envelope 物件,用於定義主題和(有時)訊息的收件者。content 方法返回一個 Illuminate\Mail\Mailables\Content 物件,用於定義將用於生成訊息內容的 Blade 範本

配置寄件者

使用信封

首先,讓我們探索配置電子郵件的寄件者。或者,換句話說,電子郵件將從誰那裡「發送」。有兩種方式可以配置寄件者。首先,您可以在訊息的信封上指定「from」地址:

use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\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 範本引擎的功能和便利性:

/**
 * 獲取訊息內容定義。
 */
public function content(): Content
{
    return new Content(
        view: 'mail.orders.shipped',
    );
}

[!NOTE] 您可能希望建立一個 resources/views/mail 目錄來容納所有電子郵件範本;但是,您可以自由地將它們放在 resources/views 目錄中的任何位置。

純文字電子郵件

如果您希望定義電子郵件的純文字版本,可以在建立訊息的 Content 定義時指定純文字範本。像 view 參數一樣,text 參數應該是一個範本名稱,將用於渲染電子郵件的內容。您可以自由定義訊息的 HTML 和純文字版本:

/**
 * 獲取訊息內容定義。
 */
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;

    /**
     * 建立一個新的訊息實例。
     */
    public function __construct(
        public Order $order,
    ) {}

    /**
     * 獲取訊息內容定義。
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.orders.shipped',
        );
    }
}

將資料設定為公共屬性後,它將自動在視圖中可用,因此您可以像在 Blade 範本中存取任何其他資料一樣存取它:

Price: {{ $order->price }}

透過 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;

    /**
     * 建立一個新的訊息實例。
     */
    public function __construct(
        protected Order $order,
    ) {}

    /**
     * 獲取訊息內容定義。
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.orders.shipped',
            with: [
                'orderName' => $this->order->name,
                'orderPrice' => $this->order->price,
            ],
        );
    }
}

透過 with 參數傳遞資料後,它將自動在視圖中可用,因此您可以像在 Blade 範本中存取任何其他資料一樣存取它:

Price: {{ $orderPrice }}

附件

要向電子郵件添加附件,您將向訊息的 attachments 方法返回的陣列中添加附件。首先,您可以透過向 Attachment 類別提供的 fromPath 方法提供檔案路徑來添加附件:

use Illuminate\Mail\Mailables\Attachment;

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromPath('/path/to/file'),
    ];
}

將檔案附加到訊息時,您還可以使用 aswithMime 方法指定附件的顯示名稱和/或 MIME 類型:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromPath('/path/to/file')
            ->as('name.pdf')
            ->withMime('application/pdf'),
    ];
}

從磁碟附加檔案

如果您已在其中一個文件系統磁碟上儲存了檔案,可以使用 fromStorage 附件方法將其附加到電子郵件:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromStorage('/path/to/file'),
    ];
}

當然,您還可以指定附件的名稱和 MIME 類型:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromStorage('/path/to/file')
            ->as('name.pdf')
            ->withMime('application/pdf'),
    ];
}

如果您需要指定與預設磁碟不同的儲存磁碟,可以使用 fromStorageDisk 方法:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromStorageDisk('s3', '/path/to/file')
            ->as('name.pdf')
            ->withMime('application/pdf'),
    ];
}

原始資料附件

fromData 附件方法可用於將原始位元組字串作為附件附加。例如,如果您已在記憶體中生成 PDF 並希望將其附加到電子郵件而不將其寫入磁碟,則可以使用此方法。fromData 方法接受一個解析原始資料位元組的閉包以及應分配給附件的名稱:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [
        Attachment::fromData(fn () => $this->pdf, 'Report.pdf')
            ->withMime('application/pdf'),
    ];
}

內聯附件

將內聯圖像嵌入到電子郵件中通常很繁瑣;但是,Laravel 提供了一種將圖像附加到電子郵件的便利方式。要嵌入內聯圖像,請使用電子郵件範本中的 $message 變數上的 embed 方法。Laravel 自動使 $message 變數在您的所有電子郵件範本中可用,因此您無需擔心手動傳遞它:


    Here is an image:

    

[!WARNING] $message 變數在純文字訊息範本中不可用,因為純文字訊息不使用內聯附件。

嵌入原始資料附件

如果您已經有一個希望嵌入到電子郵件範本中的原始圖像資料字串,可以在 $message 變數上呼叫 embedData 方法。呼叫 embedData 方法時,您需要提供一個應分配給嵌入圖像的檔名:


    Here is an image from raw data:

    

可附加物件

雖然透過簡單的字串路徑將檔案附加到訊息通常足夠了,但在許多情況下,應用程式中的可附加實體由類別表示。例如,如果您的應用程式正在將照片附加到訊息,則您的應用程式可能還有一個表示該照片的 Photo 模型。在這種情況下,將 Photo 模型簡單地傳遞給 attach 方法不是更方便嗎?可附加物件允許您執行此操作。

首先,在將附加到訊息的物件上實現 Illuminate\Contracts\Mail\Attachable 介面。此介面要求您的類別定義一個返回 Illuminate\Mail\Attachment 實例的 toMailAttachment 方法:

<?php

namespace App\Models;

use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Attachment;

class Photo extends Model implements Attachable
{
    /**
     * 獲取模型的可附加表示。
     */
    public function toMailAttachment(): Attachment
    {
        return Attachment::fromPath('/path/to/file');
    }
}

定義可附加物件後,您可以在建立電子郵件訊息時從 attachments 方法返回該物件的實例:

/**
 * 獲取訊息的附件。
 *
 * @return array
 */
public function attachments(): array
{
    return [$this->photo];
}

當然,附件資料可以儲存在遠端檔案儲存服務(如 Amazon S3)上。因此,Laravel 還允許您從儲存在應用程式其中一個文件系統磁碟上的資料生成附件實例:

// 從預設磁碟上的檔案建立附件...
return Attachment::fromStorage($this->path);

// 從特定磁碟上的檔案建立附件...
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;

/**
 * 獲取訊息頁頭。
 */
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;

/**
 * 獲取訊息信封。
 *
 * @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;

/**
 * 獲取訊息信封。
 */
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;

/**
 * 獲取訊息內容定義。
 */
public function content(): Content
{
    return new Content(
        markdown: 'mail.orders.shipped',
        with: [
            'url' => $this->orderUrl,
        ],
    );
}

編寫 Markdown 訊息

Markdown 可郵寄物件使用 Blade 元件和 Markdown 語法的組合,允許您輕鬆建立郵件訊息,同時利用 Laravel 的預建電子郵件 UI 元件:


# Order Shipped

Your order has been shipped!


View Order


Thanks,
{{ config('app.name') }}

[!NOTE] 編寫 Markdown 電子郵件時不要使用過多縮排。根據 Markdown 標準,Markdown 解析器會將縮排的內容渲染為代碼塊。

按鈕元件

按鈕元件渲染一個居中的按鈕連結。該元件接受兩個參數:url 和可選的 color。支援的顏色有 primarysuccesserror。您可以根據需要向訊息添加任意數量的按鈕元件:


View Order

面板元件

面板元件在背景顏色與訊息其餘部分略有不同的面板中渲染給定的文字塊。這使您可以引起對給定文字塊的注意:


This is the panel content.

表格元件

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


| Laravel       | Table         | Example       |
| ------------- | :-----------: | ------------: |
| Col 2 is      | Centered      | $10           |
| Col 3 is      | Right-Aligned | $20           |

自訂元件

您可以匯出所有 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\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;

class OrderShipmentController extends Controller
{
    /**
     * 發貨給定訂單。
     */
    public function store(Request $request): RedirectResponse
    {
        $order = Order::findOrFail($request->order_id);

        // 發貨訂單...

        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()->plus(minutes: 10), new OrderShipped($order));

推送到特定佇列

由於使用 make:mail 命令生成的所有可郵寄類別都使用 Illuminate\Bus\Queueable trait,因此您可以在任何可郵寄物件實例上呼叫 onQueueonConnection 方法,允許您指定訊息的連接和佇列名稱:

$message = (new OrderShipped($order))
    ->onConnection('sqs')
    ->onQueue('emails');

Mail::to($request->user())
    ->cc($moreUsers)
    ->bcc($evenMoreUsers)
    ->queue($message);

或者,您可以使用可郵寄物件類別上的 ConnectionQueue 屬性來指定連接和佇列:

use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;

#[Connection('sqs')]
#[Queue('emails')]
class OrderShipped extends Mailable
{
    // ...
}

預設佇列

如果您有希望始終被放入佇列的可郵寄類別,可以在類別上實現 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;

    /**
     * 建立一個新的訊息實例。
     */
    public function __construct()
    {
        $this->afterCommit();
    }
}

[!NOTE] 要了解更多關於解決這些問題的資訊,請查閱有關佇列作業和資料庫交易的文檔。

佇列電子郵件失敗

當佇列電子郵件失敗時,如果已定義,將呼叫佇列可郵寄物件類別上的 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;

    /**
     * 處理佇列電子郵件失敗。
     */
    public function failed(Throwable $exception): void
    {
        // ...
    }
}

渲染可郵寄物件

有時您可能希望捕獲可郵寄物件的 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
{
    /**
     * 獲取使用者的首選語言環境。
     */
    public function preferredLocale(): string
    {
        return $this->locale;
    }
}

實現介面後,Laravel 將在向模型發送可郵寄物件和通知時自動使用首選語言環境。因此,使用此介面時無需呼叫 locale 方法:

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

測試

測試可郵寄物件內容

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

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->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('[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->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」斷言斷言可郵寄物件的 HTML 版本包含給定的字串,而「text」斷言斷言可郵寄物件的純文字版本包含給定的字串。

測試可郵寄物件發送

我們建議將可郵寄物件的內容測試與斷言給定可郵寄物件已「發送」給特定使用者的測試分開進行。通常,可郵寄物件的內容與您正在測試的代碼無關,只需斷言 Laravel 被指示發送給定的可郵寄物件就足夠了。

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

<?php

use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;

test('orders can be shipped', function () {
    Mail::fake();

    // 執行訂單發貨...

    // 斷言沒有發送可郵寄物件...
    Mail::assertNothingSent();

    // 斷言已發送可郵寄物件...
    Mail::assertSent(OrderShipped::class);

    // 斷言可郵寄物件已發送兩次...
    Mail::assertSent(OrderShipped::class, 2);

    // 斷言可郵寄物件已發送到電子郵件地址...
    Mail::assertSent(OrderShipped::class, '[email protected]');

    // 斷言可郵寄物件已發送到多個電子郵件地址...
    Mail::assertSent(OrderShipped::class, ['[email protected]', '...']);

    // 斷言可郵寄物件未發送...
    Mail::assertNotSent(AnotherMailable::class);

    // 斷言可郵寄物件已發送兩次...
    Mail::assertSentTimes(OrderShipped::class, 2);

    // 斷言共發送了 3 個可郵寄物件...
    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();

        // 執行訂單發貨...

        // 斷言沒有發送可郵寄物件...
        Mail::assertNothingSent();

        // 斷言已發送可郵寄物件...
        Mail::assertSent(OrderShipped::class);

        // 斷言可郵寄物件已發送兩次...
        Mail::assertSent(OrderShipped::class, 2);

        // 斷言可郵寄物件已發送到電子郵件地址...
        Mail::assertSent(OrderShipped::class, '[email protected]');

        // 斷言可郵寄物件已發送到多個電子郵件地址...
        Mail::assertSent(OrderShipped::class, ['[email protected]', '...']);

        // 斷言可郵寄物件未發送...
        Mail::assertNotSent(AnotherMailable::class);

        // 斷言可郵寄物件已發送兩次...
        Mail::assertSentTimes(OrderShipped::class, 2);

        // 斷言共發送了 3 個可郵寄物件...
        Mail::assertSentCount(3);
    }
}

如果您正在將可郵寄物件放入佇列以便在背景中發送,則應使用 assertQueued 方法而不是 assertSent

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

您還可以使用 assertOutgoingCount 方法來斷言已發送或放入佇列的可郵寄物件總數:

Mail::assertOutgoingCount(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('...') &&
           $mail->hasMetadata('order_id', $mail->order->id);
           $mail->usesMailer('ses');
});

可郵寄物件實例還包含幾個用於檢查可郵寄物件附件的有用方法:

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 介面:http://localhost:8025

使用全域 to 地址

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

use Illuminate\Support\Facades\Mail;

/**
 * 啟動任何應用程式服務。
 */
public function boot(): void
{
    if ($this->app->environment('local')) {
        Mail::alwaysTo('[email protected]');
    }
}

使用 alwaysTo 方法時,郵件訊息上的任何額外「cc」或「bcc」地址將被移除。

事件

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

use Illuminate\Mail\Events\MessageSending;
// use Illuminate\Mail\Events\MessageSent;

class LogMessage
{
    /**
     * 處理事件。
     */
    public function handle(MessageSending $event): void
    {
        // ...
    }
}

自訂傳輸

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
{
    /**
     * 建立一個新的 Mailchimp 傳輸實例。
     */
    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(),
        ]]);
    }

    /**
     * 獲取傳輸的字串表示。
     */
    public function __toString(): string
    {
        return 'mailchimp';
    }
}

定義好自訂傳輸後,您可以透過 Mail 外觀提供的 extend 方法來註冊它。通常,這應該在應用程式的 AppServiceProviderboot 方法中完成。一個 $config 參數將被傳遞給提供給 extend 方法的閉包。此參數將包含在應用程式的 config/mail.php 配置檔案中為郵件程式定義的配置陣列:

use App\Mail\MailchimpTransport;
use Illuminate\Support\Facades\Mail;
use MailchimpTransactional\ApiClient;

/**
 * 啟動任何應用程式服務。
 */
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 傳輸

Laravel 包含對一些現有 Symfony 維護的郵件傳輸(如 Mailgun 和 Postmark)的支援。但是,您可能希望擴展 Laravel 以支援額外的 Symfony 維護的傳輸。您可以透過透過 Composer 要求必要的 Symfony 郵件程式並將傳輸註冊到 Laravel 來實現這一點。例如,您可以安裝並註冊「Brevo」(以前稱為「Sendinblue」)Symfony 郵件程式:

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

安裝好 Brevo 郵件程式套件後,您可以在應用程式的 services 配置檔案中添加 Brevo API 憑據的條目:

'brevo' => [
    'key' => env('BREVO_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;

/**
 * 啟動任何應用程式服務。
 */
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',
    // ...
],