wnx / laravel-sends
跟踪您的Laravel应用程序中的发出的电子邮件。
Requires
- php: ^8.0
- illuminate/contracts: ^9.0 | ^10.0 | ^11.0
- spatie/laravel-package-tools: ^1.9.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.4
- larastan/larastan: ^2.0
- nunomaduro/collision: ^5.10 |^6.0 | ^7.0 | ^8.0
- orchestra/testbench: ^7 | ^8 | ^9
- pestphp/pest: ^1.21 | ^2.0
- pestphp/pest-plugin-laravel: ^1.1 | ^2.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.4 | ^10.0
README
此包帮助您跟踪Laravel应用程序中的发出的电子邮件。此外,您可以将模型与发出的电子邮件关联起来。
以下是一些简短的示例,展示您可以做什么
// An email is sent to $user. The passed $product and $user models will be // associated with the sent out email generated by the `ProductReviewMail` mailable. Mail::to($user)->send(new ProductReviewMail($product, $user));
在您的应用程序中,您可以获取用户或产品的所有已发出的电子邮件。
$user->sends()->get(); $product->sends()->get();
或者您可以获取给定Mailable类的所有已发出的电子邮件。
Send::forMailClass(ProductReviewMail::class)->get();
每个Send
-模型包含以下信息
- mailable类的FQN(完全限定名)
- 主题
- 发件人地址
- 回复地址
- 收件人地址
- cc地址
- bcc地址
此外,sends
-表还有以下列,这些列可以由您的应用程序填充(了解更多)。
delivered_at
last_opened_at
打开次数
点击次数
last_clicked_at
complained_at
bounced_at
permanent_bounced_at
rejected_at
安装
您可以通过composer安装此包
composer require wnx/laravel-sends
然后,发布并运行迁移
php artisan vendor:publish --tag="sends-migrations"
php artisan migrate
可选地,您可以使用以下命令发布配置文件
php artisan vendor:publish --tag="sends-config"
这是发布配置文件的内容
return [ /* * The fully qualified class name of the `Send` model. */ 'send_model' => \Wnx\Sends\Models\Send::class, /** * If set to true, the content of sent mails is saved to the database. */ 'store_content' => env('SENDS_STORE_CONTENT', false), 'headers' => [ /** * Header containing the encrypted FQN of the mailable class. */ 'mail_class' => env('SENDS_HEADERS_MAIL_CLASS', 'X-Laravel-Mail-Class'), /** * Header containing an encrypted JSON object with information which * Eloquent models are associated with the mailable class. */ 'models' => env('SENDS_HEADERS_MAIL_MODELS', 'X-Laravel-Mail-Models'), /** * Header containing unique ID of the sent out mailable class. * Set this to `Message-ID`, if you want to use the Message ID as the unique ID identifing the sent mail. */ 'send_uuid' => env('SENDS_HEADERS_SEND_UUID', 'X-Laravel-Send-UUID'), ], ];
用法
安装完成后,更新您的应用程序的EventServiceProvider
以监听MessageSent
事件。将StoreOutgoingMailListener
-类作为监听器添加。
// app/Providers/EventServiceProvider.php protected $listen = [ // ... \Illuminate\Mail\Events\MessageSent::class => [ \Wnx\Sends\Listeners\StoreOutgoingMailListener::class, ], ]
现在,所有由mailables或通知创建的发出的电子邮件的元数据现在都存储在sends
-表中。(注意,您只能将模型与mailables关联;但不能与通知关联)
阅读以下内容以了解如何存储名称以及如何将模型与Mailable类关联。
在Send模型上存储Mailable类名
默认情况下,事件监听器存储邮件的主题和收件人地址。这很棒,但我们可以做得更好。对于您的应用程序来说,了解哪个Mailable类触发了发送的电子邮件可能是有益的。
要存储此信息,请像下面那样将StoreMailables
-trait添加到您的Mailable类中。现在您可以访问一些辅助方法。
根据您如何编写Mailables,有不同方法来使用这些新方法。在您的Mailable的build
方法中调用storeClassName
-方法。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct(public User $user, public Product $product) { } public function build() { return $this ->storeClassName() ->view('emails.products.review') ->subject("$this->product->name waits for your review"); } }
如果您使用Laravel v9.35中引入的Mailable语法,您可以在headers
方法中使用$this->storeClassName()
,或者将$this->getMailClassHeader()->toArray()
传递给Header
对象。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct( public User $user, public Product $product ) { } // ... /** * @return \Illuminate\Mail\Mailables\Headers */ public function headers() { // Call storeClassName() and let the package take care of adding the // header to the outgoing mail. $this->storeClassName(); // Or – if you want more control – use the getMailClassHeader() method // to get a Header instance and merge it with your own headers. return new Headers( text: [ 'X-Custom-Header' => 'Custom Value', ...$this->getMailClassHeader()->toArray(), ], ); } }
此方法将添加一个X-Laravel-Mail-Class
-头到发出的电子邮件中,其中包含Mailable类的完全限定名(FQN)作为加密字符串。(例如:App\Mails\ProductReviewMail
)。更新SENDS_HEADERS_MAIL_CLASS
-env变量以调整头名称。(有关详细信息,请参阅配置)。
然后,包的事件监听器将查找该头,解密值并将其存储在数据库中。
将Sends与相关模型关联
现在您已经记录了所有发出的电子邮件,如果您能通过关联的模型访问这些记录,岂不是很好?在上面的例子中,我们向用户发送了一个 ProductReviewMail
。您想查看为特定的 Product
-模型发送了多少封电子邮件吗?
要实现这一点,您需要在模型中添加 HasSends
接口和 HasSendsTrait
特性。
use Wnx\Sends\Contracts\HasSends; use Wnx\Sends\Support\HasSendsTrait; class Product extends Model implements HasSends { use HasSendsTrait; }
现在您可以在 build()
方法内调用 associateWith()
方法。将您想与 Mailable 类关联的模型作为参数传递给方法。除了作为参数传递模型外,您还可以将它们作为数组传递。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct( private User $user, private Product $product ) { } public function build() { return $this ->storeClassName() ->associateWith($this->product) // ->associateWith([$this->product]) ->view('emails.products.review') ->subject("$this->user->name, $this->product->name waits for your review"); } }
如果您使用的是 Laravel v9.35 中引入的 Mailable 语法,您也可以在 headers
方法中调用 associateWith()
或 getMailModelsHeader()
。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct( private User $user, private Product $product ) { } // ... /** * @return \Illuminate\Mail\Mailables\Headers */ public function headers() { // Call associateWith() and the package automatically associates the public // properties with this mailable. $this->associateWith($this->user); $this->associateWith([$this->product]); // Or – if you want more control – use the getMailModelsHeader() method // to get a Header instance and merge it with your own headers. return new Headers( text: [ 'X-Custom-Header' => 'Custom Value', ...$this->getMailModelsHeader($this->user)->toArray(), ...$this->getMailModelsHeader([$this->product])->toArray(), ], ); } }
现在您可以通过产品的 sends
关联关系访问发送出去的电子邮件。
$product->sends()->get();
自动关联模型与 Mailable
如果您没有向 associateWith
方法传递参数,则该软件包会自动将实现 HasSends
接口的所有公共属性与 Mailable 类关联。🪄
在下面的示例中,ProductReviewMail
-Mailable 将自动与 $product
模型关联,因为 Product
实现了 HasSends
接口。由于 $user
模型被声明为私有属性,因此将忽略它。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct( private User $user, public Product $product ) { } public function build() { return $this ->associateWith() ->view('emails.products.review') ->subject("$this->user->name, $this->product->name waits for your review"); } }
如果您使用的是 Laravel v9.35 中引入的 Mailable 语法,您也可以在 headers
方法中调用 associateWith()
或 getMailModelsHeader()
。
class ProductReviewMail extends Mailable { use SerializesModels; use StoreMailables; public function __construct( private User $user, public Product $product ) { } // ... /** * @return \Illuminate\Mail\Mailables\Headers */ public function headers() { // Call associateWith() and the package automatically associates the public // properties with this mailable. $this->associateWith(); // Or – if you want more control – use the getMailModelsHeader() method // to get a Header instance and merge it with your own headers. return new Headers( text: [ 'X-Custom-Header' => 'Custom Value', ...$this->getMailModelsHeader()->toArray(), ], ); } }
将自定义消息 ID附加到邮件
如果您通过 AWS SES 或类似服务发送电子邮件,您可能希望在将来识别发送的电子邮件(例如,当“已投递”事件的 webhook 被发送到您的应用程序时)。
该软件包附带了一个事件监听器来帮助您。更新 EventServiceProvider 以监听 MessageSending
事件,并将 AttachSendUuidListener
添加为监听器。将一个 X-Laravel-Message-UUID
标头附加到所有发出的电子邮件。该标头包含一个 UUID 值。(此值不能与 RFC 2392 中定义的 Message-ID
进行比较)
然后您可以在应用程序中使用 X-Laravel-Message-UUID
或 $send->uuid
的值。
// app/Providers/EventServiceProvider.php protected $listen = [ // ... \Illuminate\Mail\Events\MessageSending::class => [ \Wnx\Sends\Listeners\AttachSendUuidListener::class, ], ]
(如果您想在数据库中存储 Message-ID
的值,请不要添加事件监听器,而是更新 SENDS_HEADERS_SEND_UUID
环境变量到 Message-ID
。然后,StoreOutgoingMailListener
将将 Message-ID
存储到数据库中。)
存储邮件内容
默认情况下,该软件包不会存储发送出去的电子邮件的内容。通过将 sends.store_content
配置值设置为 true
,所有发出的邮件的正文都存储在 sends
数据库表的 content
列中。
/** * If set to true, the contet of sent mails is saved to the database. */ 'store_content' => true,
SENDS_STORE_CONTENT=true
自定义存储在发送模型中的属性
如果您需要与 Send
模型一起存储更多属性,您可以扩展 StoreOutgoingMailListener
并重写 getSendAttributes
方法。
例如,假设我们想将 audience
值与每封发送出去的电子邮件一起存储。我们创建一个新的事件监听器,名为 CustomStoreOutgoingMailListener
,并将其用作 MessageSent
事件的监听器。
我们的 EventServiceProvider
将看起来像这样。
// app/Providers/EventServiceProvider.php protected $listen = [ // ... \Illuminate\Mail\Events\MessageSent::class => [ \App\Listeners\CustomStoreOutgoingMailListener::class, ], ]
监听器本身将如下所示。我们扩展 Wnx\Sends\Listeners\StoreOutgoingMailListener
并重写 getSendAttributes
。我们将 $defaultAttributes
与我们想要存储的自定义属性合并。在下面的示例中,我们存储了一个 audience
值。
<?php namespace App\Listeners; use Illuminate\Mail\Events\MessageSent; use Wnx\Sends\Listeners\StoreOutgoingMailListener; class CustomStoreOutgoingMailListener extends StoreOutgoingMailListener { protected function getSendAttributes(MessageSent $event, array $defaultAttributes): array { return array_merge($defaultAttributes, [ 'audience' => $this->getAudience($event), ]); }
修剪发送模型
默认情况下,Send
模型将永久保留在您的数据库中。如果您的应用程序每天发送数千封电子邮件,您可能希望几天或几个月后修剪记录。
为此,请使用 Laravel 的 prunable 功能。
在您的 app/Models
中创建一个新的继承自 Wnx\Sends\Models\Send
的 Send
模型。然后添加 Prunable
特性,并按需设置 prunable()
方法。以下示例删除了所有超过1个月的 Send
模型。
// app/Models/Send.php namespace App\Models; use Illuminate\Database\Eloquent\Prunable; use Wnx\Sends\Models\Send as BaseSend; class Send extends BaseSend { use Prunable; public function prunable() { return static::where('created_at', '<=', now()->subMonth()); } }
可选地,您还可以更新配置,以便包内部使用您的 Send 模型。
// config/sends.php /* * The fully qualified class name of the send model. */ 'send_model' => \App\Models\Send::class,
sends
表的进一步使用
如您所注意到的,sends
表包含的列比当前包填充的列要多。这是有意为之的。
鼓励您编写自己的应用程序逻辑来填充这些目前为空的列。例如,如果您通过 AWS SES 发送电子邮件,我强烈建议您使用 renoki-co/laravel-aws-webhooks 包来处理 AWS SNS 通知。
处理 "Delivered" 事件的控制器可能如下所示。
class AwsSnsSesWebhookController extends SesWebhook { protected function onDelivery(array $message) { $uuidHeader = collect(Arr::get($message, 'mail.headers', [])) ->firstWhere('name', config('sends.headers.send_uuid')); if ($uuidHeader === null) { return; } $send = Send::forUuid($uuidHeader['value'])->first(); if ($send === null) { return; } $send->delivered_at = now(); $send->save(); } }
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志。
贡献
有关详细信息,请参阅 贡献指南。
安全漏洞
有关如何报告安全漏洞的详细信息,请参阅 我们的安全策略。
鸣谢
许可协议
MIT 许可协议 (MIT)。有关更多信息,请参阅 许可文件。