yzh52521 / think-mailer
A powerful and beautiful php mailer for All of ThinkPHP and Other PHP Frameworks based Symfony mailer
Requires
- php: >=8.0.2
- ext-fileinfo: *
- symfony/mailer: ^6.0
- topthink/framework: ^6.0|^8.0
- topthink/think-queue: ^3.0
Suggests
- aws/aws-sdk-php: Required to use the SES mail driver (^3.235.5).
- symfony/http-client: Required to use the Symfony API mail transports (^6.2).
- symfony/mailgun-mailer: Required to enable support for the Mailgun mail transport (^6.2).
- symfony/postmark-mailer: Required to enable support for the Postmark mail transport (^6.2).
README
支持 smtp
sendmail
Mailgun
、Postmark
、 Amazon SES
log
等驱动,其中log
驱动会把邮件内容写入日志,供调试用
安装
composer require yzh52521/think-mailer
Mailgun 驱动
要使用 Mailgun 驱动,可以先通过 composer 来安装 Mailgun 函数库 :
composer require symfony/mailgun-mailer symfony/http-client
接着,在应用的 config/mail.php 配置文件中,将默认项设置成 mailgun 配置文件中包含以下选项:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
'scheme' => 'https',
],
如果不使用 US Mailgun region 区域终端 配置区域终端:
'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), 'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'), ],
Postmark 驱动
要使用 Postmark 驱动,先通过 composer 来安装 Postmark 函数库:
composer require symfony/postmark-mailer symfony/http-client
接着,在应用的 config/mail.php 配置文件中,将默认项设置成 postmark。 config/mail.php 配置文件中包含如下选项:
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
如果你要给指定邮件程序使用的 Postmark message stream,可以在配置数组中添加 message_stream_id 配置选项。这个配置数组在应用程序的 config/mail.php 配置文件中:
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
],
这样,你还可以使用不同的 message stream 来设置多个 Postmark 邮件驱动
SES 驱动
要使用 Amazon SES 驱动,你必须先安装 PHP 的 Amazon AWS SDK 。你可以可以通过 Composer 软件包管理器安装此库:
composer require aws/aws-sdk-php
然后,将 config/mail.php 配置文件的 default 选项设置成 ses 并确认你的 config/mail.php 配置文件包含以下选项:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
为了通过 session token 来使用 AWS temporary credentials ,你需要向应用的 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'),
],
发送邮件,如果你想传递一些 额外的选项 给 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'],
],
],
],
生成 Mailables#
在构建 Thinkphp 应用程序时,应用程序发送的每种类型的电子邮件都表示为一个 mailable 类。 这些类存储在 app/mail 目录中。 如果您在应用程序中看不到此目录,请不要担心,因为它会在您使用 make:mail think 命令创建第一个可邮寄类时为您生成:
php think make:mail OrderShipped
编写 Mailables#
一旦你生成了一个 mailable 的类,打开它,这样我们就可以探索它的内容了。 首先,请注意所有可邮寄类的配置都是在 build 方法中完成的。 在此方法中,您可以调用各种方法,例如 from、subject、view 和 attach 来配置电子邮件的呈现和传递。
配置发件人#
使用 from 方法 首先,让我们浏览一下邮件的发件人的配置。或者,换句话说,邮件来自谁。有两种方法配置发件人。第一种,你可以在 mailable 类的 from 方法中使用 build 方法:
/** * 构建消息 * * @return $this */ public function build() { return $this->from('example@example.com', 'Example') ->view('emails/orders/shipped'); }
使用全局 from 地址#
当然,如果你的应用在任何邮件中使用的「发件人」地址都一致的话,在你生成的每一个 mailable 类中调用 from 方法可能会很麻烦。因此,你可以在 config/mail.php 文件中指定一个全局的「发件人」地址。当某个 mailable 类没有指定「发件人」时,它将使用该全局「发件人」:
'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
此外,你可以在 config/mail.php 配置文件中定义一个全局的「回复」地址:
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
配置视图#
你可以在 mailable 类的 build 方法中使用 view 方法来指定在渲染邮件内容时要使用的模板。 由于每封邮件通常使用 think 模板 来渲染其内容,因此在构建邮件 HTML 内容时你可以使用 think 模板引擎提供的所有功能及享受其带来的便利性:
/** * Build the message. * * @return $this */ public function build() { return $this->view('emails/orders/shipped'); }
技巧:你可以创建一个 view/emails 目录来存放你的所有邮件模板;当然,你也可以将其置于 view 目录下的任何位置。
纯文本邮件#
你可以使用 text 方法来定义一个纯文本格式的邮件。和 view 方法一样, 该 text 方法接受一个模板名,模板名指定了在渲染邮件内容时你想使用的模板。你既可以定义纯文本格式亦可定义 HTML 格式:
/** * 构建消息. * * @return $this */ public function build() { return $this->view('emails/orders/shipped') ->text('emails/orders/shipped_plain'); }
视图数据#
通过 Public 属性
通常情况下,你可能想要在渲染邮件的 HTML 内容时传递一些数据到视图中。有两种方法传递数据到视图中。第一种,你在 mailable 类中定义的所有 public 的属性都将自动传递到视图中。因此,举个例子,你可以将数据传递到你的 mailable 类的构造函数中,并将其设置为类的 public 属性:
<?php namespace app\mail; use app\model\Order; use think\queue\ShouldQueue; use yzh52521\mail\Mailable; class OrderShipped extends Mailable { /** * 订单实例. * * @var \app\model\Order */ public $order; /** * 创建一个消息实例. * * @param \app\model\Order $order * @return void */ public function __construct(Order $order) { $this->order = $order; } /** * 构建消息. * * @return $this */ public function build() { return $this->view('emails/orders/shipped'); } }
当数据被设置成为 public 属性之后,它将被自动传递到你的视图中,因此你可以像您在 thinkphp 模板中那样访问它们:
<div> Price: { $order->price } </div>
通过 with 方法:
如果你想要在邮件数据发送到模板前自定义它们的格式,你可以使用 with 方法来手动传递数据到视图中。一般情况下,你还是需要通过 mailable 类的构造函数来传递数据;不过,你应该将它们定义为 protected 或 private 以防止它们被自动传递到视图中。然后,在您调用 with 方法的时候,你可以以数组的形式传递你想要传递给模板的数据:
<?php namespace app\mail; use app\model\Order; use think\queue\ShouldQueue; use yzh52521\mail\Mailable; class OrderShipped extends Mailable { /** * 订单实例. * * @var \app\model\Order */ protected $order; /** * 创建消息实例. * * @param \app\model\Order $order * @return void */ public function __construct(Order $order) { $this->order = $order; } /** * 构建消息. * * @return $this */ public function build() { return $this->view('emails/orders/shipped') ->with([ 'orderName' => $this->order->name, 'orderPrice' => $this->order->price, ]); } }
当数据使用 with 法传递后,你便可以在视图中使用它们,此时,便可以像 thinkphp 模板的方式来访问它们:
<div> Price: { $orderPrice } </div>
附件
要在邮件中加入附件,在 build 方法中使用 attach 方法。 该 attach 方法接受文件的绝对路径作为它的第一个参数:
/** * Build the message. * * @return $this */ public function build() { return $this->view('emails/orders/shipped') ->attach('/path/to/file'); }
当附加文件到消息时,你也可以传递一个 array 给 attach 方法作为第二个参数,以指定显示名称和 / 或是 MIME 类型:
/** * 构建消息. * * @return $this */ public function build() { return $this->view('emails/orders/shipped') ->attach('/path/to/file', [ 'name' => 'name.pdf', 'mime' => 'application/pdf', ]); }
原始数据附件
该 attachData 可以使用字节数据作为附件。例如,你可以使用这个方法将内存中生成而没有保存到磁盘中的 PDF 附加到邮件中。 attachData 方法第一个参数接收原始字节数据,第二个参数为文件名,第三个参数接受一个数组以指定其他参数:
/** * 构建消息. * * @return $this */ public function build() { return $this->view('emails/orders/shipped') ->attachData($this->pdf, 'name.pdf', [ 'mime' => 'application/pdf', ]); }
内联附件
在邮件中嵌入内联图片通常很麻烦;
在 with()
方法 参数格式为 'cid:image' => '/path/to/image.jpg'
或者 cid:image' => ['file_stream', 'filename','filemine']
, 即参数数组的键名是上面配置的 嵌入标签 + 变量名
,
但值有两种情况:
第一, 如果值为字符串, 则该值为图片的路径 (绝对路径或相对路径) 或者 有效的url地址;
第二, 如果值为数组, 数组为 ['stream','name','mime']
的形式, 其中 stream
表示图片的数据流, 即是未保存的文件数据流,
例如 fopen()
方法获取的文件数据流, 第二个参数为文件名, 默认为 image.jpg
,第三个参数可选, 为文件的mime类型, 默认为 image/jpeg
->with([ 'date' => date('Y-m-d H:i:s'), 'cid:image' => '/path/to/image1.jpg', // 'cid:image' => [fopen('/path/to/image1.jpg','r')], // 'cid:image' => [fopen('/path/to/image1.jpg','r'),'image.jpg'], // 'cid:image' => [fopen('/path/to/image1.jpg','r'),'image.jpg','image/jpg'], ])
其中模板的内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试邮件</title>
</head>
<body>
<p>尊敬的cattong:</p>
<p> 这是一封模板测试邮件</p>
<p>{$date}</p>
<p>
<img src="{$image}" alt="">
</p>
</body>
</html>
自定义 Symfony 消息
该 Mailable 基类的 withSymfonyMessage 方法允许您注册一个闭包,在发送消息之前将使用 Symfony 消息实例调用该闭包。这使您有机会在消息传递之前对其进行深度自定义:
use Symfony\Component\Mime\Email;
public function build()
{
$this->view('emails/orders/shipped');
$this->withSymfonyMessage(function (Email $message) {
$message->getHeaders()->addTextHeader(
'Custom-Header', 'Header Value'
);
});
return $this;
}
发送邮件#
要发送邮件,使用 Mail 门面 的方法。该 to 方法接受 邮件地址、用户实例或用户集合。如果传递一个对象或者对象集合,mailer 在设置收件人时将自动使用它们的 email 和 name 属性,因此请确保对象的这些属性可用。一旦指定了收件人,就可以将 mailable 类实例传递给 send 方法:
<?php namespace app\controller; use app\mail\OrderShipped; use app\model\Order; use yzh52521\facade\Mail; class OrderShipmentController { /** * 发送给定的订单. * * @param \think\Request $request */ public function store(Request $request) { $order = Order::findOrFail($request->order_id); // Ship the order... Mail::send(new OrderShipped($order)); } }
在发送消息时不止可以指定收件人。还可以通过链式调用「to」、「cc」、「bcc」一次性指定抄送和密送收件人:
Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->send(new OrderShipped($order));
通过特定的 Mailer 发送邮件#
默认情况下,Think 将使用 mail 你的配置文件中配置为 default 邮件程序。 但是,你可以使用 mailer 方法通过特定的邮件程序配置发送:
Mail::mailer('smtp') ->to($request->user()) ->send(new OrderShipped($order));
邮件队列
将邮件消息加入队列
由于发送邮件消息可能大幅度延长应用的响应时间,许多开发者选择将邮件消息加入队列放在后台发送。thinkphp 使用内置的 统一队列 API 简化了这一工作。若要将邮件消息加入队列,可以在指定消息的接收者后,使用 Mail 门面的 queue 方法:
Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->queue(new OrderShipped($order));
以上方式都自动将作业推送到队列中以便消息在后台发送。
延迟消息队列
想要延迟发送队列化的邮件消息,可以使用设置 Mailable类 later属性
class OrderShipped extends Mailable { public function __construct(Order $order) { $this->order = $order; $this->delay =5; } } 或者 Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->later(10, new OrderShipped($order));
推送到指定队列
可以使用设置 Mailable类 queue connection 属性 :
class OrderShipped extends Mailable { public function __construct(Order $order) { $this->order = $order; $this->delay =5; $this->queue ='emails'; $this->connection ='sqs'; } } 或者 $message = (new OrderShipped($order)) ->onConnection('redis') ->onQueue('emails'); Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) ->queue($message);
默认队列
如果你希望你的邮件类始终使用队列,你可以给邮件类实现 ShouldQueue 契约,现在即使你调用了 send 方法,邮件依旧使用队列的方式发送
use think\queue\ShouldQueue; use yzh52521\mail\Mailable; class OrderShipped extends Mailable implements ShouldQueue { // }
渲染邮件#
有时您可能希望捕获邮件的 HTML 内容而不发送它。为此,可以调用 render 方法。此方法将以字符串形式返回邮件类的渲染内容:
$order = Order::find(1); return (new OrderShipped($order))->render();
事件
在发送邮件消息的过程中会触发 2 个事件。MessageSending 事件在消息发送之前触发,而 MessageSent 事件在消息发送后触发。请记住,这些事件是在邮件发送时触发的,而不是在排队时触发的。你可以在 event.php 中 监听事件:
use yzh52521\mail\events\MessageSending; use yzh52521\mail\events\MessageSent; return [ 'listen' => [ MessageSending::class => [ app\listeners\LogSendingMessage::class, ], MessageSent::class => [ app\listeners\LogSentMessage::class, ], ], ]
日志驱动程序
log 邮件驱动程序不会发送您的电子邮件,而是将所有电子邮件信息写入您的日志文件以供检查。 通常,此驱动程序仅在本地开发期间使用。有关按环境配置应用程序的更多信息,请查看 配置文档。