通知
「請求書が支払われました」のような短い知らせを、メール・データベース・ブラウザ・SMS・Slack などへ送る通知のしくみと、その書き方・キュー・テストを説明します。
通知は、アプリの中で起きたことを、利用者へ短く知らせるしくみです。郵便・電話・掲示板のように、知らせる手段(チャンネル)がいくつもあり、1つの通知を、複数のチャンネルで送れます。Laravel は、メールのほか、SMS(Vonage というサービスを使います。以前の名前は Nexmo)と Slack で通知を送れます。コミュニティ(Laravel を使う人たちの集まり)が作ったチャンネルも、数十種類あります。通知をデータベースに保存して、画面に表示することもできます。
通知は、ふつう、アプリで起きたことを知らせる短い文です。たとえば、請求のアプリなら、「請求書が支払われました」という通知を、メールと SMS で利用者に送るような使い方をします。
通知を作る#
Laravel では、通知1種類ごとに、1つのクラスを作ります。クラスは、ふつう app/Notifications に置きます。フォルダが見つからなくても大丈夫です。make:notification コマンドを実行すると、自動で作られます。
php artisan make:notification InvoicePaid
このコマンドは、新しい通知のクラスを app/Notifications に作ります。通知のクラスには、via メソッドと、toMail や toDatabase のような、メッセージを作るメソッドがいくつか入ります。メッセージを作るメソッドは、通知を、そのチャンネルに合った形のメッセージに変えます。
通知を送る#
Notifiable トレイトで送る#
通知を送る方法は2つあります。Notifiable トレイト(いくつものクラスに同じ機能を足す部品)の notify を使う方法と、Notification ファサード(Notification:: と書いて機能を呼べる窓口)を使う方法です。Notifiable は、アプリの App\Models\User モデルに、最初から入っています。
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
}
このトレイトの notify は、通知のインスタンス(クラスから作った実物)を受け取ります。
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
補足
Notifiable トレイトは、どのモデルにも付けられます。User モデルだけに付けるものではありません。
Notification ファサードで送る#
Notification ファサードでも送れます。ユーザーのコレクション(入れ物)のように、通知を受け取る相手が複数いるときに便利です。ファサードで送るには、受け取る相手全員と、通知のインスタンスを、send に渡します。
use Illuminate\Support\Facades\Notification;
Notification::send($users, new InvoicePaid($invoice));
sendNow を使うと、すぐに送れます。通知に ShouldQueue が付いていても、キューに入れずに送ります。
Notification::sendNow($developers, new DeploymentCompleted($deployment));
送るチャンネルを決める#
どの通知のクラスにも、via メソッドがあります。この通知をどのチャンネルで送るかを決めます。使えるチャンネルは、mail・database・broadcast・vonage・slack です。
| チャンネル | 説明 |
|---|---|
mail |
メールで送る |
database |
データベースに保存する |
broadcast |
ブラウザへリアルタイムに届ける |
vonage |
SMS で送る |
slack |
Slack に送る |
補足
Telegram や Pusher のような、ほかのチャンネルを使いたいときは、コミュニティが作っている Laravel Notification Channels のサイトを見てください。
via は、$notifiable(通知を受け取る相手のインスタンス)を受け取ります。$notifiable を使って、どのチャンネルで送るかを決められます。
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
}
通知をキューに入れる#
注意
通知をキューに入れる前に、キューを設定し、ワーカー(列から仕事を取り出して動かすプログラム)を起動してください。
通知を送るには、時間がかかることがあります。とくに、チャンネルが外の API を呼んで送るときです。アプリの応答を速くするために、通知のクラスに、ShouldQueue のインターフェイス(決まりごと)と Queueable のトレイトを足して、キューに入れるようにできます。make:notification で作った通知には、どちらも最初から読み込まれているので、すぐに足せます。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
// ...
}
通知に ShouldQueue を付けたら、ふつうどおりに送れます。Laravel が ShouldQueue に気づいて、自動で、通知の配信をキューに入れます。
$user->notify(new InvoicePaid($invoice));
通知をキューに入れると、受け取る相手とチャンネルの組み合わせごとに、キューのジョブ(キューに並べる1つ1つの仕事)が1つ作られます。たとえば、受け取る相手が3人、チャンネルが2つなら、6つのジョブがキューに送られます。
通知を少し待たせる#
通知を送るのを遅らせたいときは、通知のインスタンスに delay をつなげます。
$delay = now()->plus(minutes: 10);
$user->notify((new InvoicePaid($invoice))->delay($delay));
delay に配列を渡すと、チャンネルごとに待ち時間を決められます。
$user->notify((new InvoicePaid($invoice))->delay([
'mail' => now()->plus(minutes: 5),
'sms' => now()->plus(minutes: 10),
]));
通知のクラスに withDelay メソッドを書いても決められます。チャンネルの名前と待ち時間の配列を返します。
/**
* Determine the notification's delivery delay.
*
* @return array<string, \Illuminate\Support\Carbon>
*/
public function withDelay(object $notifiable): array
{
return [
'mail' => now()->plus(minutes: 5),
'sms' => now()->plus(minutes: 10),
];
}
通知のキューの接続を決める#
既定では、キューに入れた通知は、アプリの既定のキュー接続に入ります。ある通知だけ、別の接続を使いたいときは、通知のコンストラクター(クラスからものを作るときに最初に動くメソッド)で onConnection を呼びます。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*/
public function __construct()
{
$this->onConnection('redis');
}
}
チャンネルごとに別のキュー接続を使いたいときは、通知に viaConnections メソッドを書きます。チャンネルの名前とキュー接続の名前の組の配列を返します。
/**
* Determine which connections should be used for each notification channel.
*
* @return array<string, string>
*/
public function viaConnections(): array
{
return [
'mail' => 'redis',
'database' => 'sync',
];
}
チャンネルごとのキューを決める#
チャンネルごとに別のキューを使いたいときは、通知に viaQueues メソッドを書きます。チャンネルの名前とキューの名前の組の配列を返します。
/**
* Determine which queues should be used for each notification channel.
*
* @return array<string, string>
*/
public function viaQueues(): array
{
return [
'mail' => 'mail-queue',
'slack' => 'slack-queue',
];
}
キューのジョブの属性を決める#
通知を送る裏のジョブの動きは、通知のクラスに、キューに関わる PHP の属性(#[Tries(5)] のように、クラスの前に書く印)を付けて決められます。この属性は、通知を送るキューのジョブに引き継がれます。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\Tries;
#[Tries(5)]
#[Timeout(120)]
#[MaxExceptions(3)]
#[FailOnTimeout]
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
// ...
}
| 属性 | 説明 |
|---|---|
Tries |
試す回数の上限 |
Timeout |
制限時間(秒) |
MaxExceptions |
処理されなかった例外の回数の上限 |
FailOnTimeout |
時間切れになったら失敗にする |
キューに入れた通知のデータを、他人に読まれたり書き換えられたりしないようにするには、通知のクラスに ShouldBeEncrypted を付けます(暗号化)。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification implements ShouldQueue, ShouldBeEncrypted
{
use Queueable;
// ...
}
こうした属性のほかに、backoff と retryUntil のメソッドで、再挑戦までの待ち方と、再挑戦の期限を決められます。
use DateTime;
/**
* Calculate the number of seconds to wait before retrying the notification.
*/
public function backoff(): int
{
return 3;
}
/**
* Determine the time at which the notification should timeout.
*/
public function retryUntil(): DateTime
{
return now()->plus(minutes: 5);
}
補足
これらのジョブの属性とメソッドについては、キューのページの「試す回数と制限時間」を見てください。
キューの通知とミドルウェア#
キューに入れた通知にも、キューのジョブと同じように、ミドルウェア(ジョブの前後に決まった処理をはさむしくみ。キュー)を決められます。通知のクラスに middleware メソッドを書きます。このメソッドは、$notifiable と $channel を受け取るので、通知の送り先によって、返すミドルウェアを変えられます。
use Illuminate\Queue\Middleware\RateLimited;
/**
* Get the middleware the notification job should pass through.
*
* @return array<int, object>
*/
public function middleware(object $notifiable, string $channel)
{
return match ($channel) {
'mail' => [new RateLimited('postmark')],
'slack' => [new RateLimited('slack')],
default => [],
};
}
キューの通知とデータベースのトランザクション#
トランザクション(複数の書き込みを、まとめて確定するか取り消す仕組み)の中でキューの通知を出すと、確定よりも先に処理されることがあります。そのときは、トランザクションの中で変えたデータがまだ反映されていなかったり、作ったはずのデータがまだ無かったりします。通知がそのデータを使っていると、思わぬエラーが出ます。
キュー接続の after_commit が false でも、通知を送るときに afterCommit を呼べば、開いているトランザクションがすべて確定したあとで、その通知を送れます。
use App\Notifications\InvoicePaid;
$user->notify((new InvoicePaid($invoice))->afterCommit());
通知のコンストラクターの中で、afterCommit を呼んでも決められます。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*/
public function __construct()
{
$this->afterCommit();
}
}
補足
この問題への対策は、キューのページの「データベースのトランザクションとジョブ」にくわしく書いてあります。
送るかどうかを最後に決める#
キューに入れた通知は、キューのワーカーが受け取って、ふつうは相手に送られます。
ただ、ワーカーが処理するときに、「やっぱり送るか」を最後に決めたいなら、通知のクラスに shouldSend メソッドを書けます。このメソッドが false を返すと、通知は送られません。
/**
* Determine if the notification should be sent.
*/
public function shouldSend(object $notifiable, string $channel): bool
{
return $this->invoice->isPaid();
}
送ったあとに動かす#
通知を送ったあとに動かしたい処理があるなら、通知のクラスに afterSending メソッドを書きます。このメソッドは、受け取る相手・チャンネルの名前・チャンネルからの返事を受け取ります。
/**
* Handle the notification after it has been sent.
*/
public function afterSending(object $notifiable, string $channel, mixed $response): void
{
// ...
}
その場で決めた宛先に送る(オンデマンドの通知)#
アプリの「ユーザー」として保存されていない人に、通知を送りたいことがあります。Notification ファサードの route で、送る前に、その場かぎりの宛先を決められます。
use Illuminate\Broadcasting\Channel;
use Illuminate\Support\Facades\Notification;
Notification::route('mail', 'taylor@example.com')
->route('vonage', '5555555555')
->route('slack', '#slack-channel')
->route('broadcast', [new Channel('channel-name')])
->notify(new InvoicePaid($invoice));
mail の宛先に、相手の名前も書きたいときは、配列の最初の要素に、メールアドレスをキー、名前を値にした配列を渡します。
Notification::route('mail', [
'barrett@example.com' => 'Barrett Blair',
])->notify(new InvoicePaid($invoice));
routes を使うと、複数のチャンネルの宛先を、まとめて決められます。
Notification::routes([
'mail' => ['barrett@example.com' => 'Barrett Blair'],
'vonage' => '5555555555',
])->notify(new InvoicePaid($invoice));
メールの通知#
メールの形を決める#
通知をメールで送れるようにするには、通知のクラスに toMail メソッドを書きます。このメソッドは、$notifiable を受け取り、Illuminate\Notifications\Messages\MailMessage を返します。
MailMessage には、取引のメール(請求や注文の確認など)を作るのを助ける、かんたんなメソッドがあります。メールには、文章の行と、「行動を促すボタン」(call to action)を入れられます。toMail の例を見てみましょう。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->greeting('Hello!')
->line('One of your invoices has been paid!')
->lineIf($this->amount > 0, "Amount paid: {$this->amount}")
->action('View Invoice', $url)
->line('Thank you for using our application!');
}
補足
この toMail の中で、$this->invoice->id を使っています。通知がメッセージを作るのに必要なデータは、何でも、通知のコンストラクターに渡せます。
この例では、あいさつ・文章の1行・ボタン・もう1行の文章を決めています。MailMessage のメソッドを使うと、短い取引のメールを、簡単にすばやく作れます。メールのチャンネルが、これらの部品を、見やすくて画面の大きさに合わせて変わる HTML のメールと、テキスト版のメールに変えてくれます。
補足
メールの通知を送るときは、config/app.php の name を必ず決めてください。この値が、メールの通知のヘッダーとフッターに使われます。
使える MailMessage の主なメソッドは、次のとおりです。
| メソッド | 説明 |
|---|---|
greeting |
あいさつを決める |
line |
文章の1行を足す |
lineIf |
条件が true のときだけ、文章の1行を足す |
action |
ボタン(ラベルと URL)を足す |
error |
エラーのメールにする(ボタンが赤くなる) |
subject |
件名を決める |
view |
自分のテンプレートで作る |
text |
テキストだけのテンプレートで作る |
markdown |
Markdown のテンプレートで作る |
theme |
Markdown のメールのテーマを決める |
from |
差出人を決める |
mailer |
送るメイラーを決める |
attach |
ファイルを添付する |
attachMany |
複数のファイルを添付する |
attachData |
生のデータを添付する |
tag |
メールにタグをつける |
metadata |
メールにメタデータをつける |
withSymfonyMessage |
Symfony のメッセージを調整する |
エラーのメール#
通知には、請求の支払いの失敗のように、エラーを知らせるものもあります。メールを作るとき、error を呼ぶと、そのメールがエラーに関するものだと決められます。メールに error を使うと、ボタンの色が、黒ではなく赤になります。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->error()
->subject('Invoice Payment Failed')
->line('...');
}
そのほかの形の決め方#
通知のクラスに「文章の行」を書くかわりに、view で、メールを作る自分のテンプレートを決められます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->view(
'mail.invoice.paid', ['invoice' => $this->invoice]
);
}
view に渡す配列の2番目の要素にビューの名前を書くと、テキスト版のビューも決められます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->view(
['mail.invoice.paid', 'mail.invoice.paid-text'],
['invoice' => $this->invoice]
);
}
メールにテキスト版のビューしかないときは、text を使います。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->text(
'mail.invoice.paid-text', ['invoice' => $this->invoice]
);
}
差出人を決める#
既定では、メールの差出人は、config/mail.php に決めてあるものです。ある通知だけ、別の差出人にしたいときは、from を使います。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->from('barrett@example.com', 'Barrett Blair')
->line('...');
}
宛先を決める#
mail チャンネルで通知を送るとき、通知のしくみは、通知を受け取る相手のインスタンスから、email プロパティを自動で探します。どのメールアドレスに送るかを変えたいときは、受け取る側のモデルに、routeNotificationForMail メソッドを書きます。
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Notifications\Notification;
class User extends Authenticatable
{
use Notifiable;
/**
* Route notifications for the mail channel.
*
* @return array<string, string>|string
*/
public function routeNotificationForMail(Notification $notification): array|string
{
// Return email address only...
return $this->email_address;
// Return email address and name...
return [$this->email_address => $this->name];
}
}
件名を決める#
既定では、メールの件名は、通知のクラスの名前を「タイトルケース」(単語の先頭を大文字にした形)にしたものです。たとえば、通知のクラスが InvoicePaid なら、件名は Invoice Paid になります。別の件名にしたいときは、メッセージを作るとき、subject を呼びます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('Notification Subject')
->line('...');
}
メイラーを決める#
既定では、メールの通知は、config/mail.php に決めてある既定のメイラーで送られます。送るときに別のメイラーを使いたいときは、メッセージを作るとき、mailer を呼びます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->mailer('postmark')
->line('...');
}
テンプレートを書きかえる#
メールの通知が使う HTML とテキストのテンプレートは、通知のパッケージのファイルを書き出すと、書きかえられます。このコマンドを実行したあと、メールの通知のテンプレートは、resources/views/vendor/notifications に置かれます。
php artisan vendor:publish --tag=laravel-notifications
添付ファイル#
メールの通知にファイルを添付するには、メッセージを作るとき、attach を使います。第1引数に、ファイルの絶対パス(いちばん上から書いた場所)を渡します。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file');
}
補足
通知のメールの attach は、メールのページで説明した「添付できるオブジェクト」も受け取れます。くわしくは、メールのページを見てください。
ファイルを添付するとき、第2引数に array を渡して、表示する名前や MIME タイプ(ファイルの種類を表す名前)も決められます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
複数のファイルを添付するときは、attachMany を使います。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attachMany([
'/path/to/forge.svg',
'/path/to/vapor.svg' => [
'as' => 'Logo.svg',
'mime' => 'image/svg+xml',
],
]);
}
特定のファイルの保存先(ディスク)にあるファイルを添付するには、attachFromStorageDisk を使います。このメソッドは、ディスクの名前と、そのディスクの中のファイルの場所を受け取ります。
use App\Mail\InvoicePaid as InvoicePaidMailable;
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): Mailable
{
return (new InvoicePaidMailable($this->invoice))
->to($notifiable->email)
->attachFromStorageDisk('s3', '/path/to/file', 'invoice.pdf', [
'mime' => 'application/pdf',
]);
}
生のデータを添付する#
attachData は、生のバイト列(ファイルの中身そのもの)を添付します。attachData には、添付ファイルにつけるファイル名を渡します。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attachData($this->pdf, 'name.pdf', [
'mime' => 'application/pdf',
]);
}
タグとメタデータをつける#
Mailgun や Postmark などのメールのサービスには、メールの「タグ」と「メタデータ」(付属のデータ)に対応しているものがあります。アプリが送ったメールを、グループ分けしたり追いかけたりするのに使えます。メールに、tag と metadata で、タグとメタデータをつけられます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Comment Upvoted!')
->tag('upvote')
->metadata('comment_id', $this->comment->id);
}
Mailgun や Postmark を使っているなら、それぞれのサービスの説明で、タグとメタデータの詳しいことを調べられます。Amazon SES でメールを送るなら、SES の「タグ」をメールにつけるために、metadata を使います。
Symfony のメッセージを調整する#
MailMessage の withSymfonyMessage で、送る前に Symfony のメッセージを受け取って動くクロージャ(名前のない関数)を登録できます。送る前にメールを細かく調整できます。
use Symfony\Component\Mime\Email;
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()->addTextHeader(
'Custom-Header', 'Header Value'
);
});
}
メイラブルを使う#
必要なら、通知の toMail から、メイラブル(メール1種類を表すクラス)をそのまま返せます。MailMessage のかわりに Mailable を返すときは、メイラブルの to で、宛先を決める必要があります。
use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Mail\Mailable;
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): Mailable
{
return (new InvoicePaidMailable($this->invoice))
->to($notifiable->email);
}
メイラブルとオンデマンドの通知#
前に説明したオンデマンドの通知を送るとき、toMail に渡される $notifiable は、Illuminate\Notifications\AnonymousNotifiable のインスタンスです。このクラスには、オンデマンドの通知の送り先のメールアドレスを取り出せる routeNotificationFor があります。
use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Mail\Mailable;
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): Mailable
{
$address = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
return (new InvoicePaidMailable($this->invoice))
->to($address);
}
メールの通知をブラウザで確かめる#
メールの通知のテンプレートを作るときは、ふつうの Blade のテンプレートのように、ブラウザですぐ確かめられると便利です。そのため、Laravel では、メールの通知が作ったメールのメッセージを、ルートのクロージャやコントローラーから、そのまま返せます。MailMessage を返すと、HTML に組み立てられて、ブラウザに表示されます。本物のメールアドレスに送らなくても、見た目をすぐに確かめられます。
use App\Models\Invoice;
use App\Notifications\InvoicePaid;
Route::get('/notification', function () {
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))
->toMail($invoice->user);
});
Markdown のメールの通知#
Markdown のメールの通知は、メールの通知のできあがったテンプレートを使いながら、長いメッセージや、自分で工夫したメッセージを、もっと自由に書けるようにするものです。メッセージを Markdown で書くので、Laravel が、見やすくて画面の大きさに合わせて変わる HTML のメールを作ってくれます。テキスト版も、自動で作られます。
メッセージを作る#
Markdown のテンプレートつきの通知を作るには、make:notification の --markdown オプションを使います。
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
ほかのメールの通知と同じように、Markdown のテンプレートを使う通知も、通知のクラスに toMail を書きます。ただし、line や action で通知を作るかわりに、markdown で、使う Markdown のテンプレートの名前を決めます。テンプレートで使いたいデータの配列は、第2引数に渡せます。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->subject('Invoice Paid')
->markdown('mail.invoice.paid', ['url' => $url]);
}
メッセージを書く#
Markdown のメールの通知は、Blade のコンポーネント(何度も使う画面の部品)と Markdown の書き方を組み合わせて書きます。Laravel が用意した通知の部品を使いながら、簡単に通知を作れます。
<x-mail::message>
# Invoice Paid
Your invoice has been paid!
<x-mail::button :url="$url">
View Invoice
</x-mail::button>
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
補足
Markdown のメールでは、字下げをつけすぎないでください。Markdown の決まりで、字下げした内容はコードのブロックとして表示されてしまいます。
使える部品は、次のとおりです。
| 部品 | 説明 |
|---|---|
x-mail::message |
メール全体を包む |
x-mail::button |
中央に置くボタンのリンク |
x-mail::panel |
目立たせたい文章を、背景色をかえた枠に入れる |
x-mail::table |
Markdown の表を HTML の表に変える |
ボタン#
ボタンの部品は、中央にボタンのリンクを出します。引数は、url と、省略できる color です。使える色は、primary・green・red です。1つの通知に、ボタンをいくつでも置けます。
<x-mail::button :url="$url" color="green">
View Invoice
</x-mail::button>
パネル#
パネルの部品は、決まったかたまりの文章を、通知のほかの部分と少し背景色のちがう枠の中に出します。目立たせたい文章に向いています。
<x-mail::panel>
This is the panel content.
</x-mail::panel>
表#
表の部品は、Markdown の表を HTML の表に変えます。中身に Markdown の表を渡します。列の位置(左・中央・右)は、ふつうの Markdown の表の書き方で決められます。
<x-mail::table>
| Laravel | Table | Example |
| ------------- | :-----------: | ------------: |
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
</x-mail::table>
部品を書きかえる#
Markdown の通知の部品は、全部を自分のアプリに書き出して、書きかえられます。vendor:publish の Artisan コマンドで、laravel-mail のタグがついたファイルを書き出します。
php artisan vendor:publish --tag=laravel-mail
このコマンドは、Markdown のメールの部品を resources/views/vendor/mail に書き出します。mail フォルダには、html と text のフォルダがあり、使えるすべての部品の HTML 版とテキスト版が入っています。好きなように書きかえられます。
CSS を書きかえる#
書き出したあと、resources/views/vendor/mail/html/themes に、default.css があります。この CSS を書きかえると、Markdown の通知の HTML 版に、自動で反映されます。そのとき CSS は、インライン CSS(要素に直接書く CSS)の形で入ります。
Laravel の Markdown の部品のために、まったく新しいテーマを作りたいときは、html/themes に CSS ファイルを置きます。名前をつけて保存したら、mail 設定の theme を、その新しいテーマの名前にします。
通知1つだけテーマを変えたいときは、通知のメールのメッセージを作るとき、theme を呼びます。theme は、通知を送るときに使うテーマの名前を受け取ります。
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->theme('invoice')
->subject('Invoice Paid')
->markdown('mail.invoice.paid', ['url' => $url]);
}
データベースの通知#
準備#
database チャンネルは、通知の情報を、データベースの表に保存します。この表には、通知の種類や、通知を説明する JSON のデータが入ります。
この表を調べると、アプリの画面に通知を表示できます。ただし、その前に、通知を入れるデータベースの表を作る必要があります。make:notifications-table コマンドで、正しい表の形をしたマイグレーション(表を作る手順書)を作れます。
php artisan make:notifications-table
php artisan migrate
補足
通知を受け取るモデルが、UUID や ULID の主キー(ほかと重ならない文字の ID)を使っているなら、通知の表のマイグレーションで、morphs メソッドを uuidMorphs か ulidMorphs に書きかえてください。
データベースの通知の形を決める#
通知をデータベースの表に保存できるようにするには、通知のクラスに toDatabase か toArray を書きます。このメソッドは、$notifiable を受け取り、ふつうの PHP の配列を返します。返した配列は JSON に変えられ、notifications の表の data の列に保存されます。toArray の例を見てみましょう。
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
通知をアプリのデータベースに保存するとき、type の列には、既定では通知のクラス名が入り、read_at の列は null になります。通知のクラスに databaseType と initialDatabaseReadAtValue のメソッドを書くと、この動きを変えられます。
use Illuminate\Support\Carbon;
/**
* Get the notification's database type.
*/
public function databaseType(object $notifiable): string
{
return 'invoice-paid';
}
/**
* Get the initial value for the "read_at" column.
*/
public function initialDatabaseReadAtValue(): ?Carbon
{
return null;
}
toDatabase と toArray のちがい#
toArray は、broadcast チャンネルでも、JavaScript の画面へ送るデータを決めるのに使われます。database と broadcast で、配列の中身を変えたいときは、toArray のかわりに toDatabase を書きます。
通知を取り出す#
通知をデータベースに保存したら、通知を受け取る側から、簡単に取り出せる方法が要ります。Laravel の App\Models\User モデルに最初から入っている Illuminate\Notifications\Notifiable トレイトには、その相手の通知を返す notifications という Eloquent のリレーション(表どうしのつながり)があります。通知を取り出すには、ほかの Eloquent のリレーションと同じように使います。既定では、created_at(作られた日時)の新しい順に並びます。
$user = App\Models\User::find(1);
foreach ($user->notifications as $notification) {
echo $notification->type;
}
「未読」の通知だけを取り出すには、unreadNotifications のリレーションを使います。こちらも、created_at の新しい順に並びます。
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
echo $notification->type;
}
「既読」の通知だけを取り出すには、readNotifications のリレーションを使います。
$user = App\Models\User::find(1);
foreach ($user->readNotifications as $notification) {
echo $notification->type;
}
補足
JavaScript から通知を取り出したいなら、現在のユーザーのように、通知を受け取る相手の通知を返す、通知用のコントローラーを作ります。そのコントローラーの URL に、JavaScript から HTTP のリクエストを送ります。
| リレーション | 説明 |
|---|---|
notifications |
すべての通知(新しい順) |
unreadNotifications |
未読の通知だけ |
readNotifications |
既読の通知だけ |
通知を既読にする#
ふつう、利用者が通知を見たとき、その通知を「既読」にしたいはずです。Illuminate\Notifications\Notifiable トレイトには markAsRead があり、通知のデータベースの記録の read_at の列を更新します。
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
通知を1つずつ回すかわりに、通知のコレクションに、そのまま markAsRead を使えます。
$user->unreadNotifications->markAsRead();
データベースから取り出さずに、まとめて更新する SQL(一括更新)で、全部の通知を既読にもできます。
$user = App\Models\User::find(1);
$user->unreadNotifications()->update(['read_at' => now()]);
通知を delete すれば、表から完全に消せます。
$user->notifications()->delete();
ブラウザへ届ける通知(ブロードキャスト)#
準備#
通知をブロードキャスト(まとめて届けること)する前に、Laravel のブロードキャストのしくみを設定し、使い方を知っておいてください。ブロードキャストは、サーバー側で起きた Laravel のイベントに、JavaScript で動く画面から応えるしくみです。
ブロードキャストの通知の形を決める#
broadcast チャンネルは、Laravel のイベントのブロードキャストのしくみで通知を届けます。JavaScript で動く画面は、通知をリアルタイムに受け取れます。通知をブロードキャストできるようにするには、通知のクラスに toBroadcast を書きます。このメソッドは、$notifiable を受け取り、BroadcastMessage を返します。toBroadcast がなければ、toArray が、届けるデータを集めるのに使われます。返したデータは JSON に変えられて、JavaScript の画面へ届けられます。toBroadcast の例を見てみましょう。
use Illuminate\Notifications\Messages\BroadcastMessage;
/**
* Get the broadcastable representation of the notification.
*/
public function toBroadcast(object $notifiable): BroadcastMessage
{
return new BroadcastMessage([
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
]);
}
ブロードキャストのキューの設定#
ブロードキャストの通知は、すべてキューに入ります。ブロードキャストの処理をキューに入れるときの接続やキューの名前を決めたいなら、BroadcastMessage の onConnection と onQueue を使います。
return (new BroadcastMessage($data))
->onConnection('sqs')
->onQueue('broadcasts');
通知の種類を決める#
ブロードキャストの通知には、決めたデータのほかに、通知のクラスの完全な名前を入れた type の項目もつきます。この type を変えたいときは、通知のクラスに broadcastType メソッドを書きます。
/**
* Get the type of the notification being broadcast.
*/
public function broadcastType(): string
{
return 'broadcast.message';
}
通知を聞く#
通知は、{notifiable}.{id} の決まりで名づけた、プライベートなチャンネル(決まった人しか聞けないチャンネル)で届けられます。たとえば、ID が 1 の App\Models\User に通知を送るなら、App.Models.User.1 のプライベートなチャンネルで届きます。Laravel Echo(ブロードキャストを受け取る JavaScript の道具)を使うなら、notification メソッドで、チャンネルの通知を簡単に聞けます。
Echo.private('App.Models.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
React・Vue・Svelte で使う#
Laravel Echo には、React・Vue・Svelte 用のフック(部品の中で使う関数)があり、通知を簡単に聞けます。通知を聞くには、useEchoNotification を呼びます。この部品が画面から外れると、フックは自動でチャンネルから離れます。
React の場合です。
import { useEchoNotification } from "@laravel/echo-react";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
);
Vue の場合です。
<script setup lang="ts">
import { useEchoNotification } from "@laravel/echo-vue";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
);
</script>
Svelte の場合です。
<script>
import { useEchoNotification } from "@laravel/echo-svelte";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
);
</script>
既定では、フックはすべての通知を聞きます。聞きたい通知の種類を決めるには、useEchoNotification に、種類を表す文字列か、その配列を渡します。
React の場合です。
import { useEchoNotification } from "@laravel/echo-react";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
'App.Notifications.InvoicePaid',
);
Vue の場合です。
<script setup lang="ts">
import { useEchoNotification } from "@laravel/echo-vue";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
'App.Notifications.InvoicePaid',
);
</script>
Svelte の場合です。
<script>
import { useEchoNotification } from "@laravel/echo-svelte";
useEchoNotification(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.type);
},
'App.Notifications.InvoicePaid',
);
</script>
通知のデータの形(型)も決められます。型の安全さと、書きやすさが上がります。
type InvoicePaidNotification = {
invoice_id: number;
created_at: string;
};
useEchoNotification<InvoicePaidNotification>(
`App.Models.User.${userId}`,
(notification) => {
console.log(notification.invoice_id);
console.log(notification.created_at);
console.log(notification.type);
},
'App.Notifications.InvoicePaid',
);
通知のチャンネルを変える#
あるモデルのブロードキャストの通知が届くチャンネルを変えたいときは、通知を受け取る側のモデルに、receivesBroadcastNotificationsOn メソッドを書きます。
<?php
namespace App\Models;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The channels the user receives notification broadcasts on.
*/
public function receivesBroadcastNotificationsOn(): string
{
return 'users.'.$this->id;
}
}
SMS の通知#
準備#
Laravel の SMS の通知は、Vonage(以前の名前は Nexmo)が支えています。Vonage で通知を送る前に、laravel/vonage-notification-channel と guzzlehttp/guzzle のパッケージを、Composer(PHP の部品を入れる道具)で入れます。
composer require laravel/vonage-notification-channel guzzlehttp/guzzle
このパッケージには、設定ファイルがあります。ただし、その設定ファイルを自分のアプリに書き出す必要はありません。環境変数(環境ごとに変える設定値)の VONAGE_KEY と VONAGE_SECRET で、Vonage の公開鍵と秘密鍵を決められます。
鍵を決めたら、環境変数 VONAGE_SMS_FROM で、SMS を送るときにふつう使う電話番号を決めます。この電話番号は、Vonage の管理画面で作れます。
VONAGE_SMS_FROM=15556666666
SMS の通知の形を決める#
通知を SMS で送れるようにするには、通知のクラスに toVonage メソッドを書きます。このメソッドは、$notifiable を受け取り、Illuminate\Notifications\Messages\VonageMessage を返します。
use Illuminate\Notifications\Messages\VonageMessage;
/**
* Get the Vonage / SMS representation of the notification.
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('Your SMS message content');
}
Unicode の文字#
SMS に Unicode の文字(日本語などの、英数字以外の文字)が入るなら、VonageMessage を作るとき、unicode を呼びます。
use Illuminate\Notifications\Messages\VonageMessage;
/**
* Get the Vonage / SMS representation of the notification.
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('Your unicode message')
->unicode();
}
送り元の番号を変える#
環境変数 VONAGE_SMS_FROM で決めた番号とはちがう電話番号から、ある通知を送りたいときは、VonageMessage の from を呼びます。
use Illuminate\Notifications\Messages\VonageMessage;
/**
* Get the Vonage / SMS representation of the notification.
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('Your SMS message content')
->from('15554443333');
}
クライアントの参照をつける#
利用者・チーム・お客さんごとに費用を管理したいなら、通知に「クライアントの参照」(client reference)をつけられます。Vonage では、この参照でレポートを作れるので、お客さんごとの SMS の使い方が分かりやすくなります。クライアントの参照は、40文字までの好きな文字列です。
use Illuminate\Notifications\Messages\VonageMessage;
/**
* Get the Vonage / SMS representation of the notification.
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->clientReference((string) $notifiable->id)
->content('Your SMS message content');
}
SMS の宛先を決める#
Vonage の通知を正しい電話番号へ届けるには、通知を受け取る側のモデルに、routeNotificationForVonage メソッドを書きます。
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Notifications\Notification;
class User extends Authenticatable
{
use Notifiable;
/**
* Route notifications for the Vonage channel.
*/
public function routeNotificationForVonage(Notification $notification): string
{
return $this->phone_number;
}
}
Slack の通知#
準備#
Slack の通知を送る前に、Slack の通知のチャンネルを Composer で入れます。
composer require laravel/slack-notification-channel
さらに、自分の Slack のワークスペース(チームの作業場所)に、Slack App(Slack に機能を足すアプリ)を作る必要があります。
App を作ったのと同じ Slack のワークスペースにだけ通知を送るなら、App に、chat:write・chat:write.public・chat:write.customize のスコープ(できることの範囲)があることを確かめてください。これらのスコープは、Slack の App の管理画面の「OAuth & Permissions」のタブで足せます。
次に、App の「Bot User OAuth Token」をコピーして、アプリの services.php の slack の配列に置きます。このトークン(Slack を使うための合言葉のような文字列)は、Slack の「OAuth & Permissions」のタブで見つけられます。
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
App を配る#
アプリが、利用者が持つ外部の Slack のワークスペースにも通知を送るなら、Slack で App を「配る」(distribute)必要があります。App の配布は、Slack の App の「Manage Distribution」のタブで管理できます。App を配ったら、Socialite(外のサービスでログインさせる Laravel の部品)を使って、アプリの利用者のかわりに、Slack の Bot(自動で動く Slack の利用者)のトークンを取れます。
Slack の通知の形を決める#
通知を Slack のメッセージとして送れるようにするには、通知のクラスに toSlack メソッドを書きます。このメソッドは、$notifiable を受け取り、Illuminate\Notifications\Slack\SlackMessage を返します。Slack の Block Kit API を使うと、見栄えのよい通知を作れます。次の例は、Slack の Block Kit Builder(ブロックを組み立てて試せる画面)でも試せます。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
use Illuminate\Notifications\Slack\SlackMessage;
/**
* Get the Slack representation of the notification.
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('One of your invoices has been paid!')
->headerBlock('Invoice Paid')
->contextBlock(function (ContextBlock $block) {
$block->text('Customer #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('An invoice has been paid.');
$block->field("*Invoice No:*\n1000")->markdown();
$block->field("*Invoice Recipient:*\ntaylor@laravel.com")->markdown();
})
->dividerBlock()
->sectionBlock(function (SectionBlock $block) {
$block->text('Congratulations!');
});
}
Block Kit Builder のテンプレートを使う#
Block Kit のメッセージを、メソッドをつなげて作るかわりに、Slack の Block Kit Builder が作った生の JSON を、usingBlockKitTemplate に渡せます。
use Illuminate\Notifications\Slack\SlackMessage;
use Illuminate\Support\Str;
/**
* Get the Slack representation of the notification.
*/
public function toSlack(object $notifiable): SlackMessage
{
$template = <<<JSON
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Team Announcement"
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "We are hiring!"
}
}
]
}
JSON;
return (new SlackMessage)
->usingBlockKitTemplate($template);
}
Slack の操作に応える#
Slack の Block Kit の通知のしくみには、利用者の操作を扱う強力な機能があります。この機能を使うには、Slack の App で「Interactivity」を有効にし、アプリが公開している URL を指す「Request URL」を決めます。これらの設定は、Slack の App の管理画面の「Interactivity & Shortcuts」のタブで管理できます。
次の例は、actionsBlock を使っています。利用者がボタンを押すと、Slack が「Request URL」に POST のリクエストを送ります。そこには、ボタンを押した Slack のユーザーや、押されたボタンの ID などが入っています。アプリは、その内容から、どうするかを決められます。また、リクエストが本当に Slack から来たものかを、確かめてください。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
use Illuminate\Notifications\Slack\SlackMessage;
/**
* Get the Slack representation of the notification.
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('One of your invoices has been paid!')
->headerBlock('Invoice Paid')
->contextBlock(function (ContextBlock $block) {
$block->text('Customer #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('An invoice has been paid.');
})
->actionsBlock(function (ActionsBlock $block) {
// ID defaults to "button_acknowledge_invoice"...
$block->button('Acknowledge Invoice')->primary();
// Manually configure the ID...
$block->button('Deny')->danger()->id('deny_invoice');
});
}
確認の画面#
操作を実行する前に、利用者に確かめさせたいときは、ボタンを作るとき、confirm を呼びます。confirm は、メッセージと、ConfirmObject を受け取るクロージャを受け取ります。
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;
use Illuminate\Notifications\Slack\SlackMessage;
/**
* Get the Slack representation of the notification.
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('One of your invoices has been paid!')
->headerBlock('Invoice Paid')
->contextBlock(function (ContextBlock $block) {
$block->text('Customer #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('An invoice has been paid.');
})
->actionsBlock(function (ActionsBlock $block) {
$block->button('Acknowledge Invoice')
->primary()
->confirm(
'Acknowledge the payment and send a thank you email?',
function (ConfirmObject $dialog) {
$dialog->confirm('Yes');
$dialog->deny('No');
}
);
});
}
ブロックを調べる#
作っているブロックをすばやく調べたいときは、SlackMessage の dd を呼びます。dd は、Slack の Block Kit Builder の URL を作って表示します。ブラウザで、メッセージの見た目と、中身の確認ができます。dd に true を渡すと、生の中身を表示します。
return (new SlackMessage)
->text('One of your invoices has been paid!')
->headerBlock('Invoice Paid')
->dd();
Slack の宛先を決める#
Slack の通知を、正しい Slack のチームとチャンネルへ届けるには、通知を受け取る側のモデルに、routeNotificationForSlack メソッドを書きます。このメソッドは、次の3つのどれかを返せます。
null:宛先の決め方を、通知そのものに任せます。SlackMessageを作るとき、toで、通知の中でチャンネルを決められます- Slack のチャンネルを表す文字列:たとえば
#support-channelです SlackRouteのインスタンス:OAuth のトークン(利用者の許しを得て受け取る、Slack を使うための鍵)とチャンネルの名前を決められます。たとえばSlackRoute::make($this->slack_channel, $this->slack_token)です。外部のワークスペースに通知を送るときに使います
たとえば、routeNotificationForSlack から #support-channel を返すと、アプリの services.php にある Bot User OAuth Token と結びついたワークスペースの、#support-channel に通知が送られます。
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Notifications\Notification;
class User extends Authenticatable
{
use Notifiable;
/**
* Route notifications for the Slack channel.
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return '#support-channel';
}
}
外部の Slack のワークスペースに通知する#
補足
外部の Slack のワークスペースに通知を送る前に、Slack の App を「配って」(distribute)おく必要があります。
アプリの利用者が持つ Slack のワークスペースへ、通知を送りたいことは多いでしょう。そのためには、まず、その利用者の Slack の OAuth のトークンを手に入れる必要があります。Laravel Socialite には、Slack のドライバーがあります。アプリの利用者を Slack で認証して、Bot のトークンを簡単に取れます。
Bot のトークンを手に入れてアプリのデータベースに保存したら、SlackRoute::make で、通知を利用者のワークスペースへ送れます。また、通知をどのチャンネルへ送るかを、利用者に選ばせる画面も、たぶん必要になります。
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Slack\SlackRoute;
class User extends Authenticatable
{
use Notifiable;
/**
* Route notifications for the Slack channel.
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return SlackRoute::make($this->slack_channel, $this->slack_token);
}
}
通知を多言語にする#
Laravel では、HTTP のリクエストの今のロケール(言語の設定)とは別のロケールで、通知を送れます。キューに入れたときも、そのロケールを覚えています。
そのために、Illuminate\Notifications\Notification には、locale があります。送りたい言語を決めます。通知の中身を作るあいだだけ、アプリがそのロケールに切りかわり、終わったら、前のロケールに戻ります。
$user->notify((new InvoicePaid($invoice))->locale('es'));
通知を受け取る相手が複数いるときも、Notification ファサードで、多言語にできます。
Notification::locale('es')->send(
$users, new InvoicePaid($invoice)
);
利用者が選んだ言語を使う#
利用者ごとに、選んだ言語を保存しているアプリもあります。通知を受け取る側のモデルに HasLocalePreference を付けると、保存されたその言語を、通知を送るときに使うよう、Laravel に伝えられます。
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Get the user's preferred locale.
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
このインターフェイスを付けると、そのモデルに通知やメイラブルを送るとき、Laravel が自動で、選ばれた言語を使います。そのため、このインターフェイスを使うときは、locale を呼ぶ必要はありません。
$user->notify(new InvoicePaid($invoice));
テスト#
Notification ファサードの fake を呼ぶと、通知が実際には送られなくなります。ふつう、通知を送ることは、テストしたいコードと関係がありません。Laravel に「その通知を送るように言った」ことだけ確かめれば、たいてい足ります。
fake を呼んだあと、通知がユーザーに送られることになったかや、通知が受け取ったデータまで、アサーション(「こうなっているはず」を確かめる命令)で調べられます。
Pest(PHP のテストの道具)で書く場合です。
<?php
use App\Notifications\OrderShipped;
use Illuminate\Support\Facades\Notification;
test('orders can be shipped', function () {
Notification::fake();
// Perform order shipping...
// Assert that no notifications were sent...
Notification::assertNothingSent();
// Assert a notification was sent to the given users...
Notification::assertSentTo(
[$user], OrderShipped::class
);
// Assert a notification was not sent...
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// Assert a notification was sent twice...
Notification::assertSentTimes(WeeklyReminder::class, 2);
// Assert that a notification was sent to a user exactly once...
Notification::assertSentToOnce($user, OrderShipped::class);
// Assert that a given number of notifications were sent...
Notification::assertCount(3);
});
PHPUnit(PHP のテストの道具)で書く場合です。
<?php
namespace Tests\Feature;
use App\Notifications\OrderShipped;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped(): void
{
Notification::fake();
// Perform order shipping...
// Assert that no notifications were sent...
Notification::assertNothingSent();
// Assert a notification was sent to the given users...
Notification::assertSentTo(
[$user], OrderShipped::class
);
// Assert a notification was not sent...
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// Assert a notification was sent twice...
Notification::assertSentTimes(WeeklyReminder::class, 2);
// Assert that a notification was sent to a user exactly once...
Notification::assertSentToOnce($user, OrderShipped::class);
// Assert that a given number of notifications were sent...
Notification::assertCount(3);
}
}
assertSentTo と assertNotSentTo には、条件を書いたクロージャ(true か false を返す関数)を渡せます。条件に合う通知が1つでも送られていれば、成功です。
Notification::assertSentTo(
$user,
function (OrderShipped $notification, array $channels) use ($order) {
return $notification->order->id === $order->id;
}
);
オンデマンドの通知のテスト#
テストするコードが、前に説明した「その場で決めた宛先に送る通知」を送るなら、assertSentOnDemand で、その通知が送られたことを確かめられます。
Notification::assertSentOnDemand(OrderShipped::class);
Notification::assertSentOnDemandOnce(OrderShipped::class);
assertSentOnDemand の第2引数にクロージャを渡すと、オンデマンドの通知が、正しい宛先(ルート)に送られたかを確かめられます。
Notification::assertSentOnDemand(
OrderShipped::class,
function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
return $notifiable->routes['mail'] === $user->email;
}
);
Notification のアサーションは、次のとおりです。
| メソッド | 説明 |
|---|---|
assertNothingSent |
通知が1つも送られていないことを確かめる |
assertSentTo |
決めた相手に通知が送られたことを確かめる |
assertNotSentTo |
決めた相手に通知が送られていないことを確かめる |
assertSentTimes |
通知が決めた回数送られたことを確かめる |
assertSentToOnce |
決めた相手に通知がちょうど1回送られたことを確かめる |
assertCount |
送られた通知の総数を確かめる |
assertSentOnDemand |
オンデマンドの通知が送られたことを確かめる |
assertSentOnDemandOnce |
オンデマンドの通知がちょうど1回送られたことを確かめる |
通知のイベント#
送るときのイベント#
通知を送るとき、通知のしくみが Illuminate\Notifications\Events\NotificationSending というイベントを出します。このイベントには、「通知を受け取る相手」と、通知のインスタンスが入っています。アプリの中で、このイベントのリスナーを作れます。
use Illuminate\Notifications\Events\NotificationSending;
class CheckNotificationStatus
{
/**
* Handle the event.
*/
public function handle(NotificationSending $event): void
{
// ...
}
}
NotificationSending のリスナーが、handle から false を返すと、その通知は送られません。
/**
* Handle the event.
*/
public function handle(NotificationSending $event): bool
{
return false;
}
リスナーの中では、イベントの notifiable・notification・channel のプロパティで、通知の受け取り相手や通知そのものについて、くわしく知れます。
/**
* Handle the event.
*/
public function handle(NotificationSending $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
}
送ったあとのイベント#
通知を送ったとき、通知のしくみが Illuminate\Notifications\Events\NotificationSent というイベントを出します。このイベントにも、「通知を受け取る相手」と、通知のインスタンスが入っています。アプリの中で、このイベントのリスナーを作れます。
use Illuminate\Notifications\Events\NotificationSent;
class LogNotification
{
/**
* Handle the event.
*/
public function handle(NotificationSent $event): void
{
// ...
}
}
リスナーの中では、イベントの notifiable・notification・channel・response のプロパティで、通知の受け取り相手や通知そのものについて、くわしく知れます。
/**
* Handle the event.
*/
public function handle(NotificationSent $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
// $event->response
}
| イベント | 出るとき | 使えるプロパティ |
|---|---|---|
NotificationSending |
通知を送るとき(false を返すと送らない) |
channel・notifiable・notification |
NotificationSent |
通知を送ったとき | 上の3つと response |
自分で作るチャンネル(カスタムチャンネル)#
Laravel には、いくつかの通知のチャンネルが入っています。ほかのチャンネルで通知を届けるために、自分でドライバー(送り方)を書きたくなることもあるでしょう。Laravel なら簡単です。まず、send メソッドを持つクラスを作ります。このメソッドは、$notifiable と $notification の2つの引数を受け取ります。
send の中で、通知のメソッドを呼んで、そのチャンネルが分かるメッセージのオブジェクトを取り出し、好きな方法で、$notifiable へ通知を送ります。
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class VoiceChannel
{
/**
* Send the given notification.
*/
public function send(object $notifiable, Notification $notification): void
{
$message = $notification->toVoice($notifiable);
// Send notification to the $notifiable instance...
}
}
通知のチャンネルのクラスを作ったら、どの通知の via からでも、そのクラス名を返せます。この例の toVoice は、音声メッセージを表すオブジェクトなら、自分で決めたものを何でも返せます。たとえば、音声メッセージを表す VoiceMessage クラスを、自分で作れます。
<?php
namespace App\Notifications;
use App\Notifications\Messages\VoiceMessage;
use App\Notifications\VoiceChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification
{
use Queueable;
/**
* Get the notification channels.
*/
public function via(object $notifiable): string
{
return VoiceChannel::class;
}
/**
* Get the voice representation of the notification.
*/
public function toVoice(object $notifiable): VoiceMessage
{
// ...
}
}
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。