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消息:
附件還允許你指定要呈獻給用戶的數組數據。爲了提高可讀性,給定的數組會以表格形式展示:
/** * 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) { // ... } }