LaravelDocs(中文)

通知 (Notifications)

Laravel 提供支援透過多種管道傳送通知

簡介 (Introduction)

除了支援 發送電子郵件 之外,Laravel 還支援透過多種傳送頻道發送通知,包括電子郵件、SMS(透過 Vonage,前身為 Nexmo)和 Slack。此外,還有許多 社群建立的通知頻道 可用於透過數十種不同的頻道發送通知!通知也可以儲存在資料庫中,以便在你的網頁介面上顯示。

通常,通知應該是簡短的資訊性訊息,通知使用者你的應用程式中發生了什麼事。例如,如果你正在撰寫一個計費應用程式,你可能會透過電子郵件和 SMS 頻道向你的使用者發送「發票已支付」的通知。

產生通知 (Generating Notifications)

在 Laravel 中,每個通知都由一個類別表示,通常儲存在 app/Notifications 目錄中。如果你在應用程式中沒有看到這個目錄,請不要擔心 - 當你執行 make:notification Artisan 指令時,它會為你建立:

php artisan make:notification InvoicePaid

這個指令會在你的 app/Notifications 目錄中放置一個新的通知類別。每個通知類別都包含一個 via 方法和數個訊息建構方法(例如 toMailtoDatabase),這些方法會將通知轉換為針對該特定頻道量身打造的訊息。

發送通知 (Sending Notifications)

使用 Notifiable Trait (Using the Notifiable Trait)

通知可以透過兩種方式發送:使用 Notifiable trait 的 notify 方法或使用 Notification facadeNotifiable trait 預設包含在你的應用程式的 App\Models\User 模型中:

<?php

namespace App\Models;

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

class User extends Authenticatable
{
    use Notifiable;
}

這個 trait 提供的 notify 方法預期接收一個通知實例:

use App\Notifications\InvoicePaid;

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

[!NOTE] 請記住,你可以在任何模型上使用 Notifiable trait。你不僅限於將其包含在你的 User 模型中。

使用 Notification Facade (Using the Notification Facade)

或者,你可以透過 Notification facade 發送通知。當你需要向多個可通知實體(例如使用者集合)發送通知時,這種方法很有用。要使用 facade 發送通知,請將所有可通知實體和通知實例傳遞給 send 方法:

use Illuminate\Support\Facades\Notification;

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

你也可以使用 sendNow 方法立即發送通知。即使通知實作了 ShouldQueue 介面,此方法也會立即發送通知:

Notification::sendNow($developers, new DeploymentCompleted($deployment));

指定傳送頻道 (Specifying Delivery Channels)

每個通知類別都有一個 via 方法,用於決定通知將在哪個頻道上傳送。通知可以在 maildatabasebroadcastvonageslack 頻道上發送。

[!NOTE] 如果你想使用其他傳送頻道,例如 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'];
}

佇列通知 (Queueing Notifications)

[!WARNING] 在佇列通知之前,你應該設定你的佇列並 啟動一個 worker

發送通知可能需要一些時間,特別是如果頻道需要進行外部 API 呼叫來傳送通知。為了加快應用程式的回應時間,請將 ShouldQueue 介面和 Queueable trait 新增到你的類別中,讓你的通知被佇列。使用 make:notification 指令產生的所有通知都已經匯入了這個介面和 trait,所以你可以立即將它們新增到你的通知類別中:

<?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));

當佇列通知時,將為每個收件者和頻道組建立一個佇列任務。例如,如果你的通知有三個收件者和兩個頻道,將會分派六個任務到佇列。

延遲通知 (Delaying Notifications)

如果你想延遲通知的傳送,你可以將 delay 方法串接到你的通知實例化上:

$delay = now()->addMinutes(10);

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

你可以傳遞一個陣列給 delay 方法,以指定特定頻道的延遲時間:

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

或者,你可以在通知類別本身定義一個 withDelay 方法。withDelay 方法應該傳回一個包含頻道名稱和延遲值的陣列:

/**
 * Determine the notification's delivery delay.
 *
 * @return array<string, \Illuminate\Support\Carbon>
 */
public function withDelay(object $notifiable): array
{
    return [
        'mail' => now()->addMinutes(5),
        'sms' => now()->addMinutes(10),
    ];
}

自訂通知佇列連線 (Customizing the Notification Queue Connection)

預設情況下,佇列通知將使用你的應用程式的預設佇列連線進行佇列。如果你想指定用於特定通知的不同連線,你可以從通知的建構子呼叫 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',
    ];
}

自訂通知頻道佇列 (Customizing Notification Channel Queues)

如果你想為通知支援的每個通知頻道指定特定的佇列,你可以在你的通知上定義一個 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',
    ];
}

自訂佇列通知任務屬性 (Customizing Queued Notification Job Properties)

你可以透過在你的通知類別上定義屬性來自訂底層佇列任務的行為。這些屬性將由發送通知的佇列任務繼承:

<?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;

    /**
     * The number of times the notification may be attempted.
     *
     * @var int
     */
    public $tries = 5;

    /**
     * The number of seconds the notification can run before timing out.
     *
     * @var int
     */
    public $timeout = 120;

    /**
     * The maximum number of unhandled exceptions to allow before failing.
     *
     * @var int
     */
    public $maxExceptions = 3;

    // ...
}

如果你想透過 加密 確保佇列通知資料的隱私和完整性,請將 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;

    // ...
}

除了直接在你的通知類別上定義這些屬性之外,你還可以定義 backoffretryUntil 方法來指定佇列通知任務的退避策略和重試逾時:

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()->addMinutes(5);
}

[!NOTE] 有關這些任務屬性和方法的更多資訊,請查看關於 佇列任務 的文件。

佇列通知 Middleware (Queued Notification Middleware)

佇列通知可以定義 middleware,就像佇列任務一樣。要開始使用,請在你的通知類別上定義一個 middleware 方法。middleware 方法將接收 $notifiable$channel 變數,這允許你根據通知的目的地自訂傳回的 middleware:

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 => [],
    };
}

佇列通知與資料庫交易 (Queued Notifications and Database Transactions)

當佇列通知在資料庫交易中分派時,它們可能會在資料庫交易提交之前由佇列處理。發生這種情況時,你在資料庫交易期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,在交易中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果你的通知依賴於這些模型,則在處理發送佇列通知的任務時可能會發生意外錯誤。

如果你的佇列連線的 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();
    }
}

[!NOTE] 要了解有關解決這些問題的更多資訊,請查看關於 佇列任務和資料庫交易 的文件。

決定是否應發送佇列通知 (Determining if a Queued Notification Should Be (Determining If The Queued Notification Should Be Sent)

Sent)

在佇列通知被分派到佇列進行背景處理後,它通常會被佇列 worker 接受並發送給預期的收件者。

但是,如果你想在佇列 worker 處理後決定是否應發送佇列通知,你可以在通知類別上定義一個 shouldSend 方法。如果此方法傳回 false,則不會發送通知:

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

隨需通知 (On-Demand Notifications)

有時你可能需要向未儲存為應用程式「使用者」的人發送通知。使用 Notification facade 的 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));

郵件通知 (Mail Notifications)

格式化郵件訊息 (Formatting Mail Messages)

如果通知支援以電子郵件發送,你應該在通知類別上定義一個 toMail 方法。這個方法將接收一個 $notifiable 實體,並應傳回一個 Illuminate\Notifications\Messages\MailMessage 實例。

MailMessage 類別包含一些簡單的方法來幫助你建立交易式電子郵件訊息。郵件訊息可以包含文字行以及「行動呼叫」。讓我們來看一個 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!');
}

[!NOTE] 注意我們在 toMail 方法中使用了 $this->invoice->id。你可以將通知產生訊息所需的任何資料傳遞給通知的建構子。

在這個範例中,我們註冊了一個問候語、一行文字、一個行動呼叫,然後是另一行文字。MailMessage 物件提供的這些方法使得格式化小型交易式電子郵件變得簡單快速。郵件頻道隨後會將訊息元件轉換為美觀、響應式的 HTML 電子郵件樣板,並附帶純文字對應版本。以下是 mail 頻道產生的電子郵件範例:

[!NOTE] 發送郵件通知時,請務必在 config/app.php 設定檔中設定 name 設定選項。此值將用於郵件通知訊息的頁首和頁尾。

錯誤訊息 (Error Messages)

有些通知會通知使用者錯誤,例如發票付款失敗。你可以在建立訊息時呼叫 error 方法來指示郵件訊息是關於錯誤的。當在郵件訊息上使用 error 方法時,行動呼叫按鈕將是紅色而不是黑色:

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

其他郵件通知格式化選項 (Other Mail Notification Formatting Options)

除了在通知類別中定義文字「行」之外,你還可以使用 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 方法的陣列的第二個元素來指定郵件訊息的純文字視圖:

/**
 * 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]
    );
}

自訂寄件者 (Customizing the Sender)

預設情況下,電子郵件的寄件者 / from 地址定義在 config/mail.php 設定檔中。但是,你可以使用 from 方法為特定通知指定 from 地址:

/**
 * Get the mail representation of the notification.
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->from('barrett@example.com', 'Barrett Blair')
        ->line('...');
}

自訂收件者 (Customizing the Recipient)

當透過 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];
    }
}

自訂主旨 (Customizing the Subject)

預設情況下,電子郵件的主旨是通知的類別名稱格式化為「標題大小寫」。因此,如果你的通知類別名為 InvoicePaid,電子郵件的主旨將是 Invoice Paid。如果你想為訊息指定不同的主旨,你可以在建立訊息時呼叫 subject 方法:

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

自訂 Mailer (Customizing the Mailer)

預設情況下,電子郵件通知將使用 config/mail.php 設定檔中定義的預設 mailer 發送。但是,你可以在建立訊息時呼叫 mailer 方法在執行時指定不同的 mailer:

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

自訂樣板 (Customizing the Templates)

你可以透過發布通知套件的資源來修改郵件通知使用的 HTML 和純文字樣板。執行此指令後,郵件通知樣板將位於 resources/views/vendor/notifications 目錄中:

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

附件 (Attachments)

要將附件新增到電子郵件通知中,請在建立訊息時使用 attach 方法。attach 方法接受檔案的絕對路徑作為其第一個參數:

/**
 * Get the mail representation of the notification.
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
        ->greeting('Hello!')
        ->attach('/path/to/file');
}

[!NOTE] 通知郵件訊息提供的 attach 方法也接受 可附加物件。請參閱完整的 可附加物件文件 以了解更多資訊。

當附加檔案到訊息時,你也可以透過將 array 作為第二個參數傳遞給 attach 方法來指定顯示名稱和 / 或 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',
        ]);
}

與在 mailable 物件中附加檔案不同,你不能使用 attachFromStorage 直接從儲存磁碟附加檔案。你應該使用帶有儲存磁碟上檔案絕對路徑的 attach 方法。或者,你可以從 toMail 方法傳回一個 mailable

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)
        ->attachFromStorage('/path/to/file');
}

必要時,可以使用 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',
            ],
        ]);
}

原始資料附件 (Raw Data Attachments)

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',
        ]);
}

新增標籤與 Metadata (Adding Tags and Metadata)

一些第三方電子郵件提供者(如 Mailgun 和 Postmark)支援訊息「標籤」和「metadata」,可用於分組和追蹤你的應用程式發送的電子郵件。你可以透過 tagmetadata 方法將標籤和 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 驅動程式,你可以查閱 Mailgun 的文件以獲取有關 標籤metadata 的更多資訊。同樣,也可以查閱 Postmark 文件以獲取有關其支援 標籤metadata 的更多資訊。

如果你的應用程式使用 Amazon SES 發送電子郵件,你應該使用 metadata 方法將 SES "tags" 附加到訊息中。

自訂 Symfony 訊息 (Customizing the Symfony Message)

MailMessage 類別的 withSymfonyMessage 方法允許你註冊一個閉包,該閉包將在發送訊息之前使用 Symfony Message 實例被呼叫。這讓你有機會在訊息傳送之前對其進行深度自訂:

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'
            );
        });
}

使用 Mailables (Using Mailables)

如果需要,你可以從通知的 toMail 方法傳回一個完整的 mailable 物件。當傳回 Mailable 而不是 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);
}

Mailables 與隨需通知 (Mailables and On-Demand Notifications)

如果你正在發送 隨需通知,傳遞給 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);
}

預覽郵件通知 (Previewing Mail Notifications)

在設計郵件通知樣板時,能夠像典型的 Blade 樣板一樣在瀏覽器中快速預覽呈現的郵件訊息是很方便的。因此,Laravel 允許你直接從路由閉包或控制器傳回由郵件通知產生的任何郵件訊息。當傳回 MailMessage 時,它將被呈現並顯示在瀏覽器中,讓你無需將其發送到實際的電子郵件地址即可快速預覽其設計:

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 Mail Notifications)

Markdown 郵件通知允許你利用預先建立的郵件通知樣板,同時讓你有更多自由來撰寫更長、自訂的訊息。由於訊息是用 Markdown 撰寫的,Laravel 能夠為訊息呈現美觀、響應式的 HTML 樣板,同時自動產生純文字對應版本。

產生訊息 (Generating the Message)

要產生帶有對應 Markdown 樣板的通知,你可以使用 make:notification Artisan 指令的 --markdown 選項:

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

像所有其他郵件通知一樣,使用 Markdown 樣板的通知應該在它們的通知類別上定義一個 toMail 方法。但是,不要使用 lineaction 方法來建構通知,而是使用 markdown 方法來指定應使用的 Markdown 樣板名稱。你希望提供給樣板的資料陣列可以作為方法的第二個參數傳遞:

/**
 * 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]);
}

撰寫訊息 (Writing the Message)

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>

[!NOTE] 撰寫 Markdown 電子郵件時,請勿使用過多的縮排。根據 Markdown 標準,Markdown 解析器會將縮排的內容呈現為程式碼區塊。

按鈕元件 (Button Component)

按鈕元件呈現一個置中的按鈕連結。該元件接受兩個參數,一個 url 和一個可選的 color。支援的顏色有 primarygreenred。你可以根據需要在通知中新增任意數量的按鈕元件:

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

面板元件 (Panel Component)

面板元件將給定的文字區塊呈現在一個背景顏色與通知其餘部分略有不同的面板中。這允許你引起對給定文字區塊的注意:

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

表格元件 (Table Component)

表格元件允許你將 Markdown 表格轉換為 HTML 表格。該元件接受 Markdown 表格作為其內容。表格欄位對齊支援使用預設的 Markdown 表格對齊語法:

<x-mail::table>
| Laravel       | Table         | Example       |
| ------------- | :-----------: | ------------: |
| Col 2 is      | Centered      | $10           |
| Col 3 is      | Right-Aligned | $20           |
</x-mail::table>

自訂元件 (Customizing the Components)

你可以將所有 Markdown 通知元件匯出到你自己的應用程式以進行自訂。要匯出元件,請使用 vendor:publish Artisan 指令來發布 laravel-mail asset 標籤:

php artisan vendor:publish --tag=laravel-mail

此指令將把 Markdown 郵件元件發布到 resources/views/vendor/mail 目錄。mail 目錄將包含一個 html 和一個 text 目錄,每個目錄都包含每個可用元件的各自表示形式。你可以隨意自訂這些元件。

自訂 CSS (Customizing the CSS)

匯出元件後,resources/views/vendor/mail/html/themes 目錄將包含一個 default.css 檔案。你可以自訂此檔案中的 CSS,你的樣式將自動內嵌在 Markdown 通知的 HTML 表示形式中。

如果你想為 Laravel 的 Markdown 元件建立一個全新的主題,你可以在 html/themes 目錄中放置一個 CSS 檔案。命名並儲存你的 CSS 檔案後,更新 mail 設定檔的 theme 選項以符合你的新主題名稱。

要為個別通知自訂主題,你可以在建構通知的郵件訊息時呼叫 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 Notifications)

先決條件 (Prerequisites)

database 通知頻道將通知資訊儲存在資料庫資料表中。此資料表將包含通知類型以及描述通知的 JSON 資料結構等資訊。

你可以查詢資料表以在應用程式的使用者介面中顯示通知。但是,在執行此操作之前,你需要建立一個資料庫資料表來保存你的通知。你可以使用 make:notifications-table 指令來產生具有適當資料表結構的 遷移

php artisan make:notifications-table

php artisan migrate

[!NOTE] 如果你的可通知模型使用 UUID 或 ULID 主鍵,你應該在通知資料表遷移中將 morphs 方法替換為 uuidMorphsulidMorphs

格式化資料庫通知 (Formatting Database Notifications)

如果通知支援儲存在資料庫資料表中,你應該在通知類別上定義一個 toDatabasetoArray 方法。這個方法將接收一個 $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。但是,你可以透過在你的通知類別中定義 databaseTypeinitialDatabaseReadAtValue 方法來自訂此行為:

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 vs. toArray

toArray 方法也由 broadcast 頻道使用,以決定要廣播到你的 JavaScript 前端的資料。如果你希望為 databasebroadcast 頻道擁有兩種不同的陣列表示形式,你應該定義一個 toDatabase 方法而不是 toArray 方法。

存取通知 (Accessing the Notifications)

一旦通知儲存在資料庫中,你需要一種方便的方法從你的可通知實體存取它們。Laravel 預設的 App\Models\User 模型中包含的 Illuminate\Notifications\Notifiable trait 包含一個 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;
}

[!NOTE] 要從你的 JavaScript 用戶端存取你的通知,你應該為你的應用程式定義一個通知控制器,該控制器傳回可通知實體(例如目前使用者)的通知。然後,你可以從你的 JavaScript 用戶端向該控制器的 URL 發出 HTTP 請求。

將通知標記為已讀 (Marking Notifications as Read)

通常,你會希望在使用者檢視通知時將其標記為「已讀」。Illuminate\Notifications\Notifiable trait 提供了一個 markAsRead 方法,它會更新通知資料庫記錄上的 read_at 欄位:

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

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

但是,你可以直接在通知集合上使用 markAsRead 方法,而不是迴圈遍歷每個通知:

$user->unreadNotifications->markAsRead();

你也可以使用批次更新查詢將所有通知標記為已讀,而無需從資料庫中檢索它們:

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

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

你可以 delete 通知以將它們從資料表中完全移除:

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

廣播通知 (Broadcast Notifications)

先決條件 (Prerequisites)

在廣播通知之前,你應該設定並熟悉 Laravel 的 事件廣播 服務。事件廣播提供了一種從你的 JavaScript 前端對伺服器端 Laravel 事件做出反應的方法。

格式化廣播通知 (Formatting Broadcast Notifications)

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,
    ]);
}

廣播佇列設定 (Broadcast Queue Configuration)

所有廣播通知都會被佇列以進行廣播。如果你想設定用於佇列廣播操作的佇列連線或佇列名稱,你可以使用 BroadcastMessageonConnectiononQueue 方法:

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

自訂通知類型 (Customizing the Notification Type)

除了你指定的資料之外,所有廣播通知都有一個 type 欄位,其中包含通知的完整類別名稱。如果你想自訂通知 type,你可以在通知類別上定義一個 broadcastType 方法:

/**
 * Get the type of the notification being broadcast.
 */
public function broadcastType(): string
{
    return 'broadcast.message';
}

監聽通知 (Listening for Notifications)

通知將在格式為 {notifiable}.{id} 的私有頻道上廣播。因此,如果你向 ID 為 1App\Models\User 實例發送通知,通知將在 App.Models.User.1 私有頻道上廣播。當使用 Laravel Echo 時,你可以使用 notification 方法輕鬆監聽頻道上的通知:

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

使用 React 或 Vue (Using React or Vue)

Laravel Echo 包含 React 和 Vue hooks,讓監聽通知變得輕鬆。要開始使用,請呼叫 useEchoNotification hook,它用於監聽通知。當使用的元件被卸載時,useEchoNotification hook 將自動離開頻道:

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

useEchoNotification(`App.Models.User.${userId}`, (notification) => {
  console.log(notification.type);
});
<script setup lang="ts">
import { useEchoNotification } from "@laravel/echo-vue";

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

預設情況下,hook 會監聽所有通知。要指定你想監聽的通知類型,你可以提供一個字串或類型陣列給 useEchoNotification

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

useEchoNotification(
  `App.Models.User.${userId}`,
  (notification) => {
    console.log(notification.type);
  },
  "App.Notifications.InvoicePaid"
);
<script setup lang="ts">
import { useEchoNotification } from "@laravel/echo-vue";

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

你也可以指定通知 payload 資料的形狀,提供更好的類型安全和編輯便利性:

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"
);

自訂通知頻道 (Customizing the Notification Channel)

如果你想自訂實體的廣播通知在哪個頻道上廣播,你可以在可通知實體上定義一個 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 通知 (SMS Notifications)

先決條件 (Prerequisites)

在 Laravel 中發送 SMS 通知由 Vonage(前身為 Nexmo)提供支援。在透過 Vonage 發送通知之前,你需要安裝 laravel/vonage-notification-channelguzzlehttp/guzzle 套件:

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

該套件包含一個 設定檔。但是,你不需要將此設定檔匯出到你自己的應用程式。你可以簡單地使用 VONAGE_KEYVONAGE_SECRET 環境變數來定義你的 Vonage 公鑰和私鑰。

定義你的金鑰後,你應該設定一個 VONAGE_SMS_FROM 環境變數,該變數定義預設情況下發送 SMS 訊息的電話號碼。你可以在 Vonage 控制面板中產生此電話號碼:

VONAGE_SMS_FROM=15556666666

格式化 SMS 通知 (Formatting SMS Notifications)

如果通知支援以 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 內容 (Unicode Content)

如果你的 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();
}

自訂 "From" 號碼 (Customizing the "From" Number)

如果你想從與 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');
}

新增客戶參考 (Adding a 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 通知 (Routing SMS Notifications)

要將 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 Notifications)

先決條件 (Prerequisites)

在發送 Slack 通知之前,你應該透過 Composer 安裝 Slack 通知頻道:

composer require laravel/slack-notification-channel

此外,你必須為你的 Slack 工作區建立一個 Slack App

如果你只需要向建立 App 的同一個 Slack 工作區發送通知,你應該確保你的 App 具有 chat:writechat:write.publicchat:write.customize 範圍。這些範圍可以從 Slack 內的「OAuth & Permissions」App 管理分頁中新增。

接下來,複製 App 的「Bot User OAuth Token」並將其放置在你的應用程式的 services.php 設定檔中的 slack 設定陣列中。此 token 可以在 Slack 內的「OAuth & Permissions」分頁中找到:

'slack' => [
    'notifications' => [
        'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
        'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
    ],
],

App 分發 (App Distribution)

如果你的應用程式將向你的應用程式使用者擁有的外部 Slack 工作區發送通知,你需要透過 Slack「分發」你的 App。App 分發可以從 Slack 內的 App「Manage Distribution」分頁進行管理。一旦你的 App 被分發,你可以使用 Socialite 代表你的應用程式使用者 獲取 Slack Bot tokens

格式化 Slack 通知 (Formatting Slack Notifications)

如果通知支援以 Slack 訊息發送,你應該在通知類別上定義一個 toSlack 方法。這個方法將接收一個 $notifiable 實體,並應傳回一個 Illuminate\Notifications\Slack\SlackMessage 實例。你可以使用 Slack 的 Block Kit API 建構豐富的通知。以下範例可以在 Slack 的 Block Kit 建構器 中預覽:

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!');
        });
}

使用 Slack 的 Block Kit Builder 樣板 (Using Slack's Block Kit Builder (Using Slacks Block Kit Builder Template)

Template)

除了使用流暢的訊息建構器方法來建構你的 Block Kit 訊息之外,你還可以將 Slack 的 Block Kit Builder 產生的原始 JSON payload 提供給 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 Interactivity)

Slack 的 Block Kit 通知系統提供了強大的功能來 處理使用者互動。要利用這些功能,你的 Slack App 應該啟用「Interactivity」並設定一個指向你的應用程式提供的 URL 的「Request URL」。這些設定可以從 Slack 內的「Interactivity & Shortcuts」App 管理分頁中進行管理。

在以下使用 actionsBlock 方法的範例中,Slack 將向你的「Request URL」發送一個 POST 請求,其中包含點擊按鈕的 Slack 使用者、點擊按鈕的 ID 等 payload。然後你的應用程式可以根據 payload 決定要採取的動作。你也應該 驗證請求 是由 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');
        });
}

確認模態框 (Confirmation Modals)

如果你希望使用者在執行動作之前必須確認,你可以在定義按鈕時呼叫 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');
                    }
                );
        });
}

Inspecting Slack Blocks

If you would like to quickly inspect the blocks you've been building, you can invoke the dd method on the SlackMessage instance. The dd method will generate and dump a URL to Slack's Block Kit Builder, which displays a preview of the payload and notification in your browser. You may pass true to the dd method to dump the raw payload:

return (new SlackMessage)
    ->text('One of your invoices has been paid!')
    ->headerBlock('Invoice Paid')
    ->dd();

Routing Slack Notifications (Routing Slack Notifications)

To direct Slack notifications to the appropriate Slack team and channel, define a routeNotificationForSlack method on your notifiable model. This method can return one of three values:

  • null - which defers routing to the channel configured in the notification itself. You may use the to method when building your SlackMessage to configure the channel within the notification.
  • A string specifying the Slack channel to send the notification to, e.g. #support-channel.
  • A SlackRoute instance, which allows you to specify an OAuth token and channel name, e.g. SlackRoute::make($this->slack_channel, $this->slack_token). This method should be used to send notifications to external workspaces.

For instance, returning #support-channel from the routeNotificationForSlack method will send the notification to the #support-channel channel in the workspace associated with the Bot User OAuth token located in your application's services.php configuration file:

<?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';
    }
}

Notifying External Slack Workspaces (Notifying External Slack Workspaces)

[!NOTE] Before sending notifications to external Slack workspaces, your Slack App must be distributed.

Of course, you will often want to send notifications to the Slack workspaces owned by your application's users. To do so, you will first need to obtain a Slack OAuth token for the user. Thankfully, Laravel Socialite includes a Slack driver that will allow you to easily authenticate your application's users with Slack and obtain a bot token.

Once you have obtained the bot token and stored it within your application's database, you may utilize the SlackRoute::make method to route a notification to the user's workspace. In addition, your application will likely need to offer an opportunity for the user to specify which channel notifications should be sent to:

<?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);
    }
}

在地化通知 (Localizing Notifications)

Laravel 允許你以 HTTP 請求的目前語系以外的語系發送通知,如果通知被佇列,甚至會記住這個語系。

為此,Illuminate\Notifications\Notification 類別提供了一個 locale 方法來設定所需的語言。應用程式在評估通知時將切換到此語系,並在評估完成後恢復到先前的語系:

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

多個可通知項目的在地化也可以透過 Notification facade 實現:

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

使用者偏好語系 (User Preferred Locales)

有時,應用程式會儲存每個使用者的偏好語系。透過在你的可通知模型上實作 HasLocalePreference contract,你可以指示 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 將在向模型發送通知和 mailables 時自動使用偏好語系。因此,使用此介面時無需呼叫 locale 方法:

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

測試 (Testing)

你可以使用 Notification facade 的 fake 方法來阻止發送通知。通常,發送通知與你實際測試的程式碼無關。大多數情況下,只需斷言 Laravel 被指示發送給定通知就足夠了。

呼叫 Notification facade 的 fake 方法後,你可以斷言通知被指示發送給使用者,甚至檢查通知接收到的資料:

<?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 given number of notifications were sent...
    Notification::assertCount(3);
});
<?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 given number of notifications were sent...
        Notification::assertCount(3);
    }
}

你可以將閉包傳遞給 assertSentToassertNotSentTo 方法,以斷言發送的通知通過了給定的「真值測試」。如果至少發送了一個通過給定真值測試的通知,則斷言將成功:

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

隨需通知 (On-Demand Notifications)

如果你正在測試的程式碼發送 隨需通知,你可以透過 assertSentOnDemand 方法測試隨需通知是否已發送:

Notification::assertSentOnDemand(OrderShipped::class);

透過將閉包作為第二個參數傳遞給 assertSentOnDemand 方法,你可以確定隨需通知是否發送到正確的「路由」地址:

Notification::assertSentOnDemand(
    OrderShipped::class,
    function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
        return $notifiable->routes['mail'] === $user->email;
    }
);

通知事件 (Notification Events)

通知發送事件 (Notification Sending Event)

當通知正在發送時,通知系統會分派 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;
}

在事件監聽器中,你可以存取事件上的 notifiablenotificationchannel 屬性,以了解有關通知收件者或通知本身的更多資訊:

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

通知已發送事件 (Notification Sent Event)

當通知發送後,通知系統會分派 Illuminate\Notifications\Events\NotificationSent 事件。這包含「可通知」實體和通知實例本身。你可以在應用程式中為此事件建立 事件監聽器

use Illuminate\Notifications\Events\NotificationSent;

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

在事件監聽器中,你可以存取事件上的 notifiablenotificationchannelresponse 屬性,以了解有關通知收件者或通知本身的更多資訊:

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

自訂頻道 (Custom Channels)

Laravel 附帶了一些通知頻道,但你可能希望編寫自己的驅動程式以透過其他頻道發送通知。Laravel 讓這變得很簡單。首先,定義一個包含 send 方法的類別。該方法應接收兩個參數:一個 $notifiable 和一個 $notification

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
    {
        // ...
    }
}