Skip to content

Laravel - 发送邮件

Laravel 在流行的 Symfony Mailer 组件(以前是 SwiftMailer)之上提供了简洁、简单的 API,让你能够快速通过本地或基于云的服务开始发送邮件。

Laravel 的邮件服务通过应用程序的 .env 文件和 config/mail.php 配置文件进行配置。.env 文件允许你在不修改代码的情况下快速更改邮件驱动和凭证。

常见的 .env 邮件变量:

MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

常见选项说明:

  • MAIL_MAILER:要使用的驱动(例如,smtp、ses、mailgun、postmark、sendmail、log)。对于本地开发,强烈推荐使用 log(将邮件写入日志文件)或像 Mailpit/MailHog 这样的工具(smtp 配置到其主机/端口)。
  • MAIL_HOST、MAIL_PORT、MAIL_USERNAME、MAIL_PASSWORD、MAIL_ENCRYPTION:用于 SMTP 的凭证。
  • MAIL_FROM_ADDRESS、MAIL_FROM_NAME:邮件的默认发件人地址和名称。

对于像 Gmail 这样的服务,如果启用了两步验证,你可能需要生成一个“应用专用密码”(App Password)。以下是 Gmail SMTP 的配置示例:

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-gmail-username@gmail.com
MAIL_PASSWORD=your-gmail-app-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="your-gmail-username@gmail.com"
MAIL_FROM_NAME="Your Application Name"

修改 .env 文件后,清除配置缓存:php artisan config:clear。

在 Laravel 中,应用程序发送的每种类型的电子邮件都表示为一个 “mailable” 类。这些类存储在 app/Mail 目录中。你可以使用 Artisan 命令生成一个新的 mailable 类:

php artisan make:mail OrderShipped

该命令将创建 app/Mail/OrderShipped.php 文件。一个 mailable 类包含用于构建电子邮件的方法,例如定义其发件人、收件人、主题和要使用的视图。

在一个 mailable 类中,你通常在构造函数中配置其属性(要传递给视图的数据),并在 build 方法(旧版本)或 envelope 和 content 方法(新版本)中构建电子邮件消息。

Mailable 示例(OrderShipped.php - 使用 Envelope 和 Content 的现代语法):

<?php
namespace App\Mail;
use App\Models\Order; // 假设你有一个 Order 模型
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable implements ShouldQueue // 可选:用于加入队列
{
use Queueable, SerializesModels;
public Order $order;
/**
* Create a new message instance.
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
from: new \Illuminate\Mail\Mailables\Address('noreply@example.com', 'Example App'),
subject: 'Your Order Has Shipped!',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'emails.orders.shipped', // 邮件的 Blade 视图
// with: ['orderName' => $this->order->name], // 公共属性的替代方案
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
// Attachment::fromPath(public_path('/path/to/file.pdf')),
];
}
}

创建邮件视图(resources/views/emails/orders/shipped.blade.php):

Section titled “创建邮件视图(resources/views/emails/orders/shipped.blade.php):”
<!DOCTYPE html>
<html>
<head>
<title>Order Shipped</title>
</head>
<body>
<h1>Hi, {{ $order->customer_name }}!</h1> {{-- 访问 Mailable 类中的公共属性 --}}
<p>Your order #{{ $order->id }} has been shipped.</p>
<p>Thank you for your purchase!</p>
</body>
</html>

要发送 mailable,请使用 Mail Facade 上的 to 方法。to 方法接受电子邮件地址、用户实例或用户集合。然后,使用 mailable 类的实例调用 send 方法。

在控制器中的示例:

<?php
namespace App\Http\Controllers;
use App\Mail\OrderShipped;
use App\Models\Order; // 假设 Order 模型存在
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderController extends Controller
{
public function ship(Request $request, string $orderId)
{
$order = Order::findOrFail($orderId); // 获取订单
// 发送邮件
Mail::to($order->customer_email) // 如果发送给已登录用户,也可以用 $request->user()
// ->cc($moreUsers)
// ->bcc($evenMoreUsers)
->send(new OrderShipped($order));
return redirect()->route('orders.show', $orderId)->with('status', 'Order shipped email sent!');
}
}

通常你会从路由定义中调用这个 ship 方法。

发送纯文本邮件(使用 Mail::raw 或 Mailable 中的文本视图)

Section titled “发送纯文本邮件(使用 Mail::raw 或 Mailable 中的文本视图)”

如果你需要发送一个没有 Blade 模板的简单纯文本邮件,可以使用 Mail::raw():

Mail::raw('This is a plain text email body.', function ($message) {
$message->to('recipient@example.com')
->subject('Simple Text Email');
});

对于 Mailable 类,你可以在 Content 对象中指定一个文本视图:

public function content(): Content
{
return new Content(
view: 'emails.orders.shipped', // HTML 视图
text: 'emails.orders.shipped_plain' // 纯文本视图
);
}

要添加附件,请在 Mailable 类的 attachments 方法中或直接在 Envelope 对象(或旧版本中的 build 方法)上使用 attach 方法。

// 在 Mailable 类的 attachments() 方法中(较新的 Laravel):
use Illuminate\Mail\Mailables\Attachment;
public function attachments(): array
{
return [
Attachment::fromPath(public_path('documents/invoice.pdf'))
->as('customer-invoice.pdf')
->withMime('application/pdf'),
Attachment::fromStorageDisk('s3', 'avatars/profile.jpg'), // 从存储盘附加
];
}

发送电子邮件可能会显著降低应用程序的响应时间。Laravel 允许你轻松地将邮件加入队列进行后台发送。在你的 mailable 类上实现 ShouldQueue 接口。

class OrderShipped extends Mailable implements ShouldQueue
{ ... }

然后,当你调用 Mail::send() 时,Laravel 会自动将该任务推送到你的队列。你需要运行一个队列工作进程:php artisan queue:work。

在本地开发期间,你通常不希望发送真实的电子邮件。像 Mailpit 或 MailHog 这样的工具充当本地 SMTP 服务器,它们会捕获所有外发邮件并将其显示在 Web 界面中。这对于测试来说非常宝贵。

配置你的 .env 文件以指向 Mailpit(通常运行在 localhost:1025 上):

MAIL_MAILER=smtp
MAIL_HOST=localhost # or 127.0.0.1
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null # or 'tls' if Mailpit is configured for it

常见的学习障碍:电子邮件配置问题

许多初学者在发送电子邮件时会遇到困难,原因可能是不正确的 .env 配置或邮件服务器本身的问题(例如,Gmail 阻止安全性较低的应用,ISP 阻止 SMTP 端口)。在开发过程中使用 MAIL_MAILER=log 或 Mailpit 可以帮助隔离问题是出在 Laravel 的代码还是邮件服务器设置上。

有关详细信息,请查阅 Laravel 关于邮件的官方文档:https://laravel.com/docs/mail