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開箱支持 mail databasebroadcastnexmo 以及 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

注:發送郵件通知時,確保在配置文件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;
}

注:要想從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

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

slack-fields-attachment
自定義發送者 & 接收者

你可以使用 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)
    {
        // ...
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章