通知

介绍

除了支持 发送电子邮件,Laravel 还提供了通过各种投递渠道发送通知的功能,包括电子邮件、短信(通过 Vonage,前身为 Nexmo)和 Slack。此外,还有许多 社区创建的通知渠道,可以通过数十种不同的渠道发送通知!通知还可以存储在数据库中,以便在您的 Web 界面中显示。

通常,通知应该是简短的信息性消息,用于通知用户应用程序中发生的事件。例如,如果您正在编写一个账单应用程序,您可能会通过电子邮件和短信渠道向用户发送“发票已支付”的通知。

生成通知

在 Laravel 中,每个通知由一个单独的类表示,通常存储在 app/Notifications 目录中。如果您在应用程序中没有看到这个目录,不用担心——当您运行 make:notification Artisan 命令时,Laravel 会为您创建这个目录:

php artisan make:notification InvoicePaid

此命令将在 app/Notifications 目录中创建一个新的通知类。每个通知类包含一个 via 方法和多个消息构建方法,例如 toMailtoDatabase,这些方法将通知转换为适合特定渠道的消息。

发送通知

使用 Notifiable 特性

通知可以通过两种方式发送:使用 Notifiable 特性中的 notify 方法,或使用 Notification facade。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 facade

另外,您也可以通过 Notification facade 发送通知。这种方法在您需要将通知发送给多个可通知实体(例如一组用户)时非常有用。使用 facade 发送通知时,将所有可通知实体和通知实例传递给 send 方法:

use Illuminate\Support\Facades\Notification;

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

您还可以使用 sendNow 方法立即发送通知。即使通知实现了 ShouldQueue 接口,该方法也会立即发送通知:

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

指定投递渠道

每个通知类都有一个 via 方法,用于确定通知将通过哪些渠道发送。通知可以通过邮件、数据库、广播、Vonage 和 Slack 渠道发送。

如果您想使用其它传送渠道(例如 Telegram 或 Pusher),可以查看由社区驱动的 Laravel Notification Channels 网站

via 方法接收一个 $notifiable 实例,该实例是通知发送对象的类实例。您可以使用 $notifiable 来确定通知应该通过哪些渠道发送:

/**
 * 获取通知的传送渠道。
 *
 * @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));

在排队通知时,对于每个收件人和渠道的组合,都会创建一个排队作业。例如,如果您的通知有三个收件人和两个渠道,则会有六个作业被派发到队列中。

延迟通知

如果您希望延迟通知的发送,可以将 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 方法。该方法应返回一个包含渠道名和延迟时间的数组:

/**
 * 确定通知的传送延迟。
 *
 * @return array<string, \Illuminate\Support\Carbon>
 */
public function withDelay(object $notifiable): array
{
    return [
        'mail' => now()->addMinutes(5),
        'sms' => now()->addMinutes(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;

    /**
     * 创建一个新的通知实例。
     */
    public function __construct()
    {
        $this->onConnection('redis');
    }
}

或者,如果您希望为每个通知支持的通知渠道指定特定的队列连接,您可以在通知中定义一个 viaConnections 方法。该方法应返回一个包含渠道名/队列连接名对的数组:

/**
 * 确定每个通知渠道应使用的连接。
 *
 * @return array<string, string>
 */
public function viaConnections(): array
{
    return [
        'mail' => 'redis',
        'database' => 'sync',
    ];
}

自定义通知渠道队列

如果您希望为每个通知支持的通知渠道指定特定的队列,可以在通知中定义一个 viaQueues 方法。该方法应返回一个包含渠道名/队列名对的数组:

/**
 * 确定每个通知渠道应使用的队列。
 *
 * @return array<string, string>
 */
public function viaQueues(): array
{
    return [
        'mail' => 'mail-queue',
        'slack' => 'slack-queue',
    ];
}

排队通知中间件

排队通知可以像排队作业一样定义中间件。要开始使用,您可以在通知类中定义一个 middleware 方法。该方法将接收 $notifiable$channel 变量,这样您就可以根据通知的目标自定义返回的中间件:

use Illuminate\Queue\Middleware\RateLimited;

/**
 * 获取通知作业应通过的中间件。
 *
 * @return array<int, object>
 */
public function middleware(object $notifiable, string $channel)
{
    return match ($channel) {
        'email' => [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;

    /**
     * 创建一个新的通知实例。
     */
    public function __construct()
    {
        $this->afterCommit();
    }
}

要了解更多关于如何解决这些问题,请查看关于【排队作业和数据库事务】的文档。

确定是否发送排队通知

当排队通知已被派发到队列进行后台处理时,它通常会被队列工作者接受并发送到其预定的收件人。

然而,如果您希望在排队通知被队列工作者处理后,最终决定是否发送该通知,您可以在通知类中定义一个 shouldSend 方法。如果该方法返回 false,通知将不会被发送:

/**
 * 确定通知是否应发送。
 */
public function shouldSend(object $notifiable, string $channel): bool
{
    return $this->invoice->isPaid();
}

按需通知

有时,您可能需要将通知发送给未作为应用程序 “用户” 存储的人员。通过使用 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));

如果您想在发送即时通知到邮件路由时提供接收者的名字,可以提供一个包含电子邮件地址作为键,姓名作为值的数组,第一个元素为该数组的值:

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 类包含一些简单的方法,帮助您构建事务性电子邮件消息。邮件消息可以包含文本行以及一个“行动号召”。让我们看看一个 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。您可以将通知生成消息所需的任何数据传递到通知的构造函数中。

在这个示例中,我们注册了一个问候语、一行文本、一个行动号召,然后是另一行文本。MailMessage 对象提供的这些方法使得格式化小型事务性电子邮件变得简单快捷。邮件频道将把消息组件转换成一个美观、响应式的 HTML 邮件模板,并带有纯文本版本。以下是通过邮件频道生成的电子邮件示例:

image 2024 12 30 19 34 40 461

发送邮件通知时,请确保在 config/app.php 配置文件中设置 name 配置项。这个值将用于邮件通知消息的头部和底部。

错误消息

有些通知是用来通知用户错误的,例如发票支付失败。您可以通过在构建邮件消息时调用 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 方法:

/**
 * 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('...');
}

自定义收件人

在通过邮件频道发送通知时,通知系统会自动查找您的可通知实体上的 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 $this->email_address;

        // 返回电子邮件地址和姓名...
        return [$this->email_address => $this->name];
    }
}

自定义主题

默认情况下,电子邮件的主题是通知类的名称,格式化为 “标题大小写”。因此,如果您的通知类名为 InvoicePaid,则电子邮件的主题将是 Invoice Paid。如果您希望为消息指定不同的主题,可以在构建消息时调用 subject 方法:

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->subject('通知主题')
                ->line('...');
}

自定义邮件发送器

默认情况下,电子邮件通知将使用在 config/mail.php 配置文件中定义的默认邮件发送器。然而,您可以通过在构建消息时调用 mailer 方法,在运行时指定不同的邮件发送器:

/**
 * 获取通知的邮件表示。
 */
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 方法。attach 方法的第一个参数是文件的绝对路径:

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->greeting('Hello!')
                ->attach('/path/to/file');
}

通知邮件消息提供的 attach 方法还支持【附件对象】。有关附件对象的详细文档,请参阅全面的【附件对象文档】。

在附加文件时,您还可以通过将一个数组作为第二个参数传递给 attach 方法,来指定显示名称和/或 MIME 类型:

/**
 * 获取通知的邮件表示。
 */
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;

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): Mailable
{
    return (new InvoicePaidMailable($this->invoice))
                ->to($notifiable->email)
                ->attachFromStorage('/path/to/file');
}

如果需要,可以使用 attachMany 方法将多个文件附加到消息中:

/**
 * 获取通知的邮件表示。
 */
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',
                    ],
                ]);
}

原始数据附件

attachData 方法可用于附加原始字节字符串作为附件。在调用 attachData 方法时,您应提供应分配给附件的文件名:

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->greeting('Hello!')
                ->attachData($this->pdf, 'name.pdf', [
                    'mime' => 'application/pdf',
                ]);
}

添加标签和元数据

一些第三方电子邮件提供商,如 Mailgun 和 Postmark,支持邮件 “标签” 和 “元数据”,这些可以用来对您的应用程序发送的邮件进行分组和追踪。您可以通过 tagmetadata 方法向电子邮件消息添加标签和元数据:

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->greeting('评论已被点赞!')
                ->tag('upvote')
                ->metadata('comment_id', $this->comment->id);
}

如果您的应用程序使用 Mailgun 驱动程序,您可以参考 Mailgun 的文档了解更多关于 标签元数据 的信息。同样,Postmark 的文档也可以参考,了解它们对标签和元数据的支持。

如果您的应用程序使用 Amazon SES 发送电子邮件,您应该使用 metadata 方法将 SES 的 “标签” 附加到消息中。

自定义 Symfony 消息

MailMessage 类的 withSymfonyMessage 方法允许您注册一个闭包,该闭包将在发送邮件之前与 Symfony 的 Message 实例一起调用。这使您有机会在邮件发送前深入定制邮件内容:

use Symfony\Component\Mime\Email;

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->withSymfonyMessage(function (Email $message) {
                    $message->getHeaders()->addTextHeader(
                        'Custom-Header', 'Header Value'
                    );
                });
}

这个方法使您能够添加自定义的邮件头或进行其它高级自定义操作,确保邮件在发送之前满足您的需求。

使用 Mailables

如果需要,您可以从通知的 toMail 方法返回一个完整的 mailable 对象。当返回一个 Mailable 而不是 MailMessage 时,您需要使用 mailable 对象的 to 方法指定邮件的接收者:

use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Mail\Mailable;

/**
 * 获取通知的邮件表示。
 */
public function toMail(object $notifiable): Mailable
{
    return (new InvoicePaidMailable($this->invoice))
                ->to($notifiable->email);
}

Mailables 和按需通知

如果您正在发送按需通知,那么传递给 toMail 方法的 $notifiable 实例将是 Illuminate\Notifications\AnonymousNotifiable 类型,后者提供了一个 routeNotificationFor 方法,可用于获取按需通知应该发送到的电子邮件地址:

use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Mail\Mailable;

/**
 * 获取通知的邮件表示。
 */
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 时,它将被渲染并显示在浏览器中,让您能够快速预览其设计,而无需将其发送到实际的电子邮件地址:

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 Artisan 命令的 --markdown 选项:

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

与所有其它邮件通知一样,使用 Markdown 模板的通知应在其通知类中定义 toMail 方法。然而,您应该使用 markdown 方法来指定应该使用的 Markdown 模板的名称,而不是使用 lineaction 方法来构建通知。您可以通过方法的第二个参数传递一个数据数组,以便在模板中使用:

/**
 * 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>

按钮组件

按钮组件呈现一个居中的按钮链接。该组件接受两个参数:一个 URL 和一个可选的颜色。支持的颜色有 primarygreenred。您可以在通知中添加任意数量的按钮组件:

<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 目录将包含 htmltext 目录,每个目录都包含每个可用组件的相应表示形式。您可以根据需要自定义这些组件。

自定义 CSS

导出组件后,resources/views/vendor/mail/html/themes 目录将包含一个 default.css 文件。您可以自定义此文件中的 CSS,您的样式将自动内联到 Markdown 通知的 HTML 表示中。

如果您想为 Laravel 的 Markdown 组件构建一个全新的主题,您可以将 CSS 文件放置在 html/themes 目录中。命名并保存您的 CSS 文件后,更新邮件配置文件中的 theme 选项,将其设置为您的新主题的名称。

要为单个通知自定义主题,您可以在构建通知的邮件消息时调用 theme 方法。theme 方法接受一个字符串参数,即要在发送通知时使用的主题名称:

/**
 * 获取邮件表示的通知。
 */
public function toMail(object $notifiable): MailMessage
{
    return (new MailMessage)
                ->theme('invoice')
                ->subject('Invoice Paid')
                ->markdown('mail.invoice.paid', ['url' => $url]);
}

数据库通知

前提条件

数据库通知通道将通知信息存储在数据库表中。该表将包含通知类型以及描述通知的 JSON 数据结构等信息。

您可以查询该表,以便在应用程序的用户界面中显示通知。但在此之前,您需要创建一个数据库表来存储通知。您可以使用 make:notifications-table 命令生成一个带有适当表结构的迁移文件:

php artisan make:notifications-table

然后运行迁移:

php artisan migrate

如果您的可通知模型使用 【UUID 或 ULID 作为主键】,则应在通知表的迁移中将 morphs 方法替换为 uuidMorphsulidMorphs

格式化数据库通知

如果一个通知支持存储在数据库表中,您应该在通知类中定义 toDatabasetoArray 方法。这个方法将接收一个 $notifiable 实体,并应该返回一个普通的 PHP 数组。返回的数组将被编码为 JSON 并存储在通知表的 data 列中。以下是一个 toArray 方法的示例:

/**
 * 获取通知的数组表示。
 *
 * @return array<string, mixed>
 */
public function toArray(object $notifiable): array
{
    return [
        'invoice_id' => $this->invoice->id,
        'amount' => $this->invoice->amount,
    ];
}

当通知存储在应用程序的数据库中时,type 列将被填充为通知的类名。然而,您可以通过在通知类中定义一个 databaseType 方法来定制此行为:

/**
 * 获取通知的数据库类型。
 *
 * @return string
 */
public function databaseType(object $notifiable): string
{
    return 'invoice-paid';
}

toDatabase vs. toArray

toArray 方法也被广播通道用来确定需要广播到 JavaScript 前端的数据。如果您希望为数据库和广播通道提供不同的数组表示,您应该定义 toDatabase 方法,而不是 toArray 方法。

访问通知

一旦通知存储在数据库中,您需要一个方便的方式来从您的可通知实体中访问它们。Laravel 的 Illuminate\Notifications\Notifiable 特性(该特性默认包含在 App\Models\User 模型中)提供了一个 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;
}

为了从 JavaScript 客户端访问通知,您应该为应用程序定义一个通知控制器,该控制器返回一个可通知实体(例如当前用户)的通知。然后,您可以从 JavaScript 客户端向该控制器的 URL 发起 HTTP 请求。

标记通知为已读

通常,当用户查看通知时,您会希望将其标记为 “已读”。Illuminate\Notifications\Notifiable 特性提供了一个 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();

广播通知

前提条件

在广播通知之前,您应该先配置并熟悉 Laravel 的【事件广播】服务。事件广播提供了一种方式,让您可以从 JavaScript 驱动的前端响应服务器端的 Laravel 事件。

格式化广播通知

广播频道使用 Laravel 的事件广播服务来广播通知,使您的 JavaScript 驱动前端能够实时接收通知。如果通知支持广播,您可以在通知类中定义一个 toBroadcast 方法。该方法将接收一个 $notifiable 实体,并应返回一个 BroadcastMessage 实例。如果 toBroadcast 方法不存在,则会使用 toArray 方法来收集应该广播的数据。返回的数据将被编码为 JSON,并广播到您的 JavaScript 驱动前端。以下是一个示例 toBroadcast 方法:

use Illuminate\Notifications\Messages\BroadcastMessage;

/**
 * 获取可广播的通知表示形式。
 */
public function toBroadcast(object $notifiable): BroadcastMessage
{
    return new BroadcastMessage([
        'invoice_id' => $this->invoice->id,
        'amount' => $this->invoice->amount,
    ]);
}

广播队列配置

所有广播通知都会被排队处理。如果您希望配置用于广播操作的队列连接或队列名称,可以使用 BroadcastMessageonConnectiononQueue 方法:

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

自定义通知类型

除了您指定的数据外,所有广播通知还会包含一个 type 字段,里面包含通知的完整类名。如果您希望自定义通知类型,可以在通知类中定义一个 broadcastType 方法:

/**
 * 获取正在广播的通知类型。
 */
public function broadcastType(): string
{
    return 'broadcast.message';
}

监听通知

通知将通过私有频道广播,频道格式使用 {notifiable}.{id} 约定。因此,如果您正在向 App\Models\User 实例发送通知,并且该实例的 ID 为 1,则通知将通过 App.Models.User.1 私有频道进行广播。在使用 【Laravel Echo】 时,您可以轻松地在频道上监听通知,方法是使用 notification 方法:

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

自定义通知频道

如果您希望自定义实体的广播通知所在的频道,您可以在可通知实体中定义一个 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;

    /**
     * 用户接收广播通知的频道。
     */
    public function receivesBroadcastNotificationsOn(): string
    {
        return 'users.'.$this->id;
    }
}

短信通知

前提条件

在 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 形式发送,您应该在通知类中定义一个 toVonage 方法。该方法将接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\VonageMessage 实例:

use Illuminate\Notifications\Messages\VonageMessage;

/**
 * 获取通知的 Vonage / SMS 表示形式。
 */
public function toVonage(object $notifiable): VonageMessage
{
    return (new VonageMessage)
                ->content('您的 SMS 消息内容');
}

Unicode 内容

如果您的 SMS 消息包含 Unicode 字符,您应该在构造 VonageMessage 实例时调用 unicode 方法:

use Illuminate\Notifications\Messages\VonageMessage;

/**
 * 获取通知的 Vonage / SMS 表示形式。
 */
public function toVonage(object $notifiable): VonageMessage
{
    return (new VonageMessage)
                ->content('Your unicode message')
                ->unicode();
}

自定义 "From" 号码

如果您希望从与 VONAGE_SMS_FROM 环境变量中指定的电话号码不同的号码发送某些通知,您可以在 VonageMessage 实例上调用 from 方法:

use Illuminate\Notifications\Messages\VonageMessage;

/**
 * 获取通知的 Vonage / SMS 表示形式。
 */
public function toVonage(object $notifiable): VonageMessage
{
    return (new VonageMessage)
                ->content('您的 SMS 消息内容')
                ->from('15554443333');
}

添加客户参考

如果您希望跟踪每个用户、团队或客户的费用,您可以在通知中添加 “客户参考”。Vonage 允许您使用此客户参考生成报告,以便更好地了解特定客户的 SMS 使用情况。客户参考可以是最长 40 个字符的任意字符串:

use Illuminate\Notifications\Messages\VonageMessage;

/**
 * 获取通知的 Vonage / SMS 表示形式。
 */
public function toVonage(object $notifiable): VonageMessage
{
    return (new VonageMessage)
                ->clientReference((string) $notifiable->id)
                ->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;

    /**
     * 为 Vonage 渠道路由通知。
     */
    public function routeNotificationForVonage(Notification $notification): string
    {
        return $this->phone_number;
    }
}

Slack 通知

前提条件

在发送 Slack 通知之前,您应该通过 Composer 安装 Slack 通知渠道:

composer require laravel/slack-notification-channel

此外,您必须为您的 Slack 工作区创建一个 【Slack 应用】。

如果您只需要将通知发送到与应用创建所在的同一个 Slack 工作区,则应确保您的应用具有 chat:writechat:write.publicchat:write.customize 权限。如果您希望作为您的 Slack 应用发送消息,则应确保您的应用还具有 chat:write:bot 权限。这些权限可以在 Slack 的“OAuth & Permissions”应用管理标签页中添加。

接下来,复制应用的 “Bot 用户 OAuth 令牌”,并将其放入应用程序的 services.php 配置文件中的 Slack 配置数组中。该令牌可以在 Slack 的 “OAuth & Permissions” 标签页中找到:

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

应用分发

如果您的应用将向您的应用用户拥有的外部 Slack 工作区发送通知,您需要通过 Slack “分发” 您的应用。应用分发可以通过 Slack 中应用的 “Manage Distribution” 标签页进行管理。应用分发后,您可以使用 Socialite 代表您的应用用户【获取 Slack Bot 令牌】。

格式化 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\BlockKit\Composites\ConfirmObject;
use Illuminate\Notifications\Slack\SlackMessage;

/**
 * 获取通知的 Slack 表现形式。
 */
public function toSlack(object $notifiable): SlackMessage
{
    return (new SlackMessage)
            ->text('您的发票已支付!')
            ->headerBlock('发票已支付')
            ->contextBlock(function (ContextBlock $block) {
                $block->text('客户 #1234');
            })
            ->sectionBlock(function (SectionBlock $block) {
                $block->text('发票已支付。');
                $block->field("*发票编号:*\n1000")->markdown();
                $block->field("*发票接收者:*\ntaylor@laravel.com")->markdown();
            })
            ->dividerBlock()
            ->sectionBlock(function (SectionBlock $block) {
                $block->text('恭喜!');
            });
}

使用 Slack 的 Block Kit Builder 模板

您可以提供由 Slack 的 Block Kit Builder 生成的原始 JSON 有效负载,而不是使用流式消息构建方法来构建 Block Kit 消息,方法是使用 usingBlockKitTemplate

use Illuminate\Notifications\Slack\SlackMessage;
use Illuminate\Support\Str;

/**
 * 获取通知的 Slack 表现形式。
 */
public function toSlack(object $notifiable): SlackMessage
{
    $template = <<<JSON
        {
          "blocks": [
            {
              "type": "header",
              "text": {
                "type": "plain_text",
                "text": "团队公告"
              }
            },
            {
              "type": "section",
              "text": {
                "type": "plain_text",
                "text": "我们正在招聘!"
              }
            }
          ]
        }
    JSON;

    return (new SlackMessage)
            ->usingBlockKitTemplate($template);
}

Slack 互动性

Slack 的 Block Kit 通知系统提供了强大的功能来处理用户交互。为了利用这些功能,您的 Slack 应用应该启用 “交互性” 并配置一个 “请求 URL”,该 URL 指向您的应用提供的一个 URL。这些设置可以在 Slack 中的 “交互性与快捷方式”应用管理标签页中进行管理。

在以下示例中,使用了 actionsBlock 方法,Slack 会向您的 “请求 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;

/**
 * 获取通知的 Slack 表现形式。
 */
public function toSlack(object $notifiable): SlackMessage
{
    return (new SlackMessage)
            ->text('您的发票已支付!')
            ->headerBlock('发票已支付')
            ->contextBlock(function (ContextBlock $block) {
                $block->text('客户 #1234');
            })
            ->sectionBlock(function (SectionBlock $block) {
                $block->text('发票已支付。');
            })
            ->actionsBlock(function (ActionsBlock $block) {
                 // ID 默认为 "button_acknowledge_invoice"...
                $block->button('确认发票')->primary();

                // 手动配置 ID...
                $block->button('拒绝')->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;

/**
 * 获取通知的 Slack 表现形式。
 */
public function toSlack(object $notifiable): SlackMessage
{
    return (new SlackMessage)
            ->text('您的发票已支付!')
            ->headerBlock('发票已支付')
            ->contextBlock(function (ContextBlock $block) {
                $block->text('客户 #1234');
            })
            ->sectionBlock(function (SectionBlock $block) {
                $block->text('发票已支付。');
            })
            ->actionsBlock(function (ActionsBlock $block) {
                $block->button('确认发票')
                    ->primary()
                    ->confirm(
                        '确认支付并发送感谢邮件吗?',
                        function (ConfirmObject $dialog) {
                            $dialog->confirm('是');
                            $dialog->deny('否');
                        }
                    );
            });
}

检查 Slack Blocks

如果您希望快速检查您构建的 blocks,可以在 SlackMessage 实例上调用 dd 方法。dd 方法将生成并转储一个 URL 到 Slack 的 【Block Kit Builder】,您可以在浏览器中查看负载和通知的预览。您还可以向 dd 方法传递 true,以转储原始负载:

return (new SlackMessage)
        ->text('您的发票已支付!')
        ->headerBlock('发票已支付')
        ->dd();

路由 Slack 通知

为了将 Slack 通知路由到适当的 Slack 团队和频道,您需要在可通知模型中定义一个 routeNotificationForSlack 方法。该方法可以返回以下三种值之一:

  • null:表示将路由委托给通知中配置的频道。您可以在构建 SlackMessage 时使用 to 方法来配置通知中的频道。

  • 一个字符串,指定要发送通知的 Slack 频道,例如 #support-channel

  • 一个 SlackRoute 实例,允许您指定 OAuth 令牌和频道名称,例如 SlackRoute::make($this→slack_channel, $this→slack_token)。此方法应在向外部工作区发送通知时使用。

例如,从 routeNotificationForSlack 方法中返回 #support-channel 将会把通知发送到与您应用程序 services.php 配置文件中 Bot 用户 OAuth 令牌关联的工作区中的 #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;

    /**
     * 为 Slack 渠道路由通知。
     */
    public function routeNotificationForSlack(Notification $notification): mixed
    {
        return '#support-channel';
    }
}

通知外部 Slack 工作空间

在向外部 Slack 工作区发送通知之前,您的 Slack 应用必须先进行【分发】。

当然,您通常会希望将通知发送到由您的应用用户拥有的 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;

    /**
     * 为 Slack 渠道路由通知。
     */
    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 facade 实现对多个可通知实体的本地化:

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

用户首选语言环境

有时,应用程序会存储每个用户的首选语言环境。通过在您的可通知模型中实现 HasLocalePreference 合约,您可以指示 Laravel 在发送通知时使用存储的语言环境:

use Illuminate\Contracts\Translation\HasLocalePreference;

class User extends Model implements HasLocalePreference
{
    /**
     * 获取用户的首选语言环境。
     */
    public function preferredLocale(): string
    {
        return $this->locale;
    }
}

一旦实现了该接口,Laravel 会在向模型发送通知和邮件时自动使用首选语言环境。因此,使用此接口时无需调用 locale 方法:

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

测试

您可以使用 Notification facade 的 fake 方法来防止通知的发送。通常,发送通知与您实际测试的代码无关。大多数情况下,只需断言 Laravel 是否被指示发送了给定的通知就足够了。

在调用 Notification facade 的 fake 方法之后,您可以断言通知是否已指示发送给用户,并检查通知接收到的数据:

  • Pest

  • PHPUnit

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

按需通知

如果您正在测试的代码发送了【按需通知】,您可以通过 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;
    }
);

通知事件

通知发送事件

当通知正在发送时,通知系统会触发 Illuminate\Notifications\Events\NotificationSending 事件。该事件包含了 “可通知实体” 和通知实例本身。您可以在应用程序中为此事件创建【事件监听器】:

use Illuminate\Notifications\Events\NotificationSending;

class CheckNotificationStatus
{
    /**
     * 处理给定的事件。
     */
    public function handle(NotificationSending $event): void
    {
        // ...
    }
}

如果 NotificationSending 事件的事件监听器的 handle 方法返回 false,则通知将不会被发送:

/**
 * 处理给定的事件。
 */
public function handle(NotificationSending $event): bool
{
    return false;
}

在事件监听器中,您可以访问事件中的 notifiablenotificationchannel 属性,以了解更多关于通知接收者或通知本身的信息:

/**
 * 处理给定的事件。
 */
public function handle(NotificationSending $event): void
{
    // $event->channel
    // $event->notifiable
    // $event->notification
}

通知已发送事件

当通知已发送时,通知系统会触发 Illuminate\Notifications\Events\NotificationSent 事件。该事件包含了 “可通知实体” 和通知实例本身。您可以在应用程序中为此事件创建【事件监听器】:

use Illuminate\Notifications\Events\NotificationSent;

class LogNotification
{
    /**
     * 处理给定的事件。
     */
    public function handle(NotificationSent $event): void
    {
        // ...
    }
}

在事件监听器中,您可以访问事件中的 notifiablenotificationchannelresponse 属性,以了解更多关于通知接收者或通知本身的信息:

/**
 * 处理给定的事件。
 */
public function handle(NotificationSent $event): void
{
    // $event->channel
    // $event->notifiable
    // $event->notification
    // $event->response
}

自定义通道

Laravel 提供了一些内置的通知渠道,但您可能希望编写自己的驱动程序,通过其它渠道发送通知。Laravel 使这变得非常简单。要开始使用,只需定义一个包含 send 方法的类。该方法应该接收两个参数:$notifiable$notification

send 方法中,您可以调用通知上的方法来获取该渠道可以理解的消息对象,然后以您希望的方式将通知发送给 $notifiable 实例:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;

class VoiceChannel
{
    /**
     * 发送给定的通知。
     */
    public function send(object $notifiable, Notification $notification): void
    {
        $message = $notification->toVoice($notifiable);

        // 将通知发送到 $notifiable 实例...
    }
}

一旦定义了您的通知渠道类,您可以在任何通知的 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;

    /**
     * 获取通知的渠道。
     */
    public function via(object $notifiable): string
    {
        return VoiceChannel::class;
    }

    /**
     * 获取通知的语音表示。
     */
    public function toVoice(object $notifiable): VoiceMessage
    {
        // ...
    }
}