> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 邮件发送

> 介绍如何使用 Laravel Mailable 类发送邮件，从基础用法到 Markdown 邮件、队列发送。

## Laravel 邮件功能概览

Laravel 邮件基于 [Symfony Mailer](https://symfony.com/doc/current/mailer.html)，支持 SMTP、Mailgun、Postmark、Resend、Amazon SES、sendmail 等驱动。

<Info>
  以前使用的 SwiftMailer 已被废弃。Laravel 9 起使用 Symfony Mailer。
</Info>

邮件配置写在 `config/mail.php`。可通过 `.env` 切换驱动：

```ini theme={null}
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
```

## 创建 Mailable 类

Laravel 把每种邮件表达为「Mailable」类。使用 `make:mail` 命令生成：

```shell theme={null}
php artisan make:mail OrderShipped
```

生成到 `app/Mail/`。

## Mailable 设置

生成的 Mailable 类有 `envelope()`、`content()`、`attachments()` 三个方法。

```php theme={null}
<?php

namespace App\Mail;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class OrderShipped extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: '订单已发货',
        );
    }

    public function content(): Content
    {
        return new Content(
            view: 'mail.orders.shipped',
        );
    }

    public function attachments(): array
    {
        return [];
    }
}
```

### 设置发件人

在 `envelope()` 中指定发件人：

```php theme={null}
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;

public function envelope(): Envelope
{
    return new Envelope(
        from: new Address('shop@example.com', 'Shop Support'),
        subject: '订单已发货',
    );
}
```

在 `config/mail.php` 中设置全局默认发件人可省略单独指定：

```php theme={null}
'from' => [
    'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
    'name' => env('MAIL_FROM_NAME', 'Example'),
],
```

### 设置邮件正文

`content()` 中指定 Blade 模板。构造器中赋值给 `public` 属性的数据会自动传给模板。

```php theme={null}
public function content(): Content
{
    return new Content(
        view: 'mail.orders.shipped',
    );
}
```

对应 Blade 模板 `resources/views/mail/orders/shipped.blade.php`：

```blade theme={null}
<!DOCTYPE html>
<html>
<body>
    <h1>您的订单已发货</h1>
    <p>订单号：{{ $order->id }}</p>
    <p>金额合计：¥{{ number_format($order->total) }}</p>
    <p>感谢您的购买。</p>
</body>
</html>
```

也可以通过 `with` 显式传数据，此时属性可设为 `protected` 或 `private`：

```php theme={null}
public function __construct(
    protected Order $order,
) {}

public function content(): Content
{
    return new Content(
        view: 'mail.orders.shipped',
        with: [
            'orderId' => $this->order->id,
            'total' => $this->order->total,
        ],
    );
}
```

## 发送邮件

使用 `Mail` 门面的 `to()` 指定收件人，然后 `send()`：

```mermaid theme={null}
flowchart TD
    A["Mail::to()->send(new OrderShipped())"] --> B{"实现<br>ShouldQueue?"}
    B -- "否" --> C["同步发送<br>交给 Mailer"]
    B -- "是" --> D["排队<br>后台发送"]
    D --> C
    C --> E{"MAIL 驱动"}
    E --> F["smtp"]
    E --> G["ses / mailgun<br>/ postmark"]
    E --> H["log<br>(开发用)"]
```

```php theme={null}
<?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');
    }
}
```

也可指定抄送/密送：

```php theme={null}
Mail::to($request->user())
    ->cc($ccUsers)
    ->bcc($bccUsers)
    ->send(new OrderShipped($order));
```

使用特定 mailer：

```php theme={null}
Mail::mailer('postmark')
    ->to($request->user())
    ->send(new OrderShipped($order));
```

## 使用队列发送邮件

邮件发送会影响响应时间，建议通过队列在后台发送。

```php theme={null}
Mail::to($request->user())->queue(new OrderShipped($order));
```

<Warning>
  使用队列前需先配置好队列并启动 worker。
</Warning>

延时发送：

```php theme={null}
Mail::to($request->user())
    ->later(now()->plus(minutes: 10), new OrderShipped($order));
```

在 Mailable 类上实现 `ShouldQueue` 后，调用 `send()` 也会自动进入队列：

```php theme={null}
use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Mailable implements ShouldQueue
{
    // ...
}
```

### 用 PHP 属性指定队列连接与名称

用 PHP 属性可以在类级别声明式指定，无需 `on()->queue()` 链式调用：

```php theme={null}
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;

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

## 创建 Markdown 邮件

Markdown 邮件可以使用 Laravel 提供的美观响应式 HTML 模板。

### 生成 Markdown Mailable

用 `--markdown` 选项生成：

```shell theme={null}
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
```

在 `content()` 中使用 `markdown` 参数：

```php theme={null}
use Illuminate\Mail\Mailables\Content;

public function content(): Content
{
    return new Content(
        markdown: 'mail.orders.shipped',
        with: [
            'url' => route('orders.show', $this->order),
        ],
    );
}
```

### 编写 Markdown 模板

使用 Laravel 提供的 Blade 组件：

```blade theme={null}
<x-mail::message>
# 您的订单已发货

订单已发货，特此通知。

<x-mail::button :url="$url">
查看订单
</x-mail::button>

感谢您的支持。<br>
{{ config('app.name') }}
</x-mail::message>
```

可用组件：

| 组件                 | 说明                                              |
| ------------------ | ----------------------------------------------- |
| `<x-mail::button>` | 按钮链接（可用 `color` 指定 `primary`/`success`/`error`） |
| `<x-mail::panel>`  | 突出的面板                                           |
| `<x-mail::table>`  | Markdown 表格                                     |

<Tip>
  Markdown 邮件会同时生成纯文本版本，兼容不支持 HTML 的客户端。
</Tip>

## 邮件预览

在路由中返回 Mailable 实例即可在浏览器预览，便于开发调试：

```php theme={null}
// routes/web.php
Route::get('/mailable', function () {
    $order = App\Models\Order::first();
    return new App\Mail\OrderShipped($order);
});
```

## 本地开发的邮件

为避免误发邮件到生产环境，可在本地使用 [Mailpit](https://github.com/axllent/mailpit) 等工具：

```ini theme={null}
# .env (Mailpit)
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
```

Laragon、Laravel Herd 默认已包含 Mailpit。

<Info>
  也可用 `log` 驱动把邮件内容写入日志，是最简单的确认方式。
</Info>

```ini theme={null}
MAIL_MAILER=log
```

## 小结

| 想做的事           | 方法                                |
| -------------- | --------------------------------- |
| 创建 Mailable    | `php artisan make:mail ClassName` |
| 设置发件人          | `envelope()` 的 `from`             |
| 使用 Blade 模板    | `content()` 的 `view`              |
| 使用 Markdown 模板 | `content()` 的 `markdown`          |
| 发送邮件           | `Mail::to()->send()`              |
| 排队发送           | `Mail::to()->queue()`             |
| 延时发送           | `Mail::to()->later()`             |


## Related topics

- [事件与监听器](/zh/events.md)
- [任务调度](/zh/scheduling.md)
- [通知（Notifications）](/zh/notifications.md)
- [教程 - Laravel Console Starter](/zh/packages/laravel-console-starter/tutorial.md)
- [队列与任务](/zh/queues.md)
