Mail

Introduction

发送电子邮件不必很复杂。 Laravel 提供了一个干净、简单的电子邮件 API,由流行的Symfony 邮件程序 成分。 Laravel 和 Symfony Mailer 提供了通过 SMTP、Mailgun、Postmark、Amazon SES 和sendmail,让您可以快速开始通过您选择的本地或基于云的服务发送邮件。

Configuration

Laravel 的电子邮件服务可以通过你的应用程序配置config/mail.php 配置文件。此文件中配置的每个邮件程序都可能有自己独特的配置,甚至有自己独特的“传输”,允许您的应用程序使用不同的电子邮件服务来发送特定的电子邮件消息。例如,您的应用程序可能使用 Postmark 发送交易电子邮件,同时使用 Amazon SES 发送批量电子邮件。

在你的mail 配置文件,你会发现一个mailers 配置数组。这个数组包含 Laravel 支持的每个主要邮件驱动程序/传输的示例配置条目,而default 配置值确定当您的应用程序需要发送电子邮件时默认使用哪个邮件程序。

司机/运输先决条件

基于 API 的驱动程序(例如 Mailgun 和 Postmark)通常比通过 SMTP 服务器发送邮件更简单、更快。只要有可能,我们建议您使用其中一种驱动程序。

邮枪司机

要使用 Mailgun 驱动程序,请通过 Composer 安装 Symfony 的 Mailgun Mailer 传输:

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

接下来,设置default 应用程序中的选项config/mail.php 配置文件到mailgun.配置应用程序的默认邮件程序后,验证您的config/services.php 配置文件包含以下选项:

'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
],

如果您不使用美国邮枪地区,您可以在services 配置文件:

'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
    'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
],

邮戳司机

要使用 Postmark 驱动程序,请通过 Composer 安装 Symfony 的 Postmark Mailer 传输:

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

接下来,设置default 应用程序中的选项config/mail.php 配置文件到postmark.配置应用程序的默认邮件程序后,验证您的config/services.php 配置文件包含以下选项:

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

如果您想指定给定邮件程序应使用的邮戳消息流,您可以添加message_stream_id 邮件程序配置数组的配置选项。这个配置数组可以在你的应用程序中找到config/mail.php 配置文件:

'postmark' => [
    'transport' => 'postmark',
    'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
],

通过这种方式,您还可以设置具有不同消息流的多个 Postmark 邮寄程序。

SES 驱动程序

要使用 Amazon SES 驱动程序,您必须先安装适用于 PHP 的 Amazon AWS SDK。你可以通过 Composer 包管理器安装这个库:

composer require aws/aws-sdk-php

接下来,设置default 你的选项config/mail.php 配置文件到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临时凭证 通过会话令牌,您可以添加一个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'),
],

如果你想定义其他选项 Laravel 应该传递给 AWS SDK 的SendEmail 发送电子邮件时的方法,您可以定义一个options 你的数组ses 配置:

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

    // ...
],

定义故障转移邮件程序后,您应该通过将其名称指定为default 应用程序中的配置键mail 配置文件:

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

生成邮件

在构建 Laravel 应用程序时,应用程序发送的每种类型的电子邮件都表示为一个“可邮寄”类。这些类存储在app/Mail 目录。如果您在应用程序中没有看到这个目录,请不要担心,因为它会在您使用创建第一个可邮寄类时为您生成make:mail 工匠命令:

php artisan make:mail OrderShipped

写邮件

生成可邮寄类后,将其打开,以便我们探索其内容。 Mailable 类配置是通过多种方法完成的,包括envelope,content, 和attachments 方法。

envelope 方法返回一个Illuminate\Mail\Mailables\Envelope 定义主题的对象,有时还定义邮件的收件人。这content 方法返回一个Illuminate\Mail\Mailables\Content 定义的对象刀片模板 将用于生成消息内容。

配置发件人

使用信封

首先,让我们探讨配置电子邮件的发件人。或者,换句话说,电子邮件将“来自”谁。有两种配置发送器的方法。首先,您可以在邮件信封上指定“发件人”地址:

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 地址

但是,如果您的应用程序对其所有电子邮件使用相同的“发件人”地址,调用from 您生成的每个可邮寄类中的方法。相反,您可以在您的config/mail.php 配置文件。如果在可邮寄类中没有指定其他“发件人”地址,则将使用此地址:

'from' => ['address' => 'example@example.com', 'name' => 'App Name'],

此外,您可以在您的config/mail.php 配置文件:

'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],

配置视图

在可邮寄类别中'content 方法,你可以定义view,或者在呈现电子邮件内容时应使用哪个模板。由于每封电子邮件通常使用刀片模板 要呈现其内容,在构建电子邮件的 HTML 时,您可以充分利用 Blade 模板引擎的强大功能和便利性:

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

Note
您可能希望创建一个resources/views/emails 存放所有电子邮件模板的目录;但是,您可以自由地将它们放置在您想要的任何位置resources/views 目录。

纯文本电子邮件

如果您想定义电子邮件的纯文本版本,您可以在创建消息时指定纯文本模板Content 定义。像view 参数,text 参数应该是将用于呈现电子邮件内容的模板名称。您可以自由定义消息的 HTML 和纯文本版本:

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

为了清楚起见,html 参数可以用作的别名view 范围:

return new Content(
    html: 'emails.orders.shipped',
    text: 'emails.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: 'emails.orders.shipped',
        );
    }
}

一旦数据被设置为公共属性,它将自动在您的视图中可用,因此您可以像访问 Blade 模板中的任何其他数据一样访问它:

<div>
    Price: {{ $order->price }}
</div>

通过with 范围:

如果您想在发送到模板之前自定义电子邮件数据的格式,您可以通过Content 定义的with 范围。通常,您仍将通过可邮寄类的构造函数传递数据;但是,您应该将此数据设置为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: 'emails.orders.shipped',
            with: [
                'orderName' => $this->order->name,
                'orderPrice' => $this->order->price,
            ],
        );
    }
}

一旦数据被传递给with 方法,它将自动在您的视图中可用,因此您可以像访问 Blade 模板中的任何其他数据一样访问它:

<div>
    Price: {{ $orderPrice }}
</div>

Attachments

要向电子邮件添加附件,您需要将附件添加到邮件返回的数组中attachments 方法。首先,您可以通过提供文件路径来添加附件fromPath 提供的方法Attachment 班级:

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 方法:

/**
 * 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 提供了一种方便的方法来将图像附加到您的电子邮件中。要嵌入内联图像,请使用embed 上的方法$message 电子邮件模板中的变量。 Laravel 自动生成$message 所有电子邮件模板都可用的变量,因此您无需担心手动传递它:

<body>
    Here is an image:

    <img src="{{ $message->embed($pathToImage) }}">
</body>

Warning
$message 变量在纯文本消息模板中不可用,因为纯文本消息不使用内联附件。

嵌入原始数据附件

如果您已经有了要嵌入到电子邮件模板中的原始图像数据字符串,您可以调用embedData 上的方法$message 多变的。当调用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');

Headers

有时您可能需要将额外的标头附加到外发邮件中。例如,您可能需要设置自定义Message-Id 或其他任意文本标题。

为此,定义一个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',
        ],
    );
}

标签和元数据

某些第三方电子邮件提供商(例如 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 的文档以获取更多信息tagsmetadata.同样,还可以查阅 Postmark 文档以获取有关其支持的更多信息tagsmetadata.

如果您的应用程序使用 Amazon SES 发送电子邮件,您应该使用metadata 附加方法SES“标签” 到消息。

自定义 Symfony 消息

Laravel 的邮件功能由 Symfony Mailer 提供支持。 Laravel 允许您注册自定义回调,这些回调将在发送消息之前通过 Symfony Message 实例调用。这使您有机会在发送消息之前对其进行深度自定义。为此,定义一个using 你的参数Envelope 定义:

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 编写的,Laravel 能够为消息呈现漂亮的响应式 HTML 模板,同时还自动生成纯文本副本。

生成 Markdown 邮件

要使用相应的 Markdown 模板生成邮件,您可以使用--markdown 的选项make:mail 工匠命令:

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

然后,在配置 mailable 时Content 其内的定义content 方法,使用markdown 参数而不是view 范围:

use Illuminate\Mail\Mailables\Content;

/**
 * Get the message content definition.
 */
public function content(): Content
{
    return new Content(
        markdown: 'emails.orders.shipped',
        with: [
            'url' => $this->orderUrl,
        ],
    );
}

编写 Markdown 消息

Markdown mailables 使用 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 解析器会将缩进内容呈现为代码块。

按钮组件

按钮组件呈现居中按钮链接。该组件接受两个参数,一个url 和一个可选的color.支持的颜色有primary,success, 和error.您可以根据需要向消息中添加任意数量的按钮组件:

<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 文件后,更新theme 您的应用程序的选项config/mail.php 配置文件以匹配新主题的名称。

要为单个邮件自定义主题,您可以设置$theme 可邮寄类的属性设置为发送该可邮寄时应使用的主题名称。

发送邮件

要发送消息,请使用to 上的方法Mail facade.这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”、“cc”和“bcc”收件人:

Mail::to($request->user())
    ->cc($moreUsers)
    ->bcc($evenMoreUsers)
    ->send(new OrderShipped($order));

遍历收件人

有时,您可能需要通过遍历一组收件人/电子邮件地址来向收件人列表发送邮件。然而,自从to 方法将电子邮件地址附加到可邮寄的收件人列表中,循环中的每次迭代都会向每个先前的收件人发送另一封电子邮件。因此,您应该始终为每个收件人重新创建可邮寄实例:

foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
    Mail::to($recipient)->send(new OrderShipped($order));
}

通过特定邮件程序发送邮件

默认情况下,Laravel 将使用配置为default 应用程序中的邮件程序mail 配置文件。但是,您可以使用mailer 使用特定邮件程序配置发送消息的方法:

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

排队邮件

排队邮件消息

由于发送电子邮件消息会对应用程序的响应时间产生负面影响,因此许多开发人员选择将电子邮件消息排队以供后台发送。 Laravel 使用其内置的功能使这变得容易统一队列API.要将邮件消息排队,请使用queue 上的方法Mail 指定消息收件人后的外观:

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

Note
要了解有关解决这些问题的更多信息,请查看有关的文档排队作业和数据库事务.

渲染邮件

有时您可能希望捕获可邮寄的 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);
});

Warning
内联附件 在您的浏览器中预览邮件时不会呈现。要预览这些邮件,您应该将它们发送到电子邮件测试应用程序,例如Mailpit 或者HELO.

本地化邮件

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

Testing

测试可邮寄内容

Laravel 提供了多种方法来检查邮件的结构。此外,Laravel 提供了几种方便的方法来测试您的邮件是否包含您期望的内容。这些方法是:assertSeeInHtml,assertDontSeeInHtml,assertSeeInOrderInHtml,assertSeeInText,assertDontSeeInText,assertSeeInOrderInText,assertHasAttachment,assertHasAttachedData,assertHasAttachmentFromStorage, 和assertHasAttachmentFromStorageDisk.

如您所料,“HTML”断言断言您的 mailable 的 HTML 版本包含给定的字符串,而“text”断言断言您的 mailable 的纯文本版本包含给定的字符串:

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->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

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 not sent...
        Mail::assertNotSent(AnotherMailable::class);
    }
}

如果您要在后台排队发送邮件,您应该使用assertQueued 方法代替assertSent:

Mail::assertQueued(OrderShipped::class);

Mail::assertNotQueued(OrderShipped::class);

Mail::assertNothingQueued();

您可以将闭包传递给assertSent,assertNotSent,assertQueued, 或者assertNotQueued 方法来断言发送的邮件通过给定的“真实性测试”。如果至少发送了一个通过给定真值测试的可邮寄件,则断言将成功:

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

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

您可能已经注意到,有两种方法可以断言邮件未发送:assertNotSentassertNotQueued.有时您可能希望断言没有发送邮件or 排队。为此,您可以使用assertNothingOutgoingassertNotOutgoing 方法:

Mail::assertNothingOutgoing();

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

邮件和本地开发

在开发发送电子邮件的应用程序时,您可能不想实际发送电子邮件到实时电子邮件地址。 Laravel 提供了几种方法来在本地开发期间“禁用”实际发送电子邮件。

日志驱动

而不是发送您的电子邮件,log 邮件驱动程序会将所有电子邮件消息写入您的日志文件以供检查。通常,此驱动程序仅在本地开发期间使用。有关按环境配置应用程序的更多信息,请查看配置文档.

HELO / Mailtrap / Mailpit

或者,您可以使用类似的服务HELO 或者Mailtrapsmtp 驱动程序将您的电子邮件发送到“虚拟”邮箱,您可以在真正的电子邮件客户端中查看它们。这种方法的好处是允许您在 Mailtrap 的消息查看器中实际检查最终电子邮件。

如果您正在使用Laravel 风帆,您可以使用预览消息Mailpit.当 Sail 运行时,您可以访问 Mailpit 界面:http://localhost:8025.

使用全局to 地址

最后,您可以通过调用alwaysTo提供的方法Mail 正面。通常,应从boot 您的应用程序服务提供商之一的方法:

use Illuminate\Support\Facades\Mail;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    if ($this->app->environment('local')) {
        Mail::alwaysTo('taylor@example.com');
    }
}

Events

Laravel 在发送邮件消息的过程中会触发两个事件。这MessageSending 事件在发送消息之前触发,而MessageSent 发送消息后触发事件。请记住,这些事件在邮件发送时被触发sent,而不是在排队时。您可以在您的App\Providers\EventServiceProvider 服务提供者:

use App\Listeners\LogSendingMessage;
use App\Listeners\LogSentMessage;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Mail\Events\MessageSent;

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    MessageSending::class => [
        LogSendingMessage::class,
    ],

    MessageSent::class => [
        LogSentMessage::class,
    ],
];

海关运输

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

一旦你定义了你的自定义传输,你可以通过extend 提供的方法Mail 正面。通常,这应该在boot 你的应用程序的方法AppServiceProvider 服务提供者。 A$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 注册传输来实现。例如,您可以安装并注册“Sendinblue”Symfony 邮件程序:

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

安装 Sendinblue 邮件程序包后,您可以将 Sendinblue API 凭证的条目添加到应用程序的services 配置文件:

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

接下来,您可以使用Mail 门面的extend 使用 Laravel 注册传输的方法。通常,这应该在boot 服务提供者的方法:

use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory;
use Symfony\Component\Mailer\Transport\Dsn;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Mail::extend('sendinblue', function () {
        return (new SendinblueTransportFactory)->create(
            new Dsn(
                'sendinblue+api',
                'default',
                config('services.sendinblue.key')
            )
        );
    });
}

注册传输后,您可以在应用程序的 config/mail.php 配置文件中创建一个使用新传输的邮件程序定义:

'sendinblue' => [
    'transport' => 'sendinblue',
    // ...
],
豫ICP备18041297号-2