通知
イントロダクション
メール送信に加え、LaravelはSMS(Nexmo使用)、Slackなどの、さまざまな複数チャンネルへ渡る通知をサポートしています。通知はWebインターフェイスで表示できるように、データバースに保存することもできます。
通常、通知はアプリケーションで何かが起きたことをユーザへ知らせる、短い情報メッセージです。たとえば、課金アプリを作成しているなら、メールとSMSチャンネルで「課金支払い」を送信できます。
通知の作成
Laravelの各通知は、(通常、app/Notifications
ディレクトリに設置される)クラスにより表されます。このディレクトリがアプリケーションで見つからなくても、心配ありません。make:notification
Artisanコマンドを実行すると、作成されます。
php artisan make:notification InvoicePaid
このコマンドにより、真新しい通知クラスが、app/Notifications
ディレクトリに生成されます。各通知クラスはvia
メソッドと、特定のチャンネルに最適化したメッセージへ変換する、いくつかのメッセージ構築メソッド(toMail
、toDatabase
など)を含んでいます。
通知の送信
Notifiableトレイトの使用
通知は2つの方法で送信されます。Notifiable
トレイトのnotify
メソッドか、Notification
ファサードを使う方法です。最初に、Notifiable
トレイトを見ていきましょう。このトレイトは、デフォルトのApp\User
モデルで使用されており、通知を送るためのnotify
メソッドを一つ含んでいます。notify
メソッドは通知インスタンスを受け取ります。
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
Tip!! みなさんのどんなモデルであっても、
Illuminate\Notifications\Notifiable
トレイトを使えることを覚えておきましょう。使用はUser
モデルだけに限定されているわけでありません。
Notificationファサードの使用
ほかに、Notification
ファサードを使用し、通知を送る方法もあります。これは主にユーザコレクションのような、複数の通知可能エンティティに対し、通知する場合に便利です。ファサードを使い通知するには、send
メソッドへ通知可能エンティティ全部と、通知インスタンスを渡します。
Notification::send($users, new InvoicePaid($invoice));
配信チャンネルの指定
通知を配信するチャンネルを指定するため、すべての通知クラスはvia
メソッドを持っています。通知はmail
、database
、broadcast
、nexmo
、slack
へ送れるようになっています。
Tip!! TelegramやPusherのような、他の配信チャンネルを利用したい場合は、コミュニティが管理している、Laravel Notification Channels websiteをご覧ください。
via
メソッドは、通知を送っているクラスのインスタンスである、$notifiable
インスタンスを引数に受け取ります。$notifiable
を使い、通知をどこに配信するチャンネルなのかを判定することができます。
/**
* 通知の配信チャンネルを取得
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database'];
}
通知のキューイング
Note: 通知のキューイングを行う前に、キューを設定し、ワーカを起動する必要があります。
通知の送信には時間が取られます。特にそのチャンネルが通知を配信するために、外部のAPIを呼び出す必要がある場合は特にです。アプリケーションのレスポンスタイムを向上させるには、クラスにShouldQueue
インターフェイスと、Queueable
トレイトを追加し、キューイングしましょう。このインターフェイスとトレイトは、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));
If you would like to delay the deliver of the notification, you may
chain the delay
method onto your notification
instantiation:
$when = Carbon::now()->addMinutes(10);
$user->notify((new InvoicePaid($invoice))->delay($when));
メール通知
メールメッセージのフォーマット
ある通知でメール送信をサポートする場合、通知クラスにtoMail
メソッドを定義してください。このメソッドは、$notifiable
エンティティを受け取り、Illuminate\Notifications\Messages\MailMessage
インスタンスを返す必要があります。メールメッセージはテキスト行と、同時に"call
to
action"(アクションの呼び出し)を含むことでしょう。toMail
メソッドの例を見てみましょう。
/**
* 通知のメールプレゼンテーションを取得
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->greeting('Hello!')
->line('課金が支払われました。')
->action('インボイス確認', $url)
->line('私達のアプリケーションをご利用いただき、ありがとうございます。');
}
Tip!!
message
メソッドの中で、$this->invoice->id
を使っていることに注意してください。通知メッセージを生成するために必要な情報は、どんなものでも通知のコンストラクタへ渡せます。
この例では、挨拶、テキスト行、アクションの呼び出し、別のテキスト行を登録しています。これらのメソッドは、小さなトランザクションメールをシンプルで素早くフォーマットする、MailMessage
オブジェクトが提供しています。メールチャンネルはテンプレートにより、メッセージの構成物をきれいでレスポンシブなHTMLメールへ、変換します。平文メールも用意されます。以下はmail
チャンネルにより生成されたメールの例です。I
Tip!! メール通知を行うときは、
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;
/**
* メールチャンネルに対する通知をルートする
*
* @return string
*/
public function routeNotificationForMail()
{
return $this->email_address;
}
}
件名のカスタマイズ
デフォルトのメール件名は、通知のクラス名を"title
case"にフォーマットしたものです。ですから、InvoicePaid
という通知クラス名は、Invoice Paid
というメールの件名になります。メッセージの件名を明確に指定したい場合は、メッセージを構築時にsubject
メソッドを呼び出してください。
/**
* 通知のメールプレゼンテーションを取得
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Notification Subject')
->line('...');
}
テンプレートのカスタマイズ
通知パッケージのリソースを公開(開発者が変更できる場所にリソースを用意することを示すLaravel用語)することにより、メール通知で使用される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('...');
}
データベース通知
事前要件
database
通知チャンネルは、通知情報をデータベーステーブルへ保存します。このテーブルは通知タイプのような情報と同時に、通知を説明するカスタムJSONデータを含みます。
アプリケーションのユーザインターフェイスで通知を表示するために、テーブルをクエリすることができます。しかし、その前に通知を保存するデータベーステーブルを作成する必要があります。実際のテーブルスキーマのマイグレーションを生成するために、notifications:table
を使用してください。
php artisan notifications:table
php artisan migrate
データベース通知のフォーマット
通知でデータベーステーブルへの保存をサポートする場合、通知クラスにtoDatabase
かtoArray
メソッドを定義する必要があります。このメソッドは$notifiable
エンティティを受け取り、プレーンなPHP配列を返す必要があります。返された配列はJSONへエンコードされ、notifications
テーブルの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
メソッドは、JavaScriptクライアントへどのデータをブロードキャストするかを決めるために、broadcast
チャンネルでも使用されます。database
とbroadcast
チャンネルで、別々の配列プレゼンテーションを持ちたい場合は、toArray
メソッドの代わりに、toDatabase
メソッドを定義してください。
通知へのアクセス
通知をデータベースへ保存したら、通知エンティティから便利にアクセスできる方法が必要になります。LaravelのデフォルトApp\User
モデルに含まれている、Illuminate\Notifications\Notifiable
トレイトは、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;
}
Tip!! 通知にJavaScriptクライアントからアクセスするには、現在のユーザのような、通知可能なエンティティに対する通知を返す、通知コントローラをアプリケーションに定義する必要があります。その後、JavaScriptクライエントから、コントローラのURIへHTTPリクエストを作成します。
Readとしての通知作成
通常、ユーザが閲覧したときに、その通知を「既読」とマークするでしょう。Illuminate\Notifications\Notifiable
トレイトは、通知のデータベースレコード上にある、read_at
カラムを更新するmarkAsRead
メソッドを提供しています。
$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
チャンネルは、リアルタイムでJavaScriptクライアントが通知を補足できるようにする、Laravelのイベントブロードキャストサービスを用い、通知をブロードキャストします。通知でブロードキャストをサポートする場合、通知クラスで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,
];
}
Tip!! 指定したデータに付け加え、ブロードキャスト通知は、通知のクラス名を含んだ
type
フィールドも付加します。
toBroadcast
Vs. toArray
toArray
メソッドは、データベーステーブルへ保存するデータを決めるために、database
チャンネルによっても使用されます。database
とbroadcast
チャンネルで、それぞれ異なった配列プレゼンテーションを持ちたい場合は、toArray
メソッドの代わりにtoBroadcast
メソッドを定義してください。
通知のリッスン
プライベートチャンネルにブロードキャストされる通知は、{notifiable}.{id}
命名規則に従いフォーマットされます。ですから、IDが1
のApp\User
インスタンスを通知で送る場合、App.User.1
プライベートチャンネルへブロードキャストされます。Laravel
Echoを使用していれば、notification
ヘルパメソッドを使い、チャンネルへの通知を簡単にリッスンできます。
Echo.private('App.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
SMS通知
事前要件
LaravelのSMS通知送信は、Nexmoを使用します。Nexmoにより通知を送れるようにする前に、nexmo/client
Composerパッケージをインストールし、config/services.php
設定ファイルへ設定オプションをいくつか追加する必要があります。参考例として、以下の設定をコピーしてください。
'nexmo' => [
'key' => env('NEXMO_KEY'),
'secret' => env('NEXMO_SECRET'),
'sms_from' => '15556666666',
],
sms_from
オプションはSMSメッセージを送る電話番号です。アプリケーションの電話番号は、Nexmoコントロールパネルで作成してください。
SMS通知のフォーマット
SMSとしての通知をサポートするには、通知クラスにtoNexmo
メソッドを定義する必要があります。このメソッドは$notifiable
エンティティを受け取り、Illuminate\Notifications\Messages\NexmoMessage
インスタンスを返す必要があります。
/**
* 通知のNexmo/SMSプレゼンテーションを取得する
*
* @param mixed $notifiable
* @return NexmoMessage
*/
public function toNexmo($notifiable)
{
return (new NexmoMessage)
->content('Your SMS message content');
}
"from"電話番号のカスタマイズ
config/services.php
ファイルで指定した電話番号とは異なる番号から、通知を送りたい場合は、NexmoMessage
インスタンスのfrom
メソッドを使用します。
/**
* 通知のNexmo/SMSプレゼンテーションを取得する
*
* @param mixed $notifiable
* @return NexmoMessage
*/
public function toNexmo($notifiable)
{
return (new NexmoMessage)
->content('Your SMS message content')
->from('15554443333');
}
SMS通知のルート指定
nexmo
チャンネルで通知を送るとき、通知システムは自動的にphone_number
属性を通知可能エンティティの中で探します。通知を配信する電話番号をカスタマイズしたい場合は、エンティティでrouteNotificationForNexmo
メソッドを定義してください。
<?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を通じ通知を送る場合、Guzzle HTTPライブラリをComposerでインストールする必要があります。
composer require guzzlehttp/guzzle
さらに、Slackチームの"Incoming Webhook"インテグレーションを設定する必要もあります。このインテグレーションは、Slack通知のルートを行う時に使用するURLを提供します。
Slack通知のフォーマット
通知がSlackメッセージとしての送信をサポートする場合、通知クラスにtoSlack
メソッドを定義する必要があります。このメソッドは$notifiable
エンティティを受け取り、Illuminate\Notifications\Messages\SlackMessage
インスタンスを返す必要があります。Slackメッセージはテキストと同時に、追加テキストのフォーマットか、フィールドの配列を「添付」として含みます。基本的なtoSlack
の例を見てください。
/**
* 通知のSlackプレゼンテーションを取得
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('One of your invoices has been paid!');
}
この例では、Slackへ一行のテキストを送っており、以下のようなメッセージが生成されます。
Slack添付
Slackメッセージに「添付」を追加することもできます。添付はシンプルなテキストメッセージよりも、リッチなフォーマットのオプションを提供します。以下の例では、アプリケーションで起きた例外についてのエラー通知で、例外についての詳細情報を表示するリンクを含めています。
/**
* 通知の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メッセージを生成します。
添付ではさらに、ユーザに対し表示すべきデータの配列を指定することもできます。指定したデータは簡単に読めるように、テーブルスタイルの形式で表示されます。
/**
* 通知の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メッセージを作成します。
送信者と受信者のカスタマイズ
from
とto
メソッドを使い、送信者と受信者のカスタマイズができます。from
メソッドはユーザ名と絵文字識別子を受け付け、to
メソッドはチャンネルかユーザ名を受け取ります。
/**
* 通知の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
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
で登録します。
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Notifications\Events\NotificationSent' => [
'App\Listeners\LogNotification',
],
];
Tip!!
EventServiceProvider
でリスナを登録した後に、event:generate
Artisanコマンドを使うと、リスナークラスが素早く生成できます。
イベントリスナーの中で、通知受取人や通知自身について調べるために、そのイベントのnotifiable
、notification
、channel
プロパティにアクセスできます。
/**
* Handle the event.
*
* @param NotificationSent $event
* @return void
*/
public function handle(NotificationSent $event)
{
// $event->channel
// $event->notifiable
// $event->notification
}
カスタムチャンネル
Laravelはいくつかの通知チャンネルを用意していますが、他のチャンネルを使用し通知を配信するために、独自のドライバーを書くこともあるでしょう。Laravelでは、これも簡単です。手始めに、send
メソッドを含むクラスを定義しましょう。このメソッドは$notifiable
と
$notification
の、2引数を受け取ります。
<?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
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)
{
// ...
}
}