1、简介
除了支持发送邮件
之外,Laravel
还支持通过多种传输通道发送通知
,这些通道包括邮件
、短信(通过Nexmo
)以及等Slack
等。通知可以存储在数据库
以便后续在web界面中显示。
通常,通知都是很短的、用于告知用户应用中所发生事件
的消息。例如,如果你在开发一个计费应用,则需要通过邮件或短信等渠道给用户发送“账单支付”通知。
2、创建通知
在Laravel中,每个通知都以单独类的形式存在(通常存放在app/
Notifications
目录),如果在应用中没看到这个目录,别担心,它将会在你运行Artisan命令make:notification
的时候自动创建:
php artisan make:notification InvoicePaid
该命令会在app/Notifications
目录下生成一个新的通知类,每个通知类都包含一个via方法以及多个消息构建方法(如toMail
或toDatabase
),这些消息构建方法用于将通知转化成为特定渠道优化的消息。
3、发送通知
使用Notifiable Trait
通知可以通过两种方式发送:使用Notifiable
trait提供的notify
方法或者使用Notification
门面
。首先,我们来检验Notifiable
trait。该trait被默认的App\User
模型使用并提供一个可用于发送通知的方法:notify
。notify
方法接收一个通知实例:
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
注:记住,你可以在任何模型中使用Illuminate\Notifications\Notifiable
trait,不限于只在User
模型中使用。
使用Notification门面
作为替代方案,还可以通过Notification
门面发送通知。这主要在你需要发送通知给多个用户的时候很有用,要使用这个门面发送通知,需要将所有被通知用户和通知实例传递给send
方法:
Notification::send($users, new InvoicePaid($invoice));
指定传输通道
每个通知类都有一个via
方法用于决定通知通过何种通道传输,Laravel开箱支持mail
、database
、broadcast
、nexmo
以及slack
通道。
注:如果你想要使用其他传输通道,比如Telegram或Pusher,参考社区提供的驱动:Laravel通知通道网站。
via
方法接收一个$notifiable
实例,用于指定通知被发送到的类实例。你可以使用$notifiable来判断通知通过何种通道传输:
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable){
return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database'];
}
通知队列
注:使用通知队列前需要配置队列并开启一个队列任务。
发送同时可能是耗时的,尤其是通道需要调用额外的API来传输通知。为了加速应用的响应时间,可以让通知类实现ShouldQueue接口并使用Queueable
trait。如果通知类是通过make:notification命令生成的,那么该接口和trait已经默认导入,你可以快速将它们添加到通知类:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
// ...
}
ShouldQueue
接口被添加到通知类以后,你可以像之前一样正常发送通知,Laravel会自动检测到ShouldQueue
接口然后将通知传输推送到队列:
$user->notify(new InvoicePaid($invoice));
如果你想要延迟通知的传输,可以在加上delay
方法:
$when = Carbon::now()->addMinutes(10);
$user->notify((new InvoicePaid($invoice))->delay($when));
4、邮件通知
格式化邮件消息
如果通知支持以邮件方式发送,你需要在通知类上定义一个toMail
方法。该方法会接收一个$notifiable
实体并返回Illuminate\Notifications\Messages\MailMessage
实例。邮件消息可以包含多行文本以及对动作的调用,让我们来看一个toMail
方法的示例:
/**
* Get the mail representation of the notification.
*
* @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!');
}
注:注意到我们在
message
方法中使用了$this->invoice->id
,你可以传递任何通知生成消息所需要的数据到通知的构造器。
在这个例子中,我们注册了一条问候、一行文本、对动作的调用以及另一行文本。MailMessage
对象提供的这些方法让格式化短小的交易邮件变得简单快捷。mail
通道会将消息组件转化为漂亮的、响应式的、带有纯文本副本的HTML邮件模板。下面是一个通过mail
通道生成的邮件示例:
注:发送邮件通知时,确保在配置文件
config/app.php
中设置了name
的值,该值将会用在邮件通知消息的头部和尾部。
自定义收件人
通过mail通道发送通知时,通知系统会自动在被通知实体上查找email属性,你可以通过在该实体上定义一个routeNotificationForMail自定义使用哪个邮箱地址发送通知:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable{
use Notifiable;
/**
* Route notifications for the mail channel.
*
* @return string
*/
public function routeNotificationForMail() {
return $this->email_address;
}
}
自定义主题
默认情况下,邮件的主题就是格式为“标题化”的通知类名,因此,如果通知类被命名为InvoicePaid
,邮件的主题就是Invoice Paid
,如果你想要为消息指定明确的主题,可以在构建消息的时候调用subject
方法:
/**
* Get the mail representation of the notification.
*
* @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-notifications
错误消息
一些通知会告知用户错误信息,例如一次失败的单据支付,你可以在构建消息的时候调用error方法标识邮件消息是一个错误消息。当在一个邮件消息上使用error
方法时,动作按钮的颜色将会由蓝色变成红色:
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Message
*/
public function toMail($notifiable){
return (new MailMessage)
->error()
->subject('Notification Subject')
->line('...');
}
5、数据库通知
预备知识
database
通知通道会在数据表中存储通知信息,该表包含诸如通知类型以及用于描述通知的自定义JSON数据之类的信息。
你可以在用户界面中查询这个数据表来展示通知,不过,在此之前,需要创建数据表来保存信息,你可以使用notifications:table
命令来生成迁移然后生成数据表:
php artisan notifications:table
php artisan migrate
格式化数据库通知
如果一个通知支持存放在数据表,则需要在通知类中定义toDatabase
或toArray
方法,该方法接收一个$notifiable
实体并返回原生的PHP数组。返回的数组会被编码为JSON格式然后存放到notifications
表的data
字段。让我们来看一个toArray
方法的例子:
/**
* Get the array representation of the notification.
*
* @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
方法。
访问通知
通知被存放到数据表之后,需要在被通知实体中有一个便捷的方式来访问它们。Laravel默认提供的App\User
模型引入的Illuminate\Notifications\Notifiable
trait包含了返回实体对应通知的Eloquent关联关系方法notifications
,要获取这些通知,可以像访问其它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;
}
注:要想从JavaScript客户端访问通知,需要在应用中定义一个通知控制器为指定被通知实体(比如当前用户)返回通知,然后从JavaScript客户端发送一个HTTP请求到控制器对应URI。
标记通知为已读
一般情况下,我们会将用户浏览过的通知标记为已读,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' => Carbon::now()]);
当然,你也可以通过delete
方法从数据库中移除这些通知:
$user->notifications()->delete();
6、广播通知
预备知识
在进行广播通知之前,需要配置并了解事件广播,事件广播为JavaScript客户端响应服务端事件触发铺平了道路。
格式化广播通知
``broadcast通道广播通知使用了Laravel的事件广播服务,从而允许JavaScript客户端实时捕获通知。如果通知支持广播,则需要在通知类上定义
toBroadcast或
toArray方法,该方法接收一个
$notifiable实体并返回原生的PHP数组,返回的数组会编码成JSON格式然后广播到JavaScript客户端。让我们来看一个
toArray`方法的示例:
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable){
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
注:除了指定的数据,广播通知还包含了一个type
字段,用于表示通知类名。
toBroadcast Vs. toArray
toArray
方法还可以用于database
通道以判断在数据表中存储哪些数据。如果你想要为database
和broadcast
通道提供两种不同的数组表示方式,需要定义一个toBroadcast
方法来取代toArray
方法。
监听通知
通知将会以格式化为{notifiable}.{id}
的形式在私人频道上广播,因此,如果你要发送通知到ID为1
的App\User
实例,那么该通知将会在私人频道App.User.1
上进行广播,如果使用了Laravel Echo
,可以使用辅助函数notification
轻松在某个频道上监听通知:
Echo.private('App.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
7、短信(SMS)通知
预备知识
Laravel基于Nexmo发送短信通知,在使用Nexmo发送通知前,需要安装Composer包nexmo/client并在配置文件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');
}
自定义来源号码
如果你要通过与配置文件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
方法:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable{
use Notifiable;
/**
* Route notifications for the Nexmo channel.
*
* @return string
*/
public function routeNotificationForNexmo() {
return $this->phone;
}
}
8、Slack通知
预备知识
在通过Slack
发送通知前,必须通过Composer安装Guzzle HTTP库:
composer require guzzlehttp/guzzle
此外,你还要为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,最终创建的消息如下:
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消息:
basic-slack-attachment
附件还允许你指定要呈献给用户的数组数据。为了提高可读性,给定的数组会以表格形式展示:
/** * 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消息:
自定义发送者 & 接收者
你可以使用
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')
}
Slack通知路由
要路由Slack通知到适当的位置,需要在被通知的实体上定义一个routeNotificationForSlack
方法,这将会返回通知被发送到的webhook URL。webhook URL可通过在Slack组上添加一个“Incoming Webhook”服务来生成:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable{
use Notifiable;
/**
* Route notifications for the Slack channel.
*
* @return string
*/
public function routeNotificationForSlack() {
return $this->slack_webhook_url;
}
}
9、通知事件
当通知被发送后,通知系统会触发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
}
10、自定义频道
通过上面的介绍,可见Laravel为我们提供了一大把通知通道,但是如果你想要编写自己的驱动以便通过其他通道发送通知,也很简单。首先定义一个包含send
方法的类,该方法接收两个参数:$notifiable
和$notification
:
<?php
namespace App\Channels;
use Illuminate\Notifications\Notification;
class VoiceChannel{
/**
* Send the given notification.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return void
*/
public function send($notifiable, Notification $notification) {
$message = $notification->toVoice($notifiable);
// Send notification to the $notifiable instance...
}
}
通知类被定义后,就可以在应用中通过via方法返回类名:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use App\Channels\VoiceChannel;
use App\Channels\Messages\VoiceMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class InvoicePaid extends Notification{
use Queueable;
/**
* Get the notification channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable) {
return [VoiceChannel::class];
}
/**
* Get the voice representation of the notification.
*
* @param mixed $notifiable
* @return VoiceMessage
*/
public function toVoice($notifiable) {
// ...
}
网友评论