消息通知
消息通知
消息通知
简介
除了支持 发送邮件之外,Laravel 还支持通过多种频道发送通知,包括邮件、短信 (通过Nexmo),以及Slack。通知还能存储到数据库以便后续在 Web 页面中显示。
通常,通知都是简短、有信息量的消息,用于通知用户应用中发生了什么。举例来说,如果你是在编写一个在线交易的应用,你应该会通过邮件和短信频道类给用户发送一条 「账单支付」 的通知。
创建通知
Laravel 中一条通知就是一个类 (通常存放在app/Notifications
文件夹下)。看不到的话不要担心,运行下make:notification
命令就能创建了:
php artisan make:notification InvoicePaid
这条命令会在app/Notifications
目录下生成一个新的通知类。这个类包含via
方法以及一个或多个消息构建的方法 (比如toMail
或者toDatabase
) ,它们会针对指定的渠道把通知转换为对应的消息。
发送通知
使用 Notifiable Trait
通知可以通过两种方法发送:Notifiable
trait 的notify
方法或Notification
facade。首先让我们来探讨下使用 trait:
默认的
App\User
模型中使用了这个 trait,它包含着一个可以用来发送通知的方法:notify
。notify
方法需要一个通知实例做参数:use App\Notifications\InvoicePaid; $user->notify(new InvoicePaid($invoice));{tip} 记住,你可以在任意模型中使用
Illuminate\Notifications\Notifiable
trait,而不仅仅是在User
模型中。使用 Notification Facade
另外,你可以通过
Notification
facade 来发送通知。它主要用在当你给多个可接收通知的实体发送的时候,比如给用户集合发送通知。使用 Facade 发送通知的话,要把可以接收通知和通知的实例传递给send
方法:Notification::send($users, new InvoicePaid($invoice));发送指定频道
每个通知类都会有个
via
方法,它决定了通知会在哪个频道上发送。开箱即用的频道有database
,broadcast
,nexmo
和slack
。{tip} 如果你想使用其他的频道,比如 Telegram 或者 Pusher,你可以去看下社区驱动的 Laravel 通知频道网站。
via
方法接收一个$notifiable
实例。这个实例将是通知实际发送到的类的实例。你可以用$notifiable
来决定通知用哪些频道来发送:/** * 获取通知发送频道。 * * @param mixed $notifiable * @return array */ public function via($notifiable){ return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database']; }通知队列
{注:} 使用通知队列前需要配置队列并 开启一个队列任务。
发送通知可能是耗时的,尤其是通道需要调用额外的 API 来传输通知。为了加速应用的响应时间,可以将通知推送到队列中异步发送,而要实现推送通知到队列,可以让对应通知类实现
ShouldQueue
接口并使用Queueable
trait 。 如果通知类是通过make:notification
命令生成的,那么该接口和 trait 已经默认导入,你可以快速将它们添加到通知类:
ShouldQueue
接口被添加到通知类以后,你可以像之前一样正常发送通知,Laravel 会自动检测到ShouldQueue
接口然后将通知推送到队列:$user->notify(new InvoicePaid($invoice));如果你想要延迟通知的发送,可以在通知实例后加上
delay
方法:$when = now()->addMinutes(10); $user->notify((new InvoicePaid($invoice))->delay($when);按需通知
有时候你可能需要发送通知给某个用户,但是该用户不存在于应用的用户系统中,要实现这一目的,我们使用
Notification::route
方法在发送通知之前指定特别的通知路由:Notification::route('mail', 'taylor@example.com') ->route('nexmo', '5555555555') ->notify(new InvoicePaid($invoice));邮件通知
格式化邮件消息
如果通知支持以邮件方式发送,你需要在通知类上定义一个
toMail
方法。该方法会接收一个$notifiable
实体并返回Illuminate\Notifications\Messages\MailMessage
实例。邮件消息可以包含多行文本以及对动作的调用,让我们来看一个toMail
方法的示例:/** * 获取通知对应的邮件。 * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable){ $url = url('/invoice/'.$this->invoice->id); return (new MailMessage) ->greeting('Hello!') ->line('One of your invoices has been paid!') ->action('View Invoice', $url) ->line('Thank you for using our application!'); }{注:} 注意到我们在
toMail
方法中使用了$this->invoice->id
,你可以传递任何通知生成消息所需要的数据到通知的构造器。在这个例子中,我们注册了一条问候、一行文本、对动作的调用以及另一行文本。
MailMessage
对象提供的这些方法让格式化短小的事务邮件变得简单快捷。
{注:} 发送邮件通知时,确保在配置文件
config/app.php
中设置了name
的值,该值将会用在邮件通知消息的头部和尾部。其他通知格式化选项
除了在通知类中定义多行文本之外,你还可以使用
view
方法来指定一个自定义的、用于渲染通知邮件的模板:/** * 获取通知邮件。 * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable){ return (new MailMessage)->view( 'emails.name', ['invoice' => $this->invoice] ); }此外,你可以从
toMail
方法中返回一个 可邮寄对象 :use App\Mail\InvoicePaid as Mailable; /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return Mailable */ public function toMail($notifiable){ return (new Mailable($this->invoice))->to($this->user->email); }错误消息
一些通知会告知用户错误信息,例如失败的订单支付。你可以在构建消息的时候调用
error
方法来指示该邮件消息表示错误信息。在邮件消息中使用error
方法时,动作按钮将会变成红色:/** * 获取通知邮件。 * * @param mixed $notifiable * @return \Illuminate\Notifications\Message */ public function toMail($notifiable){ return (new MailMessage) ->error() ->subject('Notification Subject') ->line('...'); }自定义接收人
通过
routeNotificationForMail
来自定义使用哪个邮箱地址发送通知:email_address; } }自定义主题
默认情况下,邮件的主题就是格式为 「标题风格」 的通知类名,因此,如果通知类被命名为
InvoicePaid
,邮件的主题就是Invoice Paid
,如果你想要为消息指定明确的主题,可以在构建消息的时候调用subject
方法:/** * 获取通知的邮件表示。 * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable){ return (new MailMessage) ->subject('Notification Subject') ->line('...'); }自定义模板
你可以通过发布通知扩展包的资源来修改邮件通知所使用的 HTML 和纯文本模板。运行完下面这个命令之后,邮件通知模板将会存放到
resources/views/vendor/notifications
目录:php artisan vendor:publish --tag=laravel-notificationsMarkdown 邮件通知
Markdown 邮件通知允许你利用邮件通知的预置模板,从而让你可以自由编写更长、更具个性化的消息。因为这些消息以 Markdown 格式编写,Laravel 还可以为它们渲染出高颜值、响应式的 HTML 模板,同时自动生成纯文本的副本。
生成消息
要生成带有相应 Markdown 模板的通知,可以在使用 Artisan 命令
make:notification
时带上--markdown
选项:php artisan make:notification InvoicePaid --markdown=mail.invoice.paid和其他邮件通知一样,使用 Markdown 模板的通知类也要定义一个
toMail
方法。不过,你可以使用markdown
方法取代构造通知的line
和action
方法来指定要使用的 Markdown 模板名称:/** * 获取通知的邮件表示。 * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable){ $url = url('/invoice/'.$this->invoice->id); return (new MailMessage) ->subject('Invoice Paid') ->markdown('mail.invoice.paid', ['url' => $url]); }编写消息
Markdown 邮件通知联合使用了 Blade 组件和 Markdown 语法,从而让你在不脱离 Laravel 预置组件的情况下轻松构建通知:
@component('mail::message') # Invoice PaidYour invoice has been paid! @component('mail::button', ['url' => $url]) View Invoice @endcomponent Thanks,
{{ config('app.name') }} @endcomponent按钮组件
按钮组件渲染一个居中的按钮链接。该组件接收两个参数,
url
和可选的color
,支持的颜色有blue
,green
和red
。你可以添加任意数量的按钮组件到消息中:@component('mail::button', ['url' => $url, 'color' => 'green']) View Invoice @endcomponent面板组件
面板组件将给定的文字区块渲染到一个面板中,并且有一个淡淡的背景色与周围的消息区分开。适用于需要引起注意的文字区块:
@component('mail::panel') This is the panel content. @endcomponent表格组件
表格组件允许你将一个 Markdown 表格转化为 HTML 表格。该组件接收 Markdown 表格作为其内容。表格列对齐支持使用默认的 Markdown 表格列对齐语法:
@component('mail::table') | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | @endcomponent自定义组件
你可以导出所有 Markdown 通知组件到应用中进行自定义,要导出组件,使用 Artisan 命令
vendor:publish
来发布laravel-mail
资源标签:php artisan vendor:publish --tag=laravel-mail该命令会发布 Markdown 邮件通知组件到
resources/views/vendor/mail
目录。html
和markdown
目录,每个子目录中又包含各自的所有有效组件。你可以按照自己的喜好自由编辑这些组件。自定义 CSS
导出组件之后,
resources/views/vendor/mail/html/themes
目录将会包含一个默认的default.css
文件,你可以在这个文件中自定义 CSS,这样 Markdown 通知的 HTML 样式就会自动调整。{注:} 如果你想要为 Markdown 组件构建全新的主题,只需在
html/themes
目录中编写一个新的 CSS 文件并修改theme
选项即可。数据库通知
预备知识
database
通知通道会在数据表中存储通知信息,该表包含诸如通知类型以及用于描述通知的自定义 JSON 数据之类的信息。你可以在用户界面中查询这个数据表来展示通知,不过,在此之前,需要创建数据表来保存信息,你可以使用
notifications:table
命令来生成迁移文件然后以此生成相应数据表:php artisan notifications:table php artisan migrate格式化数据库通知
如果通知支持存入数据库表,就为通知类需要定义
toDatabase
或toArray
方法。此方法接受$notifiable
实体作参数并返回原生 PHP 数组。返回的数据将被编码为 JSON 并存储到notifications
表的data
列。来看一个toArray
方法示例:/** * 获取通知的数组表示。 * * @param mixed $notifiable * @return array */ public function toArray($notifiable){ return [ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, ]; }
toDatabase
Vs.toArray
toArray
方法还可以使用broadcast
通道来判断哪些数据被广播到 JavaScript 客户端。如果针对database
和broadcast
通道分别有两个不同的数组表示,你需要定义toDatabase
方法代替toArray
方法。访问通知
一旦通知存入数据库,就需要适当的方法自通知实体访问它们。 包含在 Lareval 的默认
App\User
模型带有Illuminate\Notifications\Notifiable
trait,它的notifications
Eloquent 关联方法能返回实体通知。要获取通知,可以像其它 Eloquent 关联方法一样访问此方法。默认情况下,通知按照created_at
时间戳排序:$user = App\User::find(1); foreach ($user->notifications as $notification) { echo $notification->type; }若要只获取 「未读」通知,可以使用
unreadNotifications
关联方法。同样这些通知按照created_at
时间戳排序:$user = App\User::find(1); foreach ($user->unreadNotifications as $notification) { echo $notification->type; }{tip} 若要从 JavaScript 客户端访问通知,需要为应用定义一个通知控制器,它返回可通知实体的通知,比如当前用户。可以从 JavaScript 客户端向该控制器 URI 发送 HTTP 请求。
标记通知已读
通常,在用户阅览一条通知之后,你会想将其标识为「已读」。
Illuminate\Notifications\Notifiable
trait 提供了markAsRead
方法,它更新数据库中通知记录的read_at
列:$user = App\User::find(1); foreach ($user->unreadNotifications as $notification) { $notification->markAsRead(); }可以在通知控制集合上直接使用
markAsRead
方法代替循环调用通知:$user->unreadNotifications->markAsRead();还可以使用指更新将所有的通知标为已读,而不从数据库中读取它们:
$user = App\User::find(1); $user->unreadNotifications()->update(['read_at' => now()]);可以使用
delete
方法从表中整个删除通知:$user->notifications()->delete();广播通知
预备知识
广播通知前,你需要配置和熟悉 Laravel 的 事件广播 服务。事件广播提供了在 JavaScript 客户端响应服务端触发 Laravel 事件的方法。
格式化广播通知
broadcast
通道使用 Laravel 的 event broadcasting 服务广播通知,它允许 JavaScript 客户端实时捕获通知。如果通知支持广播,你就需要在通知类上定义toBroadcast
方法。此方法接受$notifiable
实体作为参数,并返回BroadcastMessage
实例。返回的数据将被编码为 JSON 并广播给 JavaScript 客户端。我们来看一个toBroadcast
方法示例use Illuminate\Notifications\Messages\BroadcastMessage;/** * 获取通知的可广播表示。 * * @param mixed $notifiable * @return BroadcastMessage */ public function toBroadcast($notifiable){ return new BroadcastMessage([ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, ]); }广播队列配置
所有的广播通知都被放入广播队列。想要配置用于广播操作的队列连接或者队列名称,需要使用
BroadcastMessage
的onConnection
和onQueue
方法:return (new BroadcastMessage($data)) ->onConnection('sqs') ->onQueue('broadcasts');{tip} 除了指定的数据,广播通知还包含
type
域,它包括通知类的类名。监听通知
通知将会以格式化为
{notifiable}.{id}
的形式在私有频道上广播,因此,如果你要发送通知到 ID 为1
的App\User
实例,那么该通知将会在私有频道App.User.1
上进行广播,如果使用了 Laravel Echo,可以使用辅助函数notification
轻松在某个频道上监听通知:Echo.private('App.User.' + userId) .notification((notification) => { console.log(notification.type); });自定义通知通道
如果你想要自定义被通知实体在某个通道上接收广播通知,可以在被通知实体上定义一个
receivesBroadcastNotificationsOn
方法:id; } }短信(SMS)通知
预备知识
Laravel 基于Nexmo发送短信通知,在使用 Nexmo 发送通知前,需要安装对应 Composer 依赖包
laravel/nexmo-notification-channel
:composer require laravel/nexmo-notification-channel下一步,你需要在配置文件
config/services.php
中进行相应配置。你可以参考以下示例配置:'nexmo' => [ 'key' => env('NEXMO_KEY'), 'secret' => env('NEXMO_SECRET'), 'sms_from' => '15556666666', ],
sms_from
配置项就是你用于发送短信消息的手机号码,你需要在 Nexmo 控制面板中为应用生成一个手机号码。格式化短信通知
如果通知支持以短信方式发送,那么你需要在通知类上定义一个
toNexmo
方法。该方法接收一个$notifiable
实体并返回Illuminate\Notifications\Messages\NexmoMessage
实例:/** * Get the Nexmo / SMS representation of the notification. * * @param mixed $notifiable * @return NexmoMessage */ public function toNexmo($notifiable){ return (new NexmoMessage) ->content('Your SMS message content'); }Unicode 内容
如果你的短信消息包含 Unicode 字符,需要在构造
NexmoMessage
实例时调用unicode
方法:/** * Get the Nexmo / SMS representation of the notification. * * @param mixed $notifiable * @return NexmoMessage */ public function toNexmo($notifiable){ return (new NexmoMessage) ->content('Your unicode message') ->unicode(); }自定义 "发送" 号码
如果你要通过与配置文件
config/services.php
中指定的手机号不同的其他号码发送通知,可以使用NexmoMessage
实例上的from
方法:/** * Get the Nexmo / SMS representation of the notification. * * @param mixed $notifiable * @return NexmoMessage */ public function toNexmo($notifiable){ return (new NexmoMessage) ->content('Your SMS message content') ->from('15554443333'); }短信通知路由
使用
nexmo
通道发送通知的时候,通知系统会自动在被通知实体上查找phone_number
属性。如果你想要自定义通知被发送到的手机号码,可以在该实体上定义一个routeNotificationForNexmo
方法:phone; } }Slack 通知
预备知识
在通过 Slack 发送通知前,必须通过 Composer 安装 Slack 通知通道:
composer require laravel/slack-notification-channel此外,你还要为 Slack 组配置一个"Incoming Webhook"集成。该集成会在你进行Slack 通知路由的时候提供一个 URL。
格式化 Slack 通知
如果通知支持通过 Slack 消息发送,则需要在通知类上定义一个
toSlack
方法,该方法接收一个$notifiable
实体并返回Illuminate\Notifications\Messages\SlackMessage
实例,该实例包含文本内容以及格式化额外文本或数组字段的 “附件”。让我们来看一个基本的toSlack
使用示例:/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ return (new SlackMessage) ->content('One of your invoices has been paid!'); }在这个例子中,我们只发送一行简单的文本到 Slack,最终创建的消息如下:
Customizing The Sender & Recipient
你可以使用
from
和to
方法自定义发送者和接收者,from
方法接收一个用户名和 emoji 标识,而to
方法接收通道或用户名:/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ return (new SlackMessage) ->from('Ghost', ':ghost:') ->to('#other') ->content('This will be sent to #other'); }还可以使用图片作为 logo 用以取代 emoji:
/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ return (new SlackMessage) ->from('Laravel') ->image('https://laravel.com/favicon.png') ->content('This will display the Laravel logo next to the message'); }Slack 附件
你还可以添加 “附件” 到 Slack 消息。相对简单文本消息,附件可以提供更加丰富的格式选择。在这个例子中,我们会发送一个在应用程序中出现的异常错误通知,包含查看更多异常细节的链接:
/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ $url = url('/exceptions/'.$this->exception->id); return (new SlackMessage) ->error() ->content('Whoops! Something went wrong.') ->attachment(function ($attachment) use ($url) { $attachment->title('Exception: File Not Found', $url) ->content('File [background.jpg] was not found.'); }); }上述代码会生成如下 Slack 消息:
附件还允许你指定要呈献给用户的数组数据。为了提高可读性,给定的数组会以表格形式展示:
/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ $url = url('/invoices/'.$this->invoice->id); return (new SlackMessage) ->success() ->content('One of your invoices has been paid!') ->attachment(function ($attachment) use ($url) { $attachment->title('Invoice 1322', $url) ->fields([ 'Title' => 'Server Expenses', 'Amount' => '$1,234', 'Via' => 'American Express', 'Was Overdue' => ':-1:', ]); }); }上述代码会生成如下 Slack 消息:
Markdown 附件内容
如果一些附件字段包含 Markdown,可以使用
markdown
方法来构建 Slack 用以解析并显示以 Markdown 格式编写的附件字段,该方法支持的值包括pretext
、text
或fields
。想要了解更多关于 Slack 格式化的信息,查看Slack API 文档:/** * Get the Slack representation of the notification. * * @param mixed $notifiable * @return SlackMessage */ public function toSlack($notifiable){ $url = url('/exceptions/'.$this->exception->id); return (new SlackMessage) ->error() ->content('Whoops! Something went wrong.') ->attachment(function ($attachment) use ($url) { $attachment->title('Exception: File Not Found', $url) ->content('File [background.jpg] was *not found*.') ->markdown(['text']); }); }Slack 通知路由
要路由 Slack 通知到适当的位置,需要在被通知的实体上定义一个
routeNotificationForSlack
方法,这将会返回通知被发送到的 Webhook URL。Webhook URL 可通过在 Slack 组上添加一个 "Incoming Webhook" 服务来生成:本地化通知
Laravel 允许您以当前语言环境之外的其他语言发送通知,并且会在通知队列时记住该语言环境。
要实现这一点,
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. * * @return string */ public function preferredLocale() { return $this->locale; } }实现接口后,Laravel 将在向模型发送通知和邮件时自动使用首选区域设置。因此,使用此接口时不需要调用
locale
方法:$user->notify(new InvoicePaid($invoice));通知事件
当通知被发送后,通知系统会触发
Illuminate\Notifications\Events\NotificationSent
事件,该事件实例包含被通知的实体(如用户)和通知实例本身。你可以在EventServiceProvider
中为该事件注册监听器:/** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'Illuminate\Notifications\Events\NotificationSent' => [ 'App\Listeners\LogNotification', ], ];{提示} 在
EventServiceProvider
中注册监听器之后,使用 Artisan 命令event:generate
可以快速生成监听器类。在事件监听器中,可以访问事件的
notifiable
、notification
和channel
属性了解通知接收者和通知本身的更多信息:/** * Handle the event. * * @param NotificationSent $event * @return void */ public function handle(NotificationSent $event){ // $event->channel // $event->notifiable // $event->notification // $event->response }自定义通道
Laravel 为我们提供了多种通知通道,但是尝试编写自定义通道驱动以通过其他通道发送通知,也很简单。首先定义一个包含
send
方法的类,该方法接收两个参数:$notifiable
和$notification
:toVoice($notifiable); // Send notification to the $notifiable instance... } }一旦通知通道类被定义后,就可以在应用中通过
via
方法返回类名: