Skip to content

通知

介绍

除了支持发送电子邮件外,Laravel 还支持通过多种传递渠道发送通知,包括邮件、SMS(通过 Nexmo)和 Slack。通知也可以存储在数据库中,以便在您的 Web 界面中显示。

通常,通知应该是简短的信息性消息,通知用户应用程序中发生的事情。例如,如果您正在编写一个计费应用程序,您可能会通过电子邮件和 SMS 渠道向用户发送“发票已支付”通知。

创建通知

在 Laravel 中,每个通知由一个类表示(通常存储在 app/Notifications 目录中)。如果您在应用程序中没有看到此目录,请不要担心,当您运行 make:notification Artisan 命令时,它将为您创建:

php
php artisan make:notification InvoicePaid

此命令将在您的 app/Notifications 目录中放置一个新的通知类。每个通知类包含一个 via 方法和若干消息构建方法(如 toMailtoDatabase),这些方法将通知转换为针对特定渠道优化的消息。

发送通知

使用 Notifiable Trait

通知可以通过两种方式发送:使用 Notifiable trait 的 notify 方法或使用 Notification facade。首先,让我们看看 Notifiable trait。此 trait 由默认的 App\User 模型使用,并包含一个可用于发送通知的方法:notifynotify 方法期望接收一个通知实例:

php
use App\Notifications\InvoicePaid;

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

请记住,您可以在任何模型上使用 Illuminate\Notifications\Notifiable trait。您不必仅限于将其包含在 User 模型中。

使用 Notification Facade

或者,您可以通过 Notification facade 发送通知。这主要在您需要向多个可通知实体(如用户集合)发送通知时有用。要使用 facade 发送通知,请将所有可通知实体和通知实例传递给 send 方法:

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

指定传递渠道

每个通知类都有一个 via 方法,用于确定通知将在哪些渠道上传递。开箱即用,通知可以通过 maildatabasebroadcastnexmoslack 渠道发送。

lightbulb

如果您想使用其他传递渠道,如 Telegram 或 Pusher,请查看社区驱动的 Laravel Notification Channels 网站

via 方法接收一个 $notifiable 实例,该实例将是接收通知的类的实例。您可以使用 $notifiable 来确定通知应在哪些渠道上传递:

php
/**
 * 获取通知的传递渠道。
 *
 * @param  mixed  $notifiable
 * @return array
 */
public function via($notifiable)
{
    return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database'];
}

队列通知

exclamation

在队列通知之前,您应该配置队列并启动工作程序

发送通知可能需要时间,尤其是在渠道需要外部 API 调用来传递通知时。为了加快应用程序的响应时间,可以通过在类中添加 ShouldQueue 接口和 Queueable trait 来让通知排队。接口和 trait 已经为所有使用 make:notification 生成的通知导入,因此您可以立即将它们添加到通知类中:

php
<?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 接口并自动将通知的传递排队:

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

如果您想延迟通知的传递,可以在通知实例化时链接 delay 方法:

php
$when = Carbon::now()->addMinutes(10);

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

邮件通知

格式化邮件消息

如果通知支持以电子邮件形式发送,您应该在通知类上定义一个 toMail 方法。此方法将接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\MailMessage 实例。邮件消息可以包含文本行以及“行动呼吁”。让我们看看一个 toMail 方法的示例:

php
/**
 * 获取通知的邮件表示。
 *
 * @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!');
}
lightbulb

请注意,我们在 message 方法中使用了 $this->invoice->id。您可以将通知生成消息所需的任何数据传递到通知的构造函数中。

在此示例中,我们注册了一个问候语、一行文本、一项行动呼吁,然后是另一行文本。MailMessage 对象提供的方法使格式化小型事务性电子邮件变得简单快捷。邮件渠道将把消息组件转换为一个漂亮的、响应式的 HTML 电子邮件模板,并附带一个纯文本副本。以下是 mail 渠道生成的电子邮件示例:

lightbulb

发送邮件通知时,请确保在 config/app.php 配置文件中设置 name 值。此值将在邮件通知消息的页眉和页脚中使用。

自定义收件人

通过 mail 渠道发送通知时,通知系统将自动查找可通知实体上的 email 属性。您可以通过在实体上定义 routeNotificationForMail 方法来自定义用于传递通知的电子邮件地址:

php
<?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 方法:

php
/**
 * 获取通知的邮件表示。
 *
 * @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
php artisan vendor:publish --tag=laravel-notifications

错误消息

某些通知会通知用户错误,例如发票支付失败。您可以通过在构建消息时调用 error 方法来指示邮件消息是关于错误的。使用 error 方法在邮件消息上时,行动呼吁按钮将是红色而不是蓝色:

php
/**
 * 获取通知的邮件表示。
 *
 * @param  mixed  $notifiable
 * @return \Illuminate\Notifications\Message
 */
public function toMail($notifiable)
{
    return (new MailMessage)
                ->error()
                ->subject('Notification Subject')
                ->line('...');
}

数据库通知

先决条件

database 通知渠道将通知信息存储在数据库表中。此表将包含通知类型以及描述通知的自定义 JSON 数据。

您可以查询该表以在应用程序的用户界面中显示通知。但是,在此之前,您需要创建一个数据库表来保存通知。您可以使用 notifications:table 命令生成具有正确表模式的迁移:

php
php artisan notifications:table

php artisan migrate

格式化数据库通知

如果通知支持存储在数据库表中,您应该在通知类上定义一个 toDatabasetoArray 方法。此方法将接收一个 $notifiable 实体,并应返回一个纯 PHP 数组。返回的数组将被编码为 JSON 并存储在 notifications 表的 data 列中。让我们看看一个 toArray 方法的示例:

php
/**
 * 获取通知的数组表示。
 *
 * @param  mixed  $notifiable
 * @return array
 */
public function toArray($notifiable)
{
    return [
        'invoice_id' => $this->invoice->id,
        'amount' => $this->invoice->amount,
    ];
}

toDatabasetoArray

toArray 方法也被 broadcast 渠道用于确定要广播到 JavaScript 客户端的数据。如果您希望为 databasebroadcast 渠道提供两个不同的数组表示,您应该定义一个 toDatabase 方法而不是 toArray 方法。

访问通知

一旦通知存储在数据库中,您需要一种方便的方法从可通知实体中访问它们。Illuminate\Notifications\Notifiable trait(包含在 Laravel 的默认 App\User 模型中)包括一个 notifications Eloquent 关系,该关系返回实体的通知。要获取通知,您可以像访问任何其他 Eloquent 关系一样访问此方法。默认情况下,通知将按 created_at 时间戳排序:

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

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

如果您只想检索“未读”通知,可以使用 unreadNotifications 关系。同样,这些通知将按 created_at 时间戳排序:

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

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

要从 JavaScript 客户端访问通知,您应该为应用程序定义一个通知控制器,该控制器返回可通知实体(如当前用户)的通知。然后,您可以从 JavaScript 客户端向该控制器的 URI 发出 HTTP 请求。

标记通知为已读

通常,您会希望在用户查看通知时将其标记为“已读”。Illuminate\Notifications\Notifiable trait 提供了一个 markAsRead 方法,该方法会更新通知数据库记录的 read_at 列:

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

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

但是,您可以直接在通知集合上使用 markAsRead 方法,而不是遍历每个通知:

php
$user->unreadNotifications->markAsRead();

您还可以使用批量更新查询将所有通知标记为已读,而无需从数据库中检索它们:

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

$user->unreadNotifications()->update(['read_at' => Carbon::now()]);

当然,您可以 delete 通知以将其从表中完全删除:

php
$user->notifications()->delete();

广播通知

先决条件

在广播通知之前,您应该配置并熟悉 Laravel 的事件广播服务。事件广播提供了一种从 JavaScript 客户端响应服务器端触发的 Laravel 事件的方法。

格式化广播通知

broadcast 渠道使用 Laravel 的事件广播服务广播通知,允许您的 JavaScript 客户端实时捕获通知。如果通知支持广播,您应该在通知类上定义一个 toBroadcasttoArray 方法。此方法将接收一个 $notifiable 实体,并应返回一个纯 PHP 数组。返回的数组将被编码为 JSON 并广播到您的 JavaScript 客户端。让我们看看一个 toArray 方法的示例:

php
/**
 * 获取通知的数组表示。
 *
 * @param  mixed  $notifiable
 * @return array
 */
public function toArray($notifiable)
{
    return [
        'invoice_id' => $this->invoice->id,
        'amount' => $this->invoice->amount,
    ];
}
lightbulb

除了您指定的数据外,广播通知还将包含一个 type 字段,其中包含通知的类名。

toBroadcasttoArray

toArray 方法也被 database 渠道用于确定要存储在数据库表中的数据。如果您希望为 databasebroadcast 渠道提供两个不同的数组表示,您应该定义一个 toBroadcast 方法而不是 toArray 方法。

监听通知

通知将广播在格式为 {notifiable}.{id} 的私有频道上。因此,如果您向 ID 为 1App\User 实例发送通知,通知将广播在 App.User.1 私有频道上。使用 Laravel Echo 时,您可以使用 notification 辅助方法轻松地在频道上监听通知:

javascript
Echo.private('App.User.' + userId)
    .notification((notification) => {
        console.log(notification.type);
    });

短信通知

先决条件

在 Laravel 中发送 SMS 通知由 Nexmo 提供支持。在通过 Nexmo 发送通知之前,您需要安装 nexmo/client Composer 包,并在 config/services.php 配置文件中添加一些配置选项。您可以复制以下示例配置以开始:

php
'nexmo' => [
    'key' => env('NEXMO_KEY'),
    'secret' => env('NEXMO_SECRET'),
    'sms_from' => '15556666666',
],

sms_from 选项是您的 SMS 消息将从中发送的电话号码。您应该在 Nexmo 控制面板中为您的应用程序生成一个电话号码。

格式化短信通知

如果通知支持以 SMS 形式发送,您应该在通知类上定义一个 toNexmo 方法。此方法将接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\NexmoMessage 实例:

php
/**
 * 获取通知的 Nexmo / SMS 表示。
 *
 * @param  mixed  $notifiable
 * @return NexmoMessage
 */
public function toNexmo($notifiable)
{
    return (new NexmoMessage)
                ->content('Your SMS message content');
}

自定义“发件人”号码

如果您希望从与 config/services.php 文件中指定的电话号码不同的电话号码发送某些通知,可以在 NexmoMessage 实例上使用 from 方法:

php
/**
 * 获取通知的 Nexmo / SMS 表示。
 *
 * @param  mixed  $notifiable
 * @return NexmoMessage
 */
public function toNexmo($notifiable)
{
    return (new NexmoMessage)
                ->content('Your SMS message content')
                ->from('15554443333');
}

路由短信通知

通过 nexmo 渠道发送通知时,通知系统将自动查找可通知实体上的 phone_number 属性。如果您想自定义通知的接收电话号码,请在实体上定义 routeNotificationForNexmo 方法:

php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * 为 Nexmo 渠道路由通知。
     *
     * @return string
     */
    public function routeNotificationForNexmo()
    {
        return $this->phone;
    }
}

Slack 通知

先决条件

在通过 Slack 发送通知之前,您必须通过 Composer 安装 Guzzle HTTP 库:

bash
composer require guzzlehttp/guzzle

您还需要为您的 Slack 团队配置一个“Incoming Webhook”集成。此集成将为您提供一个 URL,您可以在路由 Slack 通知时使用。

格式化 Slack 通知

如果通知支持以 Slack 消息形式发送,您应该在通知类上定义一个 toSlack 方法。此方法将接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\SlackMessage 实例。Slack 消息可以包含文本内容以及格式化附加文本或字段数组的“附件”。让我们看看一个基本的 toSlack 示例:

php
/**
 * 获取通知的 Slack 表示。
 *
 * @param  mixed  $notifiable
 * @return SlackMessage
 */
public function toSlack($notifiable)
{
    return (new SlackMessage)
                ->content('One of your invoices has been paid!');
}

在此示例中,我们只是向 Slack 发送了一行文本,这将创建如下所示的消息:

Slack 附件

您还可以向 Slack 消息添加“附件”。附件提供比简单文本消息更丰富的格式选项。在此示例中,我们将发送有关应用程序中发生的异常的错误通知,包括一个链接以查看有关异常的更多详细信息:

php
/**
 * 获取通知的 Slack 表示。
 *
 * @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 消息:

附件还允许您指定应呈现给用户的数据数组。给定的数据将以表格样式格式呈现,便于阅读:

php
/**
 * 获取通知的 Slack 表示。
 *
 * @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 消息:

自定义发件人和收件人

您可以使用 fromto 方法自定义发件人和收件人。from 方法接受用户名和表情符号标识符,而 to 方法接受频道或用户名:

php
/**
 * 获取通知的 Slack 表示。
 *
 * @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
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * 为 Slack 渠道路由通知。
     *
     * @return string
     */
    public function routeNotificationForSlack()
    {
        return $this->slack_webhook_url;
    }
}

通知事件

发送通知时,通知系统会触发 Illuminate\Notifications\Events\NotificationSent 事件。这包含“可通知”实体和通知实例本身。您可以在 EventServiceProvider 中注册此事件的监听器:

php
/**
 * 应用程序的事件监听器映射。
 *
 * @var array
 */
protected $listen = [
    'Illuminate\Notifications\Events\NotificationSent' => [
        'App\Listeners\LogNotification',
    ],
];
lightbulb

EventServiceProvider 中注册监听器后,使用 event:generate Artisan 命令快速生成监听器类。

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

php
/**
 * 处理事件。
 *
 * @param  NotificationSent  $event
 * @return void
 */
public function handle(NotificationSent $event)
{
    // $event->channel
    // $event->notifiable
    // $event->notification
}

自定义渠道

Laravel 附带了一些通知渠道,但您可能希望编写自己的驱动程序通过其他渠道传递通知。Laravel 使其变得简单。要开始,请定义一个包含 send 方法的类。该方法应接收两个参数:$notifiable$notification

php
<?php

namespace App\Channels;

use Illuminate\Notifications\Notification;

class VoiceChannel
{
    /**
     * 发送给定的通知。
     *
     * @param  mixed  $notifiable
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return void
     */
    public function send($notifiable, Notification $notification)
    {
        $message = $notification->toVoice($notifiable);

        // 向 $notifiable 实例发送通知...
    }
}

一旦定义了通知渠道类,您可以简单地从任何通知的 via 方法返回类名:

php
<?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;

    /**
     * 获取通知渠道。
     *
     * @param  mixed  $notifiable
     * @return array|string
     */
    public function via($notifiable)
    {
        return [VoiceChannel::class];
    }

    /**
     * 获取通知的语音表示。
     *
     * @param  mixed  $notifiable
     * @return VoiceMessage
     */
    public function toVoice($notifiable)
    {
        // ...
    }
}