laravel 自带消息notification

1、简介

除了支持发送邮件之外,Laravel还支持通过多种传输通道发送通知,这些通道包括邮件、短信(通过Nexmo)以及Slack等。通知可以存储在数据库以便后续在web界面中显示。

通常,通知都是很短的、用于告知用户应用中所发生事件的消息。例如,如果你在开发一个计费应用,则需要通过邮件或短信等渠道给用户发送“账单支付”通知。

2、创建通知

在Laravel中,每个通知都以单独类的形式存在(通常存放在app/Notifications目录),如果在应用中没看到这个目录,别担心,它将会在你运行Artisan命令make:notification的时候自动创建:

php artisan make:notification InvoicePaid

该命令会在app/Notifications目录下生成一个新的通知类,每个通知类都包含一个via方法以及多个消息构建方法(如toMailtoDatabase),这些消息构建方法用于将通知转化成为特定渠道优化的消息。

3、发送通知

使用Notifiable Trait

通知可以通过两种方式发送:使用Notifiabletrait提供的notify方法或者使用Notification门面。首先,我们来检验Notifiabletrait。该trait被默认的App\User模型使用并提供一个可用于发送通知的方法:notifynotify方法接收一个通知实例:

use App\Notifications\InvoicePaid;

$user->notify(new InvoicePaid($invoice));

注:记住,你可以在任何模型中使用Illuminate\Notifications\Notifiabletrait,不限于只在User模型中使用。

使用Notification门面

作为替代方案,还可以通过 Notification 门面发送通知。这主要在你需要发送通知给多个用户的时候很有用,要使用这个门面发送通知,需要将所有被通知用户和通知实例传递给 send 方法:

Notification::send($users, new InvoicePaid($invoice));

指定传输通道

每个通知类都有一个 via 方法用于决定通知通过何种通道传输,Laravel开箱支持 maildatabasebroadcastnexmo 以及 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通道生成的邮件示例:

notification-example-947x1024.png

注:发送邮件通知时,确保在配置文件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

格式化数据库通知

如果一个通知支持存放在数据表,则需要在通知类中定义toDatabasetoArray方法,该方法接收一个$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客户端,如果你想要为databasebroadcast通道提供两种不同的数组表示,则需要定义一个toDatabase方法来取代toArray方法。

访问通知

通知被存放到数据表之后,需要在被通知实体中有一个便捷的方式来访问它们。Laravel默认提供的App\User模型引入的Illuminate\Notifications\Notifiabletrait包含了返回实体对应通知的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;
}

如果你只想获取未读消息,可使用关联关系unreadNotifications,同样,这些通知也按照created_at时间戳排序:

$user = App\User::find(1);

foreach ($user->unreadNotifications as $notification) {
    echo $notification->type;
}

注:要想从JavaScript客户端访问通知,需要在应用中定义一个通知控制器为指定被通知实体(比如当前用户)返回通知,然后从JavaScript客户端发送一个HTTP请求到控制器对应URI。

标记通知为已读

一般情况下,我们会将用户浏览过的通知标记为已读,Illuminate\Notifications\Notifiabletrait提供了一个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客户端实时捕获通知。如果通知支持广播,则需要在通知类上定义 toBroadcasttoArray 方法,该方法接收一个 $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 通道以判断在数据表中存储哪些数据。如果你想要为 databasebroadcast 通道提供两种不同的数组表示方式,需要定义一个 toBroadcast 方法来取代 toArray 方法。

监听通知

通知将会以格式化为 {notifiable}.{id} 的形式在私人频道上广播,因此,如果你要发送通知到ID为1App\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,最终创建的消息如下:

basic-slack-notification.png

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.png

附件还允许你指定要呈献给用户的数组数据。为了提高可读性,给定的数组会以表格形式展示:

/**
 * 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消息:


slack-fields-attachment.png

自定义发送者 & 接收者

你可以使用 fromto 方法自定义发送者和接收者, 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 快速生成监听器类。

在事件监听器中,可以访问事件的 notifiablenotificationchannel 属性了解通知接收者和通知本身的更多信息:

/**
 * 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)
    {
        //... 
    }
}

文章转载自:https://blog.csdn.net/zhangxh1013/article/details/53130490

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260