通知
介绍
除了支持 发送电子邮件 外,Laravel 还支持通过各种交付渠道发送通知,包括电子邮件、短信(通过 Vonage,以前称为 Nexmo)和 Slack。此外,还创建了各种 社区构建的通知渠道,用于通过数十个不同的渠道发送通知!通知也可以存储在数据库中,以便在您的 Web 界面中显示。
通常,通知应该是简短的、信息性的消息,用于通知用户在您的应用程序中发生的事件。例如,如果您正在编写一个计费应用程序,您可能会通过电子邮件和短信渠道向您的用户发送“发票已支付”通知。
生成通知
在 Laravel 中,每个通知都由单个类表示,该类通常存储在app/Notifications
目录中。如果您在应用程序中没有看到此目录,请不要担心——当您运行make:notification
Artisan 命令时,它将为您创建。
php artisan make:notification InvoicePaid
此命令会将一个新的通知类放在您的app/Notifications
目录中。每个通知类都包含一个via
方法和可变数量的消息构建方法,例如toMail
或toDatabase
,它们将通知转换为适合特定通道的邮件。
发送通知
使用 Notifiable 特性
通知可以通过两种方式发送:使用Notifiable
特性的notify
方法或使用Notification
门面。Notifiable
特性默认包含在您的应用程序的App\Models\User
模型中。
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable; class User extends Authenticatable{ use Notifiable;}
此特性提供的notify
方法需要接收一个通知实例。
use App\Notifications\InvoicePaid; $user->notify(new InvoicePaid($invoice));
记住,您可以在任何模型上使用Notifiable
特性。您不限于仅将其包含在User
模型中。
使用 Notification 门面
或者,您可以通过Notification
门面 发送通知。当您需要向多个可通知实体(例如用户集合)发送通知时,此方法很有用。要使用门面发送通知,请将所有可通知实体和通知实例传递给send
方法。
use Illuminate\Support\Facades\Notification; Notification::send($users, new InvoicePaid($invoice));
您还可以使用sendNow
方法立即发送通知。即使通知实现了ShouldQueue
接口,此方法也会立即发送通知。
Notification::sendNow($developers, new DeploymentCompleted($deployment));
指定发送渠道
每个通知类都有一个via
方法,该方法确定将在哪些通道上发送通知。通知可以在mail
、database
、broadcast
、vonage
和slack
通道上发送。
如果您想使用其他交付渠道(例如 Telegram 或 Pusher),请查看社区驱动的 Laravel Notification Channels 网站。
via
方法接收一个$notifiable
实例,它将是正在向其发送通知的类的实例。您可以使用$notifiable
来确定应在哪些通道上发送通知。
/** * Get the notification's delivery channels. * * @return array<int, string> */public function via(object $notifiable): array{ return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];}
将通知排队
在排队通知之前,您应该配置您的队列并 启动一个工作进程。
发送通知可能需要一些时间,尤其是在通道需要进行外部 API 调用来发送通知的情况下。为了加快应用程序的响应时间,您可以通过向类中添加ShouldQueue
接口和Queueable
特性来让您的通知排队。使用make:notification
命令生成的全部通知已导入该接口和特性,因此您可以立即将它们添加到您的通知类中。
<?php namespace App\Notifications; use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Notifications\Notification; class InvoicePaid extends Notification implements ShouldQueue{ use Queueable; // ...}
将ShouldQueue
接口添加到您的通知后,您可以像往常一样发送通知。Laravel 将检测类上的ShouldQueue
接口并自动将通知的发送排队。
$user->notify(new InvoicePaid($invoice));
排队通知时,将为每个收件人和通道组合创建一个排队作业。例如,如果您的通知有三个收件人和两个通道,则将六个作业分派到队列中。
延迟通知
如果您想延迟通知的发送,可以在通知实例化时链接delay
方法。
$delay = now()->addMinutes(10); $user->notify((new InvoicePaid($invoice))->delay($delay));
您可以将数组传递给delay
方法,以指定特定通道的延迟时间。
$user->notify((new InvoicePaid($invoice))->delay([ 'mail' => now()->addMinutes(5), 'sms' => now()->addMinutes(10),]));
或者,您可以在通知类本身定义一个withDelay
方法。withDelay
方法应该返回一个通道名称和延迟值的数组。
/** * Determine the notification's delivery delay. * * @return array<string, \Illuminate\Support\Carbon> */public function withDelay(object $notifiable): array{ return [ 'mail' => now()->addMinutes(5), 'sms' => now()->addMinutes(10), ];}
自定义通知队列连接
默认情况下,排队的通知将使用应用程序的默认队列连接进行排队。如果您想指定应为特定通知使用的不同连接,可以在通知的构造函数中调用onConnection
方法。
<?php namespace App\Notifications; use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Notifications\Notification; class InvoicePaid extends Notification implements ShouldQueue{ use Queueable; /** * Create a new notification instance. */ public function __construct() { $this->onConnection('redis'); }}
或者,如果您想指定应为通知支持的每个通知通道使用的特定队列连接,可以在通知上定义一个viaConnections
方法。此方法应该返回通道名称/队列连接名称对的数组。
/** * Determine which connections should be used for each notification channel. * * @return array<string, string> */public function viaConnections(): array{ return [ 'mail' => 'redis', 'database' => 'sync', ];}
自定义通知通道队列
如果您想指定应为通知支持的每个通知通道使用的特定队列,可以在通知上定义一个viaQueues
方法。此方法应该返回通道名称/队列名称对的数组。
/** * Determine which queues should be used for each notification channel. * * @return array<string, string> */public function viaQueues(): array{ return [ 'mail' => 'mail-queue', 'slack' => 'slack-queue', ];}
排队通知中间件
排队的通知可以 像排队作业一样定义中间件。要开始,请在您的通知类上定义一个middleware
方法。middleware
方法将接收$notifiable
和$channel
变量,允许您根据通知的目标自定义返回的中间件。
use Illuminate\Queue\Middleware\RateLimited; /** * Get the middleware the notification job should pass through. * * @return array<int, object> */public function middleware(object $notifiable, string $channel){ return match ($channel) { 'email' => [new RateLimited('postmark')], 'slack' => [new RateLimited('slack')], default => [], };}
排队通知和数据库事务
当排队通知在数据库事务内分派时,它们可能在数据库事务提交之前就被队列处理。发生这种情况时,您在数据库事务期间对模型或数据库记录所做的任何更新可能尚未反映在数据库中。此外,在事务中创建的任何模型或数据库记录可能不存在于数据库中。如果您的通知依赖于这些模型,则在处理发送排队通知的作业时可能会发生意外错误。
如果您的队列连接的after_commit
配置选项设置为false
,您仍然可以通过在发送通知时调用afterCommit
方法来指示应在所有打开的数据库事务提交后分派特定排队通知。
use App\Notifications\InvoicePaid; $user->notify((new InvoicePaid($invoice))->afterCommit());
或者,您可以从通知的构造函数中调用afterCommit
方法。
<?php namespace App\Notifications; use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Notifications\Notification; class InvoicePaid extends Notification implements ShouldQueue{ use Queueable; /** * Create a new notification instance. */ public function __construct() { $this->afterCommit(); }}
要了解有关解决这些问题的更多信息,请查看有关排队作业和数据库事务的文档。
确定是否应发送排队通知
为后台处理的队列分派排队通知后,队列工作程序通常会接受它并将其发送给预期的接收者。
但是,如果您希望在队列工作程序处理排队通知后最终确定是否应发送该通知,则可以在通知类上定义shouldSend
方法。如果此方法返回false
,则不会发送通知。
/** * Determine if the notification should be sent. */public function shouldSend(object $notifiable, string $channel): bool{ return $this->invoice->isPaid();}
按需通知
有时您可能需要向未存储为应用程序“用户”的人员发送通知。使用Notification
外观的route
方法,您可以在发送通知之前指定临时通知路由信息。
use Illuminate\Broadcasting\Channel;use Illuminate\Support\Facades\Notification; ->route('vonage', '5555555555') ->route('slack', '#slack-channel') ->route('broadcast', [new Channel('channel-name')]) ->notify(new InvoicePaid($invoice));
如果您想在向mail
路由发送按需通知时提供接收者的姓名,您可以提供一个数组,其中包含电子邮件地址作为键,并且名称作为数组中第一个元素的值。
Notification::route('mail', [])->notify(new InvoicePaid($invoice));
使用routes
方法,您可以一次为多个通知渠道提供临时路由信息。
Notification::routes([ 'vonage' => '5555555555',])->notify(new InvoicePaid($invoice));
邮件通知
格式化邮件信息
如果通知支持作为电子邮件发送,则应在通知类上定义toMail
方法。此方法将接收$notifiable
实体,并应返回Illuminate\Notifications\Messages\MailMessage
实例。
MailMessage
类包含一些简单的帮助您构建事务性电子邮件消息的方法。邮件消息可以包含文本行以及“行动号召”。让我们来看一个toMail
方法示例。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ $url = url('/invoice/'.$this->invoice->id); return (new MailMessage) ->greeting('Hello!') ->line('One of your invoices has been paid!') ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") ->action('View Invoice', $url) ->line('Thank you for using our application!');}
请注意,我们在toMail
方法中使用$this->invoice->id
。您可以将通知生成其消息所需的任何数据传递到通知的构造函数中。
在此示例中,我们注册了一个问候语、一行文本、一个行动号召,然后是另一行文本。MailMessage
对象提供的这些方法使格式化小型事务性电子邮件变得简单快捷。然后,邮件通道将消息组件转换为美观的响应式HTML电子邮件模板以及纯文本副本。这是一个由mail
通道生成的电子邮件示例。
发送邮件通知时,请确保在config/app.php
配置文件中设置name
配置选项。此值将用于邮件通知消息的页眉和页脚。
错误消息
某些通知会告知用户错误,例如发票支付失败。您可以在构建消息时调用error
方法来指示邮件消息是关于错误的。当在邮件消息上使用error
方法时,行动号召按钮将变为红色而不是黑色。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->error() ->subject('Invoice Payment Failed') ->line('...');}
其他邮件通知格式选项
您可以使用view
方法指定应用于呈现通知电子邮件的自定义模板,而不是在通知类中定义文本“行”。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage)->view( 'mail.invoice.paid', ['invoice' => $this->invoice] );}
您可以通过将视图名称作为传递给view
方法的数组的第二个元素来指定邮件消息的纯文本视图。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage)->view( ['mail.invoice.paid', 'mail.invoice.paid-text'], ['invoice' => $this->invoice] );}
或者,如果您的消息只有纯文本视图,您可以使用text
方法。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage)->text( 'mail.invoice.paid-text', ['invoice' => $this->invoice] );}
自定义发件人
默认情况下,电子邮件的发件人/发件人地址在config/mail.php
配置文件中定义。但是,您可以使用from
方法为特定通知指定发件人地址。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->line('...');}
自定义收件人
通过mail
通道发送通知时,通知系统会自动在您的可通知实体上查找email
属性。您可以通过在可通知实体上定义routeNotificationForMail
方法来自定义用于传递通知的电子邮件地址。
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable;use Illuminate\Notifications\Notification; class User extends Authenticatable{ use Notifiable; /** * Route notifications for the mail channel. * * @return array<string, string>|string */ public function routeNotificationForMail(Notification $notification): array|string { // Return email address only... return $this->email_address; // Return email address and name... return [$this->email_address => $this->name]; }}
自定义主题
默认情况下,电子邮件主题是格式化为“标题大小写”的通知的类名。因此,如果您的通知类名为InvoicePaid
,则电子邮件主题将为Invoice Paid
。如果您想为消息指定不同的主题,则可以在构建消息时调用subject
方法。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->subject('Notification Subject') ->line('...');}
自定义邮件发送器
默认情况下,电子邮件通知将使用config/mail.php
配置文件中定义的默认邮件发送程序发送。但是,您可以在运行时通过在构建消息时调用mailer
方法来指定不同的邮件发送程序。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->mailer('postmark') ->line('...');}
自定义模板
您可以通过发布通知包的资源来修改邮件通知使用的HTML和纯文本模板。运行此命令后,邮件通知模板将位于resources/views/vendor/notifications
目录中。
php artisan vendor:publish --tag=laravel-notifications
附件
要向电子邮件通知添加附件,请在构建消息时使用attach
方法。attach
方法接受文件的绝对路径作为其第一个参数。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->greeting('Hello!') ->attach('/path/to/file');}
将文件附加到消息时,您还可以通过将array
作为第二个参数传递给attach
方法来指定显示名称和/或MIME类型。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->greeting('Hello!') ->attach('/path/to/file', [ 'as' => 'name.pdf', 'mime' => 'application/pdf', ]);}
与在可邮件对象中附加文件不同,您不能使用attachFromStorage
直接从存储磁盘附加文件。您应该改用attach
方法,并使用存储磁盘上文件的绝对路径。或者,您可以从toMail
方法返回一个可邮件对象。
use App\Mail\InvoicePaid as InvoicePaidMailable; /** * Get the mail representation of the notification. */public function toMail(object $notifiable): Mailable{ return (new InvoicePaidMailable($this->invoice)) ->to($notifiable->email) ->attachFromStorage('/path/to/file');}
如有必要,可以使用attachMany
方法将多个文件附加到消息。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->greeting('Hello!') ->attachMany([ '/path/to/forge.svg', '/path/to/vapor.svg' => [ 'as' => 'Logo.svg', 'mime' => 'image/svg+xml', ], ]);}
原始数据附件
attachData
方法可用于将原始字节字符串作为附件附加。调用attachData
方法时,应提供应分配给附件的文件名。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->greeting('Hello!') ->attachData($this->pdf, 'name.pdf', [ 'mime' => 'application/pdf', ]);}
添加标签和元数据
某些第三方电子邮件提供商(例如Mailgun和Postmark)支持消息“标签”和“元数据”,可用于对应用程序发送的电子邮件进行分组和跟踪。您可以通过tag
和metadata
方法向电子邮件添加标签和元数据。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->greeting('Comment Upvoted!') ->tag('upvote') ->metadata('comment_id', $this->comment->id);}
如果您的应用程序使用Mailgun驱动程序,您可以查阅Mailgun的文档以了解更多关于标签和元数据的信息。同样,也可以查阅Postmark文档以了解更多关于他们对标签和元数据的支持信息。
如果您的应用程序使用Amazon SES发送电子邮件,则应使用metadata
方法将SES“标签”附加到消息。
自定义 Symfony 消息
MailMessage
类的withSymfonyMessage
方法允许您注册一个闭包,该闭包将在发送消息之前使用Symfony消息实例调用。这使您有机会在消息传递之前对其进行深入自定义。
use Symfony\Component\Mime\Email; /** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->withSymfonyMessage(function (Email $message) { $message->getHeaders()->addTextHeader( 'Custom-Header', 'Header Value' ); });}
使用 Mailables
如果需要,您可以从通知的toMail
方法返回完整的可邮件对象。当返回Mailable
而不是MailMessage
时,您需要使用可邮件对象的to
方法指定消息接收者。
use App\Mail\InvoicePaid as InvoicePaidMailable;use Illuminate\Mail\Mailable; /** * Get the mail representation of the notification. */public function toMail(object $notifiable): Mailable{ return (new InvoicePaidMailable($this->invoice)) ->to($notifiable->email);}
可邮件对象和按需通知
如果您正在发送按需通知,则传递给toMail
方法的$notifiable
实例将是Illuminate\Notifications\AnonymousNotifiable
的实例,它提供了一个routeNotificationFor
方法,可用于检索应将按需通知发送到的电子邮件地址。
use App\Mail\InvoicePaid as InvoicePaidMailable;use Illuminate\Notifications\AnonymousNotifiable;use Illuminate\Mail\Mailable; /** * Get the mail representation of the notification. */public function toMail(object $notifiable): Mailable{ $address = $notifiable instanceof AnonymousNotifiable ? $notifiable->routeNotificationFor('mail') : $notifiable->email; return (new InvoicePaidMailable($this->invoice)) ->to($address);}
预览邮件通知
在设计邮件通知模板时,方便地在浏览器中快速预览呈现的邮件消息,就像典型的Blade模板一样。为此,Laravel允许您直接从路由闭包或控制器返回邮件通知生成的任何邮件消息。当返回MailMessage
时,它将在浏览器中呈现和显示,允许您快速预览其设计,而无需将其发送到实际的电子邮件地址。
use App\Models\Invoice;use App\Notifications\InvoicePaid; Route::get('/notification', function () { $invoice = Invoice::find(1); return (new InvoicePaid($invoice)) ->toMail($invoice->user);});
Markdown 邮件通知
Markdown邮件通知允许您利用邮件通知的预构建模板,同时让您有更多自由来编写更长、更自定义的消息。由于消息是用Markdown编写的,Laravel能够为消息呈现美观的响应式HTML模板,同时也会自动生成纯文本副本。
生成消息
要生成具有相应Markdown模板的通知,您可以使用make:notification
Artisan命令的--markdown
选项。
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
与所有其他邮件通知一样,使用Markdown模板的通知应在其通知类上定义toMail
方法。但是,不要使用line
和action
方法来构建通知,而是使用markdown
方法指定应使用的Markdown模板的名称。您可以将希望提供给模板的数据数组作为方法的第二个参数传递。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ $url = url('/invoice/'.$this->invoice->id); return (new MailMessage) ->subject('Invoice Paid') ->markdown('mail.invoice.paid', ['url' => $url]);}
编写消息
Markdown邮件通知结合使用Blade组件和Markdown语法,使您可以轻松构建通知,同时利用Laravel预先制作的通知组件。
<x-mail::message># Invoice Paid Your invoice has been paid! <x-mail::button :url="$url">View Invoice</x-mail::button> Thanks,<br>{{ config('app.name') }}</x-mail::message>
按钮组件
按钮组件呈现居中的按钮链接。该组件接受两个参数,一个url
和一个可选的color
。支持的颜色为primary
、green
和red
。您可以根据需要向通知添加任意数量的按钮组件。
<x-mail::button :url="$url" color="green">View Invoice</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表示形式中。
如果您想为 Laravel 的 Markdown 组件构建一个全新的主题,您可以将 CSS 文件放置在html/themes
目录中。命名并保存 CSS 文件后,更新mail
配置文件中的theme
选项以匹配新主题的名称。
要自定义单个通知的主题,您可以在构建通知的邮件消息时调用theme
方法。theme
方法接受发送通知时应使用的主题名称。
/** * Get the mail representation of the notification. */public function toMail(object $notifiable): MailMessage{ return (new MailMessage) ->theme('invoice') ->subject('Invoice Paid') ->markdown('mail.invoice.paid', ['url' => $url]);}
数据库通知
先决条件
database
通知通道将通知信息存储在数据库表中。此表将包含诸如通知类型以及描述通知的 JSON 数据结构等信息。
您可以查询该表以在应用程序的用户界面中显示通知。但是,在此之前,您需要创建一个数据库表来保存您的通知。您可以使用make:notifications-table
命令生成一个包含正确表结构的迁移
php artisan make:notifications-table php artisan migrate
如果您的可通知模型正在使用UUID 或 ULID 主键,则应在通知表迁移中将morphs
方法替换为uuidMorphs
或ulidMorphs
。
格式化数据库通知
如果通知支持存储在数据库表中,则应在通知类上定义toDatabase
或toArray
方法。此方法将接收一个$notifiable
实体,并应返回一个简单的 PHP 数组。返回的数组将被编码为 JSON 并存储在您的notifications
表的data
列中。让我们来看一个toArray
方法的示例。
/** * Get the array representation of the notification. * * @return array<string, mixed> */public function toArray(object $notifiable): array{ return [ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, ];}
当通知存储在您的应用程序数据库中时,type
列将填充通知的类名。但是,您可以通过在通知类上定义databaseType
方法来自定义此行为。
/** * Get the notification's database type. * * @return string */public function databaseType(object $notifiable): string{ return 'invoice-paid';}
toDatabase
与 toArray
toArray
方法还被broadcast
通道用来确定要广播到您的 JavaScript 前端的数据。如果您希望为database
和broadcast
通道提供两种不同的数组表示,则应定义toDatabase
方法而不是toArray
方法。
访问通知
一旦通知存储在数据库中,您就需要一种方便的方法从可通知实体访问它们。Illuminate\Notifications\Notifiable
特性(包含在 Laravel 的默认App\Models\User
模型中)包含一个notifications
Eloquent 关系,它返回实体的通知。要获取通知,您可以像访问任何其他 Eloquent 关系一样访问此方法。默认情况下,通知将按created_at
时间戳排序,最新的通知位于集合的开头。
$user = App\Models\User::find(1); foreach ($user->notifications as $notification) { echo $notification->type;}
如果您只想检索“未读”通知,可以使用unreadNotifications
关系。同样,这些通知将按created_at
时间戳排序,最新的通知位于集合的开头。
$user = App\Models\User::find(1); foreach ($user->unreadNotifications as $notification) { echo $notification->type;}
要从您的 JavaScript 客户端访问您的通知,您应该为您的应用程序定义一个通知控制器,该控制器返回可通知实体(例如当前用户)的通知。然后,您可以从您的 JavaScript 客户端向该控制器的 URL 发出 HTTP 请求。
将通知标记为已读
通常,当用户查看通知时,您需要将其标记为“已读”。Illuminate\Notifications\Notifiable
特性提供了一个markAsRead
方法,该方法更新通知数据库记录中的read_at
列。
$user = App\Models\User::find(1); foreach ($user->unreadNotifications as $notification) { $notification->markAsRead();}
但是,您可以直接在通知集合上使用markAsRead
方法,而不是循环遍历每个通知。
$user->unreadNotifications->markAsRead();
您还可以使用批量更新查询将所有通知标记为已读,而无需从数据库中检索它们。
$user = App\Models\User::find(1); $user->unreadNotifications()->update(['read_at' => now()]);
您可以delete
通知以将其完全从表中删除。
$user->notifications()->delete();
广播通知
先决条件
在广播通知之前,您应该配置并熟悉 Laravel 的事件广播服务。事件广播提供了一种从您的 JavaScript 前端响应服务器端 Laravel 事件的方法。
格式化广播通知
broadcast
通道使用 Laravel 的事件广播服务广播通知,允许您的 JavaScript 前端实时捕获通知。如果通知支持广播,您可以在通知类上定义toBroadcast
方法。此方法将接收一个$notifiable
实体,并应返回一个BroadcastMessage
实例。如果不存在toBroadcast
方法,则将使用toArray
方法收集应广播的数据。返回的数据将被编码为 JSON 并广播到您的 JavaScript 前端。让我们来看一个toBroadcast
方法的示例。
use Illuminate\Notifications\Messages\BroadcastMessage; /** * Get the broadcastable representation of the notification. */public function toBroadcast(object $notifiable): BroadcastMessage{ return new BroadcastMessage([ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, ]);}
广播队列配置
所有广播通知都排队等待广播。如果您想配置用于排队广播操作的队列连接或队列名称,您可以使用BroadcastMessage
的onConnection
和onQueue
方法。
return (new BroadcastMessage($data)) ->onConnection('sqs') ->onQueue('broadcasts');
自定义通知类型
除了您指定的数据外,所有广播通知还包含一个type
字段,其中包含通知的全类名。如果您想自定义通知type
,您可以在通知类上定义broadcastType
方法。
/** * Get the type of the notification being broadcast. */public function broadcastType(): string{ return 'broadcast.message';}
侦听通知
通知将在使用{notifiable}.{id}
约定的私有频道上广播。因此,如果您向 ID 为1
的App\Models\User
实例发送通知,则通知将在App.Models.User.1
私有频道上广播。使用Laravel Echo时,您可以使用notification
方法轻松监听频道的通知。
Echo.private('App.Models.User.' + userId) .notification((notification) => { console.log(notification.type); });
自定义通知通道
如果您想自定义实体的广播通知广播到的通道,您可以在可通知实体上定义receivesBroadcastNotificationsOn
方法。
<?php namespace App\Models; use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable; class User extends Authenticatable{ use Notifiable; /** * The channels the user receives notification broadcasts on. */ public function receivesBroadcastNotificationsOn(): string { return 'users.'.$this->id; }}
短信通知
先决条件
在 Laravel 中发送短信通知由Vonage(以前称为 Nexmo)提供支持。在您可以通过 Vonage 发送通知之前,您需要安装laravel/vonage-notification-channel
和guzzlehttp/guzzle
包。
composer require laravel/vonage-notification-channel guzzlehttp/guzzle
该包包含一个配置文件。但是,您不需要将此配置文件导出到您自己的应用程序。您可以简单地使用VONAGE_KEY
和VONAGE_SECRET
环境变量来定义您的 Vonage 公钥和私钥。
定义密钥后,您应该设置一个VONAGE_SMS_FROM
环境变量,该变量定义默认情况下应从哪个电话号码发送短信。您可以在 Vonage 控制面板中生成此电话号码。
VONAGE_SMS_FROM=15556666666
格式化短信通知
如果通知支持作为短信发送,则应在通知类上定义toVonage
方法。此方法将接收一个$notifiable
实体,并应返回一个Illuminate\Notifications\Messages\VonageMessage
实例。
use Illuminate\Notifications\Messages\VonageMessage; /** * Get the Vonage / SMS representation of the notification. */public function toVonage(object $notifiable): VonageMessage{ return (new VonageMessage) ->content('Your SMS message content');}
Unicode 内容
如果您的短信包含 Unicode 字符,则在构造VonageMessage
实例时应调用unicode
方法。
use Illuminate\Notifications\Messages\VonageMessage; /** * Get the Vonage / SMS representation of the notification. */public function toVonage(object $notifiable): VonageMessage{ return (new VonageMessage) ->content('Your unicode message') ->unicode();}
自定义“发件人”号码
如果您想从与VONAGE_SMS_FROM
环境变量指定的电话号码不同的电话号码发送某些通知,则可以在VonageMessage
实例上调用from
方法。
use Illuminate\Notifications\Messages\VonageMessage; /** * Get the Vonage / SMS representation of the notification. */public function toVonage(object $notifiable): VonageMessage{ return (new VonageMessage) ->content('Your SMS message content') ->from('15554443333');}
添加客户端引用
如果您想跟踪每个用户、团队或客户的成本,您可以向通知添加“客户参考”。Vonage 将允许您使用此客户参考生成报告,以便您可以更好地了解特定客户的短信使用情况。客户参考可以是最多 40 个字符的任何字符串。
use Illuminate\Notifications\Messages\VonageMessage; /** * Get the Vonage / SMS representation of the notification. */public function toVonage(object $notifiable): VonageMessage{ return (new VonageMessage) ->clientReference((string) $notifiable->id) ->content('Your SMS message content');}
路由短信通知
要将 Vonage 通知路由到正确的电话号码,请在您的可通知实体上定义routeNotificationForVonage
方法。
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable;use Illuminate\Notifications\Notification; class User extends Authenticatable{ use Notifiable; /** * Route notifications for the Vonage channel. */ public function routeNotificationForVonage(Notification $notification): string { return $this->phone_number; }}
Slack 通知
先决条件
在发送 Slack 通知之前,您应该通过 Composer 安装 Slack 通知通道。
composer require laravel/slack-notification-channel
此外,您必须为您的 Slack 工作区创建一个Slack 应用程序。
如果您只需要向创建应用程序的同一 Slack 工作区发送通知,则应确保您的应用程序具有chat:write
、chat:write.public
和chat:write.customize
作用域。如果您想以 Slack 应用程序的身份发送消息,则应确保您的应用程序也具有chat:write:bot
作用域。这些作用域可以从 Slack 中的“OAuth 和权限”应用程序管理选项卡中添加。
接下来,复制应用程序的“Bot User OAuth Token”并将其放在应用程序的services.php
配置文件中的slack
配置数组中。此令牌可以在 Slack 的“OAuth 和权限”选项卡中找到。
'slack' => [ 'notifications' => [ 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), ],],
应用分发
如果您的应用程序将向应用程序用户拥有的外部 Slack 工作区发送通知,则需要通过 Slack “分发”您的应用程序。应用程序分发可以在 Slack 中的应用程序的“管理分发”选项卡中管理。一旦您的应用程序被分发,您可以使用Socialite代表您的应用程序用户获取 Slack Bot 令牌。
格式化 Slack 通知
如果通知支持作为 Slack 消息发送,则应在通知类上定义toSlack
方法。此方法将接收一个$notifiable
实体,并应返回一个Illuminate\Notifications\Slack\SlackMessage
实例。您可以使用Slack 的 Block Kit API构建丰富的通知。以下示例可以在Slack 的 Block Kit 生成器中预览。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. */public function toSlack(object $notifiable): SlackMessage{ return (new SlackMessage) ->text('One of your invoices has been paid!') ->headerBlock('Invoice Paid') ->contextBlock(function (ContextBlock $block) { $block->text('Customer #1234'); }) ->sectionBlock(function (SectionBlock $block) { $block->text('An invoice has been paid.'); $block->field("*Invoice No:*\n1000")->markdown(); }) ->dividerBlock() ->sectionBlock(function (SectionBlock $block) { $block->text('Congratulations!'); });}
使用 Slack 的 Block Kit 生成器模板
您可以提供 Slack 的 Block Kit 生成器生成的原始 JSON 负载到usingBlockKitTemplate
方法,而不是使用流畅的消息构建器方法来构建您的 Block Kit 消息。
use Illuminate\Notifications\Slack\SlackMessage;use Illuminate\Support\Str; /** * Get the Slack representation of the notification. */public function toSlack(object $notifiable): SlackMessage{ $template = <<<JSON { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "Team Announcement" } }, { "type": "section", "text": { "type": "plain_text", "text": "We are hiring!" } } ] } JSON; return (new SlackMessage) ->usingBlockKitTemplate($template);}
Slack 交互性
Slack 的 Block Kit 通知系统提供了强大的功能来处理用户交互。要利用这些功能,您的 Slack 应用程序应启用“交互”并配置指向您的应用程序提供的 URL 的“请求 URL”。这些设置可以在 Slack 中的“交互和快捷方式”应用程序管理选项卡中管理。
在下面的例子中,使用了actionsBlock
方法,Slack 将会向你的“请求 URL”发送一个POST
请求,请求体包含点击按钮的 Slack 用户、被点击按钮的 ID 等信息。然后,你的应用程序可以根据请求体来确定要采取的操作。你还应该验证请求是由 Slack 发出的。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. */public function toSlack(object $notifiable): SlackMessage{ return (new SlackMessage) ->text('One of your invoices has been paid!') ->headerBlock('Invoice Paid') ->contextBlock(function (ContextBlock $block) { $block->text('Customer #1234'); }) ->sectionBlock(function (SectionBlock $block) { $block->text('An invoice has been paid.'); }) ->actionsBlock(function (ActionsBlock $block) { // ID defaults to "button_acknowledge_invoice"... $block->button('Acknowledge Invoice')->primary(); // Manually configure the ID... $block->button('Deny')->danger()->id('deny_invoice'); });}
确认模态框
如果你希望用户在执行操作之前需要确认,你可以在定义按钮时调用confirm
方法。confirm
方法接收一个消息和一个闭包,闭包接收一个ConfirmObject
实例。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. */public function toSlack(object $notifiable): SlackMessage{ return (new SlackMessage) ->text('One of your invoices has been paid!') ->headerBlock('Invoice Paid') ->contextBlock(function (ContextBlock $block) { $block->text('Customer #1234'); }) ->sectionBlock(function (SectionBlock $block) { $block->text('An invoice has been paid.'); }) ->actionsBlock(function (ActionsBlock $block) { $block->button('Acknowledge Invoice') ->primary() ->confirm( 'Acknowledge the payment and send a thank you email?', function (ConfirmObject $dialog) { $dialog->confirm('Yes'); $dialog->deny('No'); } ); });}
检查 Slack Blocks
如果你想快速检查你构建的 blocks,你可以在SlackMessage
实例上调用dd
方法。dd
方法会生成并转储一个指向 Slack 的Block Kit Builder的 URL,该 URL 在你的浏览器中显示有效载荷和通知的预览。你可以向dd
方法传递true
来转储原始有效载荷。
return (new SlackMessage) ->text('One of your invoices has been paid!') ->headerBlock('Invoice Paid') ->dd();
路由 Slack 通知
要将 Slack 通知定向到相应的 Slack 团队和频道,请在你的可通知模型上定义一个routeNotificationForSlack
方法。此方法可以返回三个值之一:
-
null
- 将路由延迟到通知本身中配置的频道。你可以在构建SlackMessage
时使用to
方法来配置通知中的频道。 - 一个指定要发送通知的 Slack 频道的字符串,例如
#support-channel
。 - 一个
SlackRoute
实例,允许你指定 OAuth 令牌和频道名称,例如SlackRoute::make($this->slack_channel, $this->slack_token)
。此方法应该用于向外部工作区发送通知。
例如,从routeNotificationForSlack
方法返回#support-channel
将把通知发送到与位于你的应用程序services.php
配置文件中的 Bot 用户 OAuth 令牌关联的工作区中的#support-channel
频道。
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable;use Illuminate\Notifications\Notification; class User extends Authenticatable{ use Notifiable; /** * Route notifications for the Slack channel. */ public function routeNotificationForSlack(Notification $notification): mixed { return '#support-channel'; }}
通知外部 Slack 工作区
在向外部 Slack 工作区发送通知之前,你的 Slack 应用必须已分发。
当然,你通常希望将通知发送到你的应用程序用户拥有的 Slack 工作区。为此,你首先需要获取用户的 Slack OAuth 令牌。值得庆幸的是,Laravel Socialite包含一个 Slack 驱动程序,它允许你轻松地使用 Slack 认证你的应用程序用户并获取 bot 令牌。
一旦你获得了 bot 令牌并将其存储在你的应用程序数据库中,你就可以使用SlackRoute::make
方法将通知路由到用户的 workspace。此外,你的应用程序可能需要为用户提供一个指定应将通知发送到哪个频道的机会。
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable;use Illuminate\Notifications\Notifiable;use Illuminate\Notifications\Notification;use Illuminate\Notifications\Slack\SlackRoute; class User extends Authenticatable{ use Notifiable; /** * Route notifications for the Slack channel. */ public function routeNotificationForSlack(Notification $notification): mixed { return SlackRoute::make($this->slack_channel, $this->slack_token); }}
本地化通知
Laravel 允许你以与 HTTP 请求当前区域设置不同的区域设置发送通知,如果通知排队,它甚至会记住此区域设置。
为此,Illuminate\Notifications\Notification
类提供了一个locale
方法来设置所需的语言。当评估通知时,应用程序将更改为此区域设置,然后在评估完成后恢复到之前的区域设置。
$user->notify((new InvoicePaid($invoice))->locale('es'));
也可以通过Notification
facade 来实现多个可通知条目的本地化。
Notification::locale('es')->send( $users, new InvoicePaid($invoice));
用户首选区域设置
有时,应用程序会存储每个用户的首选区域设置。通过在你的可通知模型上实现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
方法。
$user->notify(new InvoicePaid($invoice));
测试
你可以使用Notification
facade 的fake
方法来防止发送通知。通常情况下,发送通知与你实际测试的代码无关。大多数情况下,只需断言 Laravel 已被指示发送给定的通知就足够了。
调用Notification
facade 的fake
方法后,你可以断言已指示将通知发送给用户,甚至可以检查通知收到的数据。
<?php use App\Notifications\OrderShipped;use Illuminate\Support\Facades\Notification; test('orders can be shipped', function () { Notification::fake(); // Perform order shipping... // Assert that no notifications were sent... Notification::assertNothingSent(); // Assert a notification was sent to the given users... Notification::assertSentTo( [$user], OrderShipped::class ); // Assert a notification was not sent... Notification::assertNotSentTo( [$user], AnotherNotification::class ); // Assert that a given number of notifications were sent... Notification::assertCount(3);});
<?php namespace Tests\Feature; use App\Notifications\OrderShipped;use Illuminate\Support\Facades\Notification;use Tests\TestCase; class ExampleTest extends TestCase{ public function test_orders_can_be_shipped(): void { Notification::fake(); // Perform order shipping... // Assert that no notifications were sent... Notification::assertNothingSent(); // Assert a notification was sent to the given users... Notification::assertSentTo( [$user], OrderShipped::class ); // Assert a notification was not sent... Notification::assertNotSentTo( [$user], AnotherNotification::class ); // Assert that a given number of notifications were sent... Notification::assertCount(3); }}
你可以向assertSentTo
或assertNotSentTo
方法传递一个闭包,以便断言已发送通过给定“真值测试”的通知。如果至少发送了一个通过给定真值测试的通知,则断言将成功。
Notification::assertSentTo( $user, function (OrderShipped $notification, array $channels) use ($order) { return $notification->order->id === $order->id; });
按需通知
如果你的测试代码发送按需通知,你可以通过assertSentOnDemand
方法测试是否发送了按需通知。
Notification::assertSentOnDemand(OrderShipped::class);
通过向assertSentOnDemand
方法的第二个参数传递一个闭包,你可以确定是否将按需通知发送到正确的“路由”地址。
Notification::assertSentOnDemand( OrderShipped::class, function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { return $notifiable->routes['mail'] === $user->email; });
通知事件
通知发送事件
当发送通知时,通知系统会调度Illuminate\Notifications\Events\NotificationSending
事件。这包含“可通知”实体和通知实例本身。你可以在你的应用程序中为该事件创建事件监听器。
use Illuminate\Notifications\Events\NotificationSending; class CheckNotificationStatus{ /** * Handle the given event. */ public function handle(NotificationSending $event): void { // ... }}
如果NotificationSending
事件的事件监听器从其handle
方法返回false
,则不会发送通知。
/** * Handle the given event. */public function handle(NotificationSending $event): bool{ return false;}
在事件监听器中,你可以访问事件上的notifiable
、notification
和channel
属性,以了解更多关于通知接收者或通知本身的信息。
/** * Handle the given event. */public function handle(NotificationSending $event): void{ // $event->channel // $event->notifiable // $event->notification}
通知已发送事件
当发送通知时,通知系统会调度Illuminate\Notifications\Events\NotificationSent
事件。这包含“可通知”实体和通知实例本身。你可以在你的应用程序中为该事件创建事件监听器。
use Illuminate\Notifications\Events\NotificationSent; class LogNotification{ /** * Handle the given event. */ public function handle(NotificationSent $event): void { // ... }}
在事件监听器中,你可以访问事件上的notifiable
、notification
、channel
和response
属性,以了解更多关于通知接收者或通知本身的信息。
/** * Handle the given event. */public function handle(NotificationSent $event): void{ // $event->channel // $event->notifiable // $event->notification // $event->response}
自定义通道
Laravel 附带了一些通知通道,但是你可能需要编写自己的驱动程序才能通过其他通道传递通知。Laravel 使这变得很简单。首先,定义一个包含send
方法的类。该方法应该接收两个参数:一个$notifiable
和一个$notification
。
在send
方法中,你可以调用通知上的方法来检索你的通道理解的消息对象,然后根据你的意愿将通知发送到$notifiable
实例。
<?php namespace App\Notifications; use Illuminate\Notifications\Notification; class VoiceChannel{ /** * Send the given notification. */ public function send(object $notifiable, Notification $notification): void { $message = $notification->toVoice($notifiable); // Send notification to the $notifiable instance... }}
定义通知通道类后,你可以从任何通知的via
方法返回类名。在这个例子中,你的通知的toVoice
方法可以返回你选择的任何对象来表示语音消息。例如,你可能定义你自己的VoiceMessage
类来表示这些消息。
<?php namespace App\Notifications; use App\Notifications\Messages\VoiceMessage;use App\Notifications\VoiceChannel;use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Notifications\Notification; class InvoicePaid extends Notification{ use Queueable; /** * Get the notification channels. */ public function via(object $notifiable): string { return VoiceChannel::class; } /** * Get the voice representation of the notification. */ public function toVoice(object $notifiable): VoiceMessage { // ... }}