通知
简介
除了 发送邮件,Laravel 还支持通过多种频道发送通知,包括邮件、短信(通过 Nexmo)以及 Slack。通知还能存到数据库,这样就能在网页界面上显示了。
通常情况下,通知应该是简短、有信息量的消息来通知用户你的应用发生了什么。举例来说,如果你在编写一个在线交易应用,你应该会通过邮件和短信频道来给用户发送一条「账单已付」的通知。
创建通知
Laravel 中一条通知就是一个类(通常存在 app/Notifications
文件夹里)。看不到的话不要担心,运行一下 make:notification
命令就能创建了:
php artisan make:notification InvoicePaid
这个命令会在 app/Notifications
目录下生成一个新的通知类。这个类包含 via
方法和几个消息构建方法(比如 toMail
或 toDatabase
),它们会针对指定的渠道把通知转换过为对应的消息。
发送通知
使 用 Notifiable Trait
通知可以通过两种方法发送: Notifiable
trait 的 notify
方法或 Notification
facade。首先,我们看下 Notifiable
trait 。默认的 App\User
模型中使用了这个 trait,它包含着一个可以用来发通知的方法:notify
。它需要一个通知实例做参数:
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
记住,你可以在任意模型中使用 Illuminate\Notifications\Notifiable
trait,而不仅仅是在 User
模型中。
使用 Notification Facade
另外,你可以通过 Notification
facade 来发送通知。它主要用在当你给多个可接收通知的实体发送通知的时候,比如给用户集合发通知。要用 facade 发送通知的话,要把可接收通知的实体和通知的实例传递给 send
方法:
Notification::send($users, new InvoicePaid($invoice));
指定发送频道
每个通知类都有个 via
方法,它决定了通知在哪个频道上发送。开箱即用的通知频道有 mail
, database
, broadcast
, nexmo
, 和 slack
。
如果你想用其他的频道比如 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
命令来生成通知文件的时候就已经被导入了,所以你只需要添加到你的通知类就行了:
<?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));
邮件通知
格式化邮件消息
如果一条通知支持以邮件发送,你应该在通知类里定义一个 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)
->line('One of your invoices has been paid!')
->action('View Invoice', $url)
->line('Thank you for using our application!');
}
注意我们在方法中用了 $this->invoice->id
,其实你可以传递应用所需要的任何数据来传递给通知的构造器。
在这个例子中,我们注册了一行文本,引导链接 ,然后又是一行文本。MailMessage
提供的这些方法简化了对小的事务性的邮件进行格式化操作。邮件频道将会把这些消息组件转换成漂亮的响应式的 HTML 邮件模板并附上文本。下面是个 mail
频道生成的邮件示例:
自定义接收者
当通过 mail
频道来发送通知的时候,通知系统将会自动寻找你的 notifiable 实体中的 email
属性。你可以通过在实体中定义 routeNotificationForMail
方法来自定义邮件地址。
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* 邮件频道的路由
*
* @return string
*/
public function routeNotificationForMail()
{
return $this->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-notifications
错误消息
有些通知是给用户提示错误,比如账单支付失败的提示。你可以通过调用 error
方法来指定这条邮件消息被当做一个错误提示。当邮件消息使用了 error
方法后,引导链接按钮会变成红色而非蓝色。
/**
* 获取通知的邮件展示方式
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Message
*/
public function toMail($notifiable)
{
return (new MailMessage)
->error()
->subject('Notification Subject')
->line('...');
}
数据库通知
先决条件
数据库通知频道在一张数据表里存储通知信息。这张表包含了比如通知类型、JSON 格式数据等描述通知的信息。
你可以查询这张表的内容在应用界面上展示通知。但是在这之前,你需要先 创建一个数据表来保存通知。你可以用 notifications:table
命令来生成迁移表。
php artisan notifications:table
php artisan migrate
格式化数据库通知
如果通知支持被存储到数据表中,你应该在通知类中定义一个 toDatabase
或 toArray
方法。这个方法接收 $notifiable
实体参数并返回一个普通的 PHP 数组。这个返回的数组将被转成 JSON 格式并存储到通知数据表的 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
方法。
访问通知
一旦通知被存到数据库中,你需要一种方便的方式来从通知实体中访问它们。 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;
}
要从 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' => Carbon::now()]);
当然,你可以通过 delete
通知来把它们从数据库删除:
$user->notifications()->delete();
广播通知
先决条件
在广播通知前,你应该配置并熟悉 Laravel 事件广播 服务。事件广播提供了一种 JavaScript 客户端响应服务端 Laravel 事件的机制。
格式化广播通知
broadcast
频道使用 Laravel 事件广播 服务来广播通知,它使得 JavaScript 客户端可以实时捕捉通知。如果一条通知支持广播,你应该在通知类里定义一个 toBroadcast
或 toArray
方法。这个方法将收到一个 $notifiable
实体并返回一个普通的 PHP 数组。返回的数组会被编码成 JSON 格式并广播给你的 JavaScript 客户端。我们来看个 toArray
方法的例子:
/**
* 获取通知的数组展示方式
*
* @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
方法。