本文へ移動
Laravel Tips

通知

「請求書が支払われました」のような短い知らせを、メール・データベース・ブラウザ・SMS・Slack などへ送る通知のしくみと、その書き方・キュー・テストを説明します。

通知は、アプリの中で起きたことを、利用者へ短く知らせるしくみです。郵便・電話・掲示板のように、知らせる手段(チャンネル)がいくつもあり、1つの通知を、複数のチャンネルで送れます。Laravel は、メールのほか、SMS(Vonage というサービスを使います。以前の名前は Nexmo)と Slack で通知を送れます。コミュニティ(Laravel を使う人たちの集まり)が作ったチャンネルも、数十種類あります。通知をデータベースに保存して、画面に表示することもできます。

通知は、ふつう、アプリで起きたことを知らせる短い文です。たとえば、請求のアプリなら、「請求書が支払われました」という通知を、メールと SMS で利用者に送るような使い方をします。

通知を作る#

Laravel では、通知1種類ごとに、1つのクラスを作ります。クラスは、ふつう app/Notifications に置きます。フォルダが見つからなくても大丈夫です。make:notification コマンドを実行すると、自動で作られます。

bash
php artisan make:notification InvoicePaid

このコマンドは、新しい通知のクラスを app/Notifications に作ります。通知のクラスには、via メソッドと、toMail や toDatabase のような、メッセージを作るメソッドがいくつか入ります。メッセージを作るメソッドは、通知を、そのチャンネルに合った形のメッセージに変えます。

通知を送る#

Notifiable トレイトで送る#

通知を送る方法は2つあります。Notifiable トレイト(いくつものクラスに同じ機能を足す部品)の notify を使う方法と、Notification ファサード(Notification:: と書いて機能を呼べる窓口)を使う方法です。Notifiable は、アプリの App\Models\User モデルに、最初から入っています。

php
<?php

namespace App\Models;

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

class User extends Authenticatable
{
    use Notifiable;
}

このトレイトの notify は、通知のインスタンス(クラスから作った実物)を受け取ります。

php
use App\Notifications\InvoicePaid;

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

補足

Notifiable トレイトは、どのモデルにも付けられます。User モデルだけに付けるものではありません。

Notification ファサードで送る#

Notification ファサードでも送れます。ユーザーのコレクション(入れ物)のように、通知を受け取る相手が複数いるときに便利です。ファサードで送るには、受け取る相手全員と、通知のインスタンスを、send に渡します。

php
use Illuminate\Support\Facades\Notification;

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

sendNow を使うと、すぐに送れます。通知に ShouldQueue が付いていても、キューに入れずに送ります。

php
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 を使って、どのチャンネルで送るかを決められます。

php
/**
 * 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
<?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 に気づいて、自動で、通知の配信をキューに入れます。

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

通知をキューに入れると、受け取る相手とチャンネルの組み合わせごとに、キューのジョブ(キューに並べる1つ1つの仕事)が1つ作られます。たとえば、受け取る相手が3人、チャンネルが2つなら、6つのジョブがキューに送られます。

通知を少し待たせる#

通知を送るのを遅らせたいときは、通知のインスタンスに delay をつなげます。

php
$delay = now()->plus(minutes: 10);

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

delay に配列を渡すと、チャンネルごとに待ち時間を決められます。

php
$user->notify((new InvoicePaid($invoice))->delay([
    'mail' => now()->plus(minutes: 5),
    'sms' => now()->plus(minutes: 10),
]));

通知のクラスに withDelay メソッドを書いても決められます。チャンネルの名前と待ち時間の配列を返します。

php
/**
 * 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
<?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 メソッドを書きます。チャンネルの名前とキュー接続の名前の組の配列を返します。

php
/**
 * Determine which connections should be used for each notification channel.
 *
 * @return array<string, string>
 */
public function viaConnections(): array
{
    return [
        'mail' => 'redis',
        'database' => 'sync',
    ];
}

チャンネルごとのキューを決める#

チャンネルごとに別のキューを使いたいときは、通知に viaQueues メソッドを書きます。チャンネルの名前とキューの名前の組の配列を返します。

php
/**
 * 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
<?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
<?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 のメソッドで、再挑戦までの待ち方と、再挑戦の期限を決められます。

php
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 を受け取るので、通知の送り先によって、返すミドルウェアを変えられます。

php
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 を呼べば、開いているトランザクションがすべて確定したあとで、その通知を送れます。

php
use App\Notifications\InvoicePaid;

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

通知のコンストラクターの中で、afterCommit を呼んでも決められます。

php
<?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 を返すと、通知は送られません。

php
/**
 * Determine if the notification should be sent.
 */
public function shouldSend(object $notifiable, string $channel): bool
{
    return $this->invoice->isPaid();
}

送ったあとに動かす#

通知を送ったあとに動かしたい処理があるなら、通知のクラスに afterSending メソッドを書きます。このメソッドは、受け取る相手・チャンネルの名前・チャンネルからの返事を受け取ります。

php
/**
 * Handle the notification after it has been sent.
 */
public function afterSending(object $notifiable, string $channel, mixed $response): void
{
    // ...
}

その場で決めた宛先に送る(オンデマンドの通知)#

アプリの「ユーザー」として保存されていない人に、通知を送りたいことがあります。Notification ファサードの route で、送る前に、その場かぎりの宛先を決められます。

php
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 の宛先に、相手の名前も書きたいときは、配列の最初の要素に、メールアドレスをキー、名前を値にした配列を渡します。

php
Notification::route('mail', [
    'barrett@example.com' => 'Barrett Blair',
])->notify(new InvoicePaid($invoice));

routes を使うと、複数のチャンネルの宛先を、まとめて決められます。

php
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 の例を見てみましょう。

php
/**
 * 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 を使うと、ボタンの色が、黒ではなく赤になります。

php
/**
 * Get the mail representation of the notification.
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->error()
        ->subject('Invoice Payment Failed')
        ->line('...');
}

そのほかの形の決め方#

通知のクラスに「文章の行」を書くかわりに、view で、メールを作る自分のテンプレートを決められます。

php
/**
 * 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番目の要素にビューの名前を書くと、テキスト版のビューも決められます。

php
/**
 * 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 を使います。

php
/**
 * 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 を使います。

php
/**
 * 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
<?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 を呼びます。

php
/**
 * Get the mail representation of the notification.
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->subject('Notification Subject')
        ->line('...');
}

メイラーを決める#

既定では、メールの通知は、config/mail.php に決めてある既定のメイラーで送られます。送るときに別のメイラーを使いたいときは、メッセージを作るとき、mailer を呼びます。

php
/**
 * Get the mail representation of the notification.
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->mailer('postmark')
        ->line('...');
}

テンプレートを書きかえる#

メールの通知が使う HTML とテキストのテンプレートは、通知のパッケージのファイルを書き出すと、書きかえられます。このコマンドを実行したあと、メールの通知のテンプレートは、resources/views/vendor/notifications に置かれます。

bash
php artisan vendor:publish --tag=laravel-notifications

添付ファイル#

メールの通知にファイルを添付するには、メッセージを作るとき、attach を使います。第1引数に、ファイルの絶対パス(いちばん上から書いた場所)を渡します。

php
/**
 * 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 タイプ(ファイルの種類を表す名前)も決められます。

php
/**
 * 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 を使います。

php
/**
 * 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 を使います。このメソッドは、ディスクの名前と、そのディスクの中のファイルの場所を受け取ります。

php
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 には、添付ファイルにつけるファイル名を渡します。

php
/**
 * 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 で、タグとメタデータをつけられます。

php
/**
 * 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 のメッセージを受け取って動くクロージャ(名前のない関数)を登録できます。送る前にメールを細かく調整できます。

php
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 で、宛先を決める必要があります。

php
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 があります。

php
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 に組み立てられて、ブラウザに表示されます。本物のメールアドレスに送らなくても、見た目をすぐに確かめられます。

php
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 オプションを使います。

bash
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid

ほかのメールの通知と同じように、Markdown のテンプレートを使う通知も、通知のクラスに toMail を書きます。ただし、line や action で通知を作るかわりに、markdown で、使う Markdown のテンプレートの名前を決めます。テンプレートで使いたいデータの配列は、第2引数に渡せます。

php
/**
 * 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 が用意した通知の部品を使いながら、簡単に通知を作れます。

blade
<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つの通知に、ボタンをいくつでも置けます。

blade
<x-mail::button :url="$url" color="green">
View Invoice
</x-mail::button>

パネル#

パネルの部品は、決まったかたまりの文章を、通知のほかの部分と少し背景色のちがう枠の中に出します。目立たせたい文章に向いています。

blade
<x-mail::panel>
This is the panel content.
</x-mail::panel>

表#

表の部品は、Markdown の表を HTML の表に変えます。中身に Markdown の表を渡します。列の位置(左・中央・右)は、ふつうの Markdown の表の書き方で決められます。

blade
<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 のタグがついたファイルを書き出します。

bash
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 は、通知を送るときに使うテーマの名前を受け取ります。

php
/**
 * 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 コマンドで、正しい表の形をしたマイグレーション(表を作る手順書)を作れます。

bash
php artisan make:notifications-table

php artisan migrate

補足

通知を受け取るモデルが、UUID や ULID の主キー(ほかと重ならない文字の ID)を使っているなら、通知の表のマイグレーションで、morphs メソッドを uuidMorphs か ulidMorphs に書きかえてください。

データベースの通知の形を決める#

通知をデータベースの表に保存できるようにするには、通知のクラスに toDatabase か toArray を書きます。このメソッドは、$notifiable を受け取り、ふつうの PHP の配列を返します。返した配列は JSON に変えられ、notifications の表の data の列に保存されます。toArray の例を見てみましょう。

php
/**
 * 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 のメソッドを書くと、この動きを変えられます。

php
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(作られた日時)の新しい順に並びます。

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

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

「未読」の通知だけを取り出すには、unreadNotifications のリレーションを使います。こちらも、created_at の新しい順に並びます。

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

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

「既読」の通知だけを取り出すには、readNotifications のリレーションを使います。

php
$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 の列を更新します。

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

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

通知を1つずつ回すかわりに、通知のコレクションに、そのまま markAsRead を使えます。

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

データベースから取り出さずに、まとめて更新する SQL(一括更新)で、全部の通知を既読にもできます。

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

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

通知を delete すれば、表から完全に消せます。

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

ブラウザへ届ける通知(ブロードキャスト)#

準備#

通知をブロードキャスト(まとめて届けること)する前に、Laravel のブロードキャストのしくみを設定し、使い方を知っておいてください。ブロードキャストは、サーバー側で起きた Laravel のイベントに、JavaScript で動く画面から応えるしくみです。

ブロードキャストの通知の形を決める#

broadcast チャンネルは、Laravel のイベントのブロードキャストのしくみで通知を届けます。JavaScript で動く画面は、通知をリアルタイムに受け取れます。通知をブロードキャストできるようにするには、通知のクラスに toBroadcast を書きます。このメソッドは、$notifiable を受け取り、BroadcastMessage を返します。toBroadcast がなければ、toArray が、届けるデータを集めるのに使われます。返したデータは JSON に変えられて、JavaScript の画面へ届けられます。toBroadcast の例を見てみましょう。

php
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 を使います。

php
return (new BroadcastMessage($data))
    ->onConnection('sqs')
    ->onQueue('broadcasts');

通知の種類を決める#

ブロードキャストの通知には、決めたデータのほかに、通知のクラスの完全な名前を入れた type の項目もつきます。この type を変えたいときは、通知のクラスに broadcastType メソッドを書きます。

php
/**
 * 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 メソッドで、チャンネルの通知を簡単に聞けます。

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

React・Vue・Svelte で使う#

Laravel Echo には、React・Vue・Svelte 用のフック(部品の中で使う関数)があり、通知を簡単に聞けます。通知を聞くには、useEchoNotification を呼びます。この部品が画面から外れると、フックは自動でチャンネルから離れます。

React の場合です。

js
import { useEchoNotification } from "@laravel/echo-react";

useEchoNotification(
    `App.Models.User.${userId}`,
    (notification) => {
        console.log(notification.type);
    },
);

Vue の場合です。

vue
<script setup lang="ts">
import { useEchoNotification } from "@laravel/echo-vue";

useEchoNotification(
    `App.Models.User.${userId}`,
    (notification) => {
        console.log(notification.type);
    },
);
</script>

Svelte の場合です。

svelte
<script>
import { useEchoNotification } from "@laravel/echo-svelte";

useEchoNotification(
    `App.Models.User.${userId}`,
    (notification) => {
        console.log(notification.type);
    },
);
</script>

既定では、フックはすべての通知を聞きます。聞きたい通知の種類を決めるには、useEchoNotification に、種類を表す文字列か、その配列を渡します。

React の場合です。

js
import { useEchoNotification } from "@laravel/echo-react";

useEchoNotification(
    `App.Models.User.${userId}`,
    (notification) => {
        console.log(notification.type);
    },
    'App.Notifications.InvoicePaid',
);

Vue の場合です。

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 の場合です。

svelte
<script>
import { useEchoNotification } from "@laravel/echo-svelte";

useEchoNotification(
    `App.Models.User.${userId}`,
    (notification) => {
        console.log(notification.type);
    },
    'App.Notifications.InvoicePaid',
);
</script>

通知のデータの形(型)も決められます。型の安全さと、書きやすさが上がります。

ts
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
<?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 の部品を入れる道具)で入れます。

bash
composer require laravel/vonage-notification-channel guzzlehttp/guzzle

このパッケージには、設定ファイルがあります。ただし、その設定ファイルを自分のアプリに書き出す必要はありません。環境変数(環境ごとに変える設定値)の VONAGE_KEY と VONAGE_SECRET で、Vonage の公開鍵と秘密鍵を決められます。

鍵を決めたら、環境変数 VONAGE_SMS_FROM で、SMS を送るときにふつう使う電話番号を決めます。この電話番号は、Vonage の管理画面で作れます。

ini
VONAGE_SMS_FROM=15556666666

SMS の通知の形を決める#

通知を SMS で送れるようにするには、通知のクラスに toVonage メソッドを書きます。このメソッドは、$notifiable を受け取り、Illuminate\Notifications\Messages\VonageMessage を返します。

php
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 を呼びます。

php
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 を呼びます。

php
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文字までの好きな文字列です。

php
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
<?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 で入れます。

bash
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」のタブで見つけられます。

php
'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(ブロックを組み立てて試せる画面)でも試せます。

php
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 に渡せます。

php
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 から来たものかを、確かめてください。

php
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 を受け取るクロージャを受け取ります。

php
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 を渡すと、生の中身を表示します。

php
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
<?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
<?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 があります。送りたい言語を決めます。通知の中身を作るあいだだけ、アプリがそのロケールに切りかわり、終わったら、前のロケールに戻ります。

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

通知を受け取る相手が複数いるときも、Notification ファサードで、多言語にできます。

php
Notification::locale('es')->send(
    $users, new InvoicePaid($invoice)
);

利用者が選んだ言語を使う#

利用者ごとに、選んだ言語を保存しているアプリもあります。通知を受け取る側のモデルに HasLocalePreference を付けると、保存されたその言語を、通知を送るときに使うよう、Laravel に伝えられます。

php
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 を呼ぶ必要はありません。

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

テスト#

Notification ファサードの fake を呼ぶと、通知が実際には送られなくなります。ふつう、通知を送ることは、テストしたいコードと関係がありません。Laravel に「その通知を送るように言った」ことだけ確かめれば、たいてい足ります。

fake を呼んだあと、通知がユーザーに送られることになったかや、通知が受け取ったデータまで、アサーション(「こうなっているはず」を確かめる命令)で調べられます。

Pest(PHP のテストの道具)で書く場合です。

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
<?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つでも送られていれば、成功です。

php
Notification::assertSentTo(
    $user,
    function (OrderShipped $notification, array $channels) use ($order) {
        return $notification->order->id === $order->id;
    }
);

オンデマンドの通知のテスト#

テストするコードが、前に説明した「その場で決めた宛先に送る通知」を送るなら、assertSentOnDemand で、その通知が送られたことを確かめられます。

php
Notification::assertSentOnDemand(OrderShipped::class);
Notification::assertSentOnDemandOnce(OrderShipped::class);

assertSentOnDemand の第2引数にクロージャを渡すと、オンデマンドの通知が、正しい宛先(ルート)に送られたかを確かめられます。

php
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 というイベントを出します。このイベントには、「通知を受け取る相手」と、通知のインスタンスが入っています。アプリの中で、このイベントのリスナーを作れます。

php
use Illuminate\Notifications\Events\NotificationSending;

class CheckNotificationStatus
{
    /**
     * Handle the event.
     */
    public function handle(NotificationSending $event): void
    {
        // ...
    }
}

NotificationSending のリスナーが、handle から false を返すと、その通知は送られません。

php
/**
 * Handle the event.
 */
public function handle(NotificationSending $event): bool
{
    return false;
}

リスナーの中では、イベントの notifiable・notification・channel のプロパティで、通知の受け取り相手や通知そのものについて、くわしく知れます。

php
/**
 * Handle the event.
 */
public function handle(NotificationSending $event): void
{
    // $event->channel
    // $event->notifiable
    // $event->notification
}

送ったあとのイベント#

通知を送ったとき、通知のしくみが Illuminate\Notifications\Events\NotificationSent というイベントを出します。このイベントにも、「通知を受け取る相手」と、通知のインスタンスが入っています。アプリの中で、このイベントのリスナーを作れます。

php
use Illuminate\Notifications\Events\NotificationSent;

class LogNotification
{
    /**
     * Handle the event.
     */
    public function handle(NotificationSent $event): void
    {
        // ...
    }
}

リスナーの中では、イベントの notifiable・notification・channel・response のプロパティで、通知の受け取り相手や通知そのものについて、くわしく知れます。

php
/**
 * 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
<?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
<?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日時点の内容をもとに、日本語でまとめています。

ページの一覧