Appearance
通知
简介
除了支持 发送电子邮件 外,Laravel 还支持通过多种传递渠道发送通知,包括邮件、短信(通过 Vonage,前身为 Nexmo)和 Slack。此外,还有许多 社区构建的通知渠道 可以通过数十种不同的渠道发送通知!通知也可以存储在数据库中,以便在您的 Web 界面中显示。
通常,通知应该是简短的信息性消息,用于通知用户您的应用程序中发生的事情。例如,如果您正在编写一个计费应用程序,您可以通过电子邮件和短信渠道向用户发送 "已付款发票" 通知。
生成通知
在 Laravel 中,每个通知都由一个单独的类表示,通常存储在 app/Notifications
目录中。如果在您的应用程序中没有看到此目录,不用担心 - 当您运行 make:notification
Artisan 命令时,它将为您创建:
shell
php artisan make:notification InvoicePaid
此命令将在您的 app/Notifications
目录中放置一个新的通知类。每个通知类都包含一个 via
方法和一个可变数量的消息构建方法,例如 toMail
或 toDatabase
,它们将通知转换为针对该特定渠道的消息。
发送通知
使用 Notifiable Trait
通知可以通过两种方式发送:使用 Notifiable
trait 的 notify
方法或使用 Notification
facade。Notifiable
trait 已包含在您的应用程序的 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;
}
此 trait 提供的 notify
方法需要接收一个通知实例:
php
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
NOTE
请记住,您可以在任何模型上使用 Notifiable
trait。您不仅限于只在 User
模型上包含它。
使用 Notification Facade
或者,您可以使用 Notification
facade 发送通知。当您需要向多个可通知实体(例如一组用户)发送通知时,此方法非常有用。要使用 facade 发送通知,请将所有可通知实体和通知实例传递给 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
渠道发送。
NOTE
如果您想使用其他传递渠道,例如 Telegram 或 Pusher,请查看 Laravel Notification Channels website。
via
方法接收一个 $notifiable
实例,它将是接收通知的类的实例。您可以使用 $notifiable
来确定应该在哪些渠道上传递通知:
php
/**
* 获取通知的传递渠道
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
}
队列化通知
WARNING
在队列化通知之前,您应该配置您的队列并 启动一个 worker。
发送通知可能需要一些时间,特别是如果渠道需要进行外部 API 调用来传递通知。为了加快应用程序的响应时间,让您的通知被队列化,在您的类中添加 ShouldQueue
接口和 Queueable
trait。所有使用 make:notification
命令生成的通知都已经导入了这些接口和 trait,因此您可以立即将它们添加到您的通知类中:
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));
将通知排队时,将为每个收件人和通道组合创建一个排队的作业。例如,如果您的通知有 3 个收件人和 2 个频道,则将向队列分派 6 个作业。
延迟通知
如果您想延迟通知的传递,您可以在实例化通知时链接 delay
方法:
php
$delay = now()->addMinutes(10);
$user->notify((new InvoicePaid($invoice))->delay($delay));
您可以传递一个数组给 delay
方法,以指定每个渠道的延迟时间:
php
$user->notify((new InvoicePaid($invoice))->delay([
'mail' => now()->addMinutes(5),
'sms' => now()->addMinutes(10),
]));
或者,您可以在通知类本身上定义一个 withDelay
方法。withDelay
方法应返回一个渠道名称和延迟值的数组:
php
/**
* 确定通知的送达延迟
*
* @return array<string, \Illuminate\Support\Carbon>
*/
public function withDelay(object $notifiable): array
{
return [
'mail' => now()->addMinutes(5),
'sms' => now()->addMinutes(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;
/**
* 创建新的通知实例
*/
public function __construct()
{
$this->onConnection('redis');
}
}
或者,如果您想为通知支持的每个渠道指定特定的队列连接,您可以在通知上定义一个 viaConnections
方法。此方法应返回一个渠道名称 / 队列连接名称对的数组:
php
/**
* 确定每个通知通道应使用哪些连接
*
* @return array<string, string>
*/
public function viaConnections(): array
{
return [
'mail' => 'redis',
'database' => 'sync',
];
}
自定义通知渠道队列
如果您想为通知支持的每个渠道指定特定的队列,您可以在通知上定义一个 viaQueues
方法。此方法应返回一个渠道名称 / 队列名称对的数组:
php
/**
* 确定每个通知通道应使用哪些队列
*
* @return array<string, string>
*/
public function viaQueues(): array
{
return [
'mail' => 'mail-queue',
'slack' => 'slack-queue',
];
}
队列化通知中间件
队列化的通知可以定义中间件 就像队列化的作业一样。要开始,在通知类上定义一个 middleware
方法。middleware
方法将接收 $notifiable
和 $channel
变量,允许您根据通知的目的地定制返回的中间件:
php
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
方法:
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;
/**
* 创建新的通知实例
*/
public function __construct()
{
$this->afterCommit();
}
}
NOTE
要了解更多关于如何解决这些问题的信息,请查阅有关 队列化作业和数据库事务 的文档。
确定是否应发送队列化通知
在队列化的通知已被分派到队列进行后台处理后,它通常会被队列工作器接受并发送到其预定的收件人。
但是,如果您想在队列工作器处理队列化通知后最后确定是否应发送该通知,您可以在通知类上定义一个 shouldSend
方法。如果此方法返回 false
,则不会发送通知:
php
/**
* 确定是否应发送通知
*/
public function shouldSend(object $notifiable, string $channel): bool
{
return $this->invoice->isPaid();
}
按需通知
有时您可能需要向不存储为应用程序 "用户" 的人发送通知。使用 Notification
facade 的 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
路由时提供收件人的名称,您可以在传递给 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
类包含一些简单的方法,可以帮助您构建事务性电子邮件消息。邮件消息可以包含文本行以及 "调用操作"。让我们看一个 toMail
方法的示例:
php
/**
* 获取通知的邮件表示形式
*/
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 电子邮件模板,并带有纯文本副本。这是邮件渠道生成的电子邮件示例:
NOTE
发送邮件通知时,请确保在 config/app.php
配置文件中设置 name
配置选项。此值将用于邮件通知消息的标题和页脚中。
错误消息
一些通知用户发生了错误,例如发票付款失败。您可以通过在构建消息时调用 error
方法来指示邮件消息是关于错误的。当在邮件消息上使用 error
方法时,调用操作按钮将是红色而不是黑色:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->error()
->subject('Invoice Payment Failed')
->line('...');
}
其他邮件通知格式选项
您可以使用 view
方法指定自定义模板来渲染通知电子邮件,而不是在通知类中定义消息的 "行":
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->view(
'mail.invoice.paid', ['invoice' => $this->invoice]
);
}
您可以通过将视图名称作为数组的第二个元素传递给 view
方法来为邮件消息指定纯文本视图:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->view(
['mail.invoice.paid', 'mail.invoice.paid-text'],
['invoice' => $this->invoice]
);
}
或者,如果您的消息只有纯文本视图,您可以使用 text
方法:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->text(
'mail.invoice.paid-text', ['invoice' => $this->invoice]
);
}
自定义发件人
默认情况下,电子邮件的发件人/发件地址在 config/mail.php
配置文件中定义。但是,您可以使用 from
方法为特定通知指定发件地址:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->from('barrett@example.com', 'Barrett Blair')
->line('...');
}
自定义收件人
通过邮件渠道发送通知时,通知系统将自动在可通知实体上查找电子邮件属性。您可以通过在可通知实体上定义 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;
/**
* 邮件通道的路由通知
*
* @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];
}
}
自定义主题
默认情况下,电子邮件的主题是通知类的类名格式化为 "Title Case"。因此,如果您的通知类名为 InvoicePaid
,电子邮件的主题将是 Invoice Paid
。如果您想为消息指定不同的主题,您可以在构建消息时调用 subject
方法:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('Notification Subject')
->line('...');
}
自定义邮件程序
默认情况下,电子邮件通知将使用 config/mail.php
配置文件中定义的默认邮件程序发送。但是,您可以在运行时使用 mailer
方法指定不同的邮件程序:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->mailer('postmark')
->line('...');
}
自定义模板
您可以通过发布通知包的资源来修改邮件通知使用的 HTML 和纯文本模板。运行此命令后,邮件通知模板将位于 resources/views/vendor/notifications
目录中:
shell
php artisan vendor:publish --tag=laravel-notifications
附件
要向电子邮件通知添加附件,请在构建消息时使用 attach
方法。attach
方法接受文件的绝对路径作为其第一个参数:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file');
}
当附加文件到消息时,您还可以通过将数组作为 attach
方法的第二个参数来指定显示名称和/或 MIME 类型:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
与在可邮寄对象中附加文件不同,您不能使用 attachFromStorage
直接从存储磁盘附加文件。您应该改为使用 attach
方法和存储磁盘上文件的绝对路径。或者,您可以从 toMail
方法返回一个 可邮寄对象:
php
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
方法将多个文件附加到消息上:
php
/**
* 获取通知的邮件表示形式
*/
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
方法时,您应提供应分配给附件的文件名:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attachData($this->pdf, 'name.pdf', [
'mime' => 'application/pdf',
]);
}
添加标签和元数据
一些第三方电子邮件提供商,如 Mailgun 和 Postmark,支持消息 "标签" 和 "元数据",可用于对应用程序发送的电子邮件进行分组和跟踪。您可以使用 tag
和 metadata
方法向电子邮件消息添加标签和元数据:
php
/**
* 获取通知的邮件表示形式
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Comment Upvoted!')
->tag('upvote')
->metadata('comment_id', $this->comment->id);
}
如果您的应用程序使用 Mailgun 驱动程序发送电子邮件,您可以参阅 Mailgun 的文档,了解有关 标签 和 元数据 的更多信息。同样,您还可以参阅 Postmark 的文档,了解他们对 标签 和 元数据 的支持。
如果您的应用程序使用 Amazon SES 发送电子邮件,您应该使用 metadata
方法附加 SES "标签" 到消息。
自定义 Symfony 消息
MailMessage
类的 withSymfonyMessage
方法允许您注册一个闭包,该闭包将在发送消息之前与 Symfony Message 实例一起调用。这使您有机会在传递消息之前深度自定义消息:
php
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'
);
});
}
使用邮件对象
如果需要,您可以从通知的 toMail
方法返回完整的 可邮寄对象。当返回 Mailable
而不是 MailMessage
时,您需要使用可邮寄对象的 to
方法指定消息收件人:
php
use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Mail\Mailable;
/**
* 获取通知的邮件表示形式
*/
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;
/**
* 获取通知的邮件表示形式
*/
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
时,它将在浏览器中呈现和显示,允许您快速预览其设计,而无需将其发送到实际的电子邮件地址:
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
Artisan 命令的 --markdown
选项:
shell
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
与所有其他邮件通知一样,使用 Markdown 模板的通知应该在其通知类上定义一个 toMail
方法。但是,不要使用 line
和 action
方法构建通知,而是使用 markdown
方法指定应使用的 Markdown 模板的名称。可以将希望传递给模板的任何数据作为方法的第二个参数传递:
php
/**
* 获取通知的邮件表示形式
*/
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>
按钮组件
按钮组件呈现居中的按钮链接。该组件接受两个参数,一个 url
和一个可选的 color
。支持的颜色包括原色
、绿色
和红色
。您可以根据需要向通知添加任意数量的按钮组件:
blade
<x-mail::button :url="$url" color="green">
View Invoice
</x-mail::button>
面板组件
表格组件允许您将 Markdown 表格转换为 HTML 表格。该组件接受 Markdown 表作为其内容。使用默认的 Markdown 表格对齐语法支持表格列对齐:
blade
<x-mail::panel>
This is the panel content.
</x-mail::panel>
Table 组件
表格组件允许您将 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
资产标签:
shell
php artisan vendor:publish --tag=laravel-mail
此命令会将 Markdown 邮件组件发布到 resources/views/vendor/mail
目录。mail
目录将包含一个 html
和一个 text
目录,每个目录都包含每个可用组件的各自表示形式。您可以随意自定义这些组件。
自定义 CSS
导出组件后, resources/views/vendor/mail/html/themes
该目录将包含一个 default.css
文件。您可以自定义此文件中的 CSS,您的样式将自动内联到 Markdown 通知的 HTML 表示中。
如果您想为 Laravel 的 Markdown 组件构建一个全新的主题,您可以在 html/themes
目录中放置一个 CSS 文件。命名并保存 CSS 文件后,请更新 mail
配置文件的 theme
选项以匹配新主题的名称。
要自定义单个通知的主题,您可以在构建通知的邮件消息时调用 theme
方法。theme
方法接受发送通知时应使用的主题名称:
php
/**
* 获取通知的邮件表示形式
*/
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
命令生成具有适当表架构的迁移:
shell
php artisan make:notifications-table
php artisan migrate
NOTE
如果您的可通知模型使用 UUID 或 ULID 主键,则应在通知表迁移中将 morphs
方法替换为 uuidMorphs
或 ulidMorphs
。
格式化数据库通知
如果通知支持存储在数据库表中,则应在通知类上定义 toDatabase
或 toArray
方法。此方法将接收一个 $notifiable
实体,并应返回一个普通的 PHP 数组。返回的数组将被编码为 JSON 并存储在 notifications
表的 data
列中。让我们看一个 toArray
方法示例:
php
/**
* 获取通知的数组表示形式
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
当通知存储在应用程序的数据库中时,type
列将填充通知的类名。但是,您可以通过在通知类上定义 databaseType
方法来自定义此行为:
php
/**
* 获取通知的数据库类型
*
* @return string
*/
public function databaseType(object $notifiable): string
{
return 'invoice-paid';
}
toDatabase
vs. toArray
广播
通道还使用 toArray
方法来确定要向 JavaScript 支持的前端广播哪些数据。如果要为database
频道和broadcast
频道提供两种不同的数组表示形式,则应定义 toDatabase
方法,而不是 toArray
方法。
访问通知
通知存储在数据库中后,您需要一种便捷的方式来从应通知实体访问它们。该 Illuminate\Notifications\Notifiable
特征包含在 Laravel 的默认 App\Models\User
模型中,包括返回实体通知的通知 Eloquent 关系。要获取通知,您可以像任何其他 Eloquent 关系一样访问此方法。默认情况下,通知将按 created_at
时间戳排序,最近的通知位于集合的开头:
php
$user = App\Models\User::find(1);
foreach ($user->notifications as $notification) {
echo $notification->type;
}
如果您只想检索 “unread” 通知,则可以使用 unreadNotifications
关系。同样,这些通知将按 created_at
时间戳排序,最近的通知位于集合的开头:
php
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
echo $notification->type;
}
NOTE
要从 JavaScript 客户端访问通知,您应该为应用程序定义一个通知控制器,该控制器返回可通知实体(例如当前用户)的通知。然后,您可以从 JavaScript 客户端向该控制器的 URL 发出 HTTP 请求。
将通知标记为已读
通常,您需要在用户查看通知时将其标记为 “已读”。该 Illuminate\Notifications\Notifiable
特征提供了一个 markAsRead
方法,该方法更新通知数据库记录上的 read_at
列:
php
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
但是,您可以直接对通知集合使用 markAsRead
方法,而不是遍历每个通知:
php
$user->unreadNotifications->markAsRead();
您还可以使用 mass-update 查询将所有通知标记为已读,而无需从数据库中检索它们:
php
$user = App\Models\User::find(1);
$user->unreadNotifications()->update(['read_at' => now()]);
您可以删除
通知以将其从表中完全删除:
php
$user->notifications()->delete();
广播通知
先决条件
在广播通知之前,您应该配置并熟悉 Laravel的 事件广播服务。事件广播提供了一种从 JavaScript 支持的前端对服务器端 Laravel 事件做出反应的方法。
格式化广播通知
broadcast
频道使用 Laravel的 事件广播服务广播通知,允许您的 JavaScript 支持的前端实时捕获通知。如果通知支持广播,则可以在通知类上定义 toBroadcast
方法。此方法将接收一个 $notifiable
实体,并应返回一个 BroadcastMessage
实例。如果 toBroadcast
方法不存在,则使用 toArray
方法收集应该广播的数据。返回的数据将被编码为 JSON 并广播到基于 JavaScript 的前端。我们来看一个 toBroadcast
方法示例:
php
use Illuminate\Notifications\Messages\BroadcastMessage;
/**
* 获取通知的可广播表示形式
*/
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
字段,其中包含通知的完整类名。如果您想自定义通知类型
,您可以在 notification 类中定义一个 broadcastType
方法:
php
/**
* 获取正在广播的通知的类型
*/
public function broadcastType(): string
{
return 'broadcast.message';
}
侦听通知
通知将在使用 {notifiable} 格式化的专用频道上广播id}
约定。因此,如果你向 ID 为 1
的 App\Models\User
实例发送通知,该通知将在 App.Models.User.1
专用通道上广播。使用 Laravel Echo 时,您可以使用通知
方法轻松收听频道上的通知:
php
Echo.private('App.Models.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
自定义通知通道
如果您想自定义在哪个频道上广播实体的广播通知,您可以在 notifiable 实体上定义一个 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;
/**
* 用户接收通知广播的频道
*/
public function receivesBroadcastNotificationsOn(): string
{
return 'users.'.$this->id;
}
}
短信通知
先决条件
在 Laravel 中发送 SMS 通知由 Vonage(以前称为 Nexmo)提供支持。在通过 Vonage 发送通知之前,您需要安装 laravel/vonage-notification-channel
和 guzzlehttp/guzzle
软件包:
php
composer require laravel/vonage-notification-channel guzzlehttp/guzzle
该软件包包括一个配置文件。但是,您不需要将此配置文件导出到您自己的应用程序。您可以简单地使用 VONAGE_KEY
和 VONAGE_SECRET
环境变量来定义您的 Vonage 公钥和私有密钥。
定义密钥后,您应该设置一个 VONAGE_SMS_FROM
环境变量,该变量定义默认情况下应从中发送 SMS 消息的电话号码。您可以在 Vonage 控制面板中生成此电话号码:
php
VONAGE_SMS_FROM=15556666666
格式化 SMS 通知
如果通知支持以 SMS 形式发送,则应在通知类上定义 toVonage
方法。此方法将接收一个 $notifiable
实体,并应返回一个 Illuminate\Notifications\Messages\VonageMessage
实例:
php
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage/SMS 表示形式
*/
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();
}
自定义 “From” 编号
如果您想从与 VONAGE_SMS_FROM
环境变量指定的电话号码不同的电话号码发送一些通知,您可以在 VonageMessage
实例上调用 from
方法:
php
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage/SMS 表示形式
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('Your SMS message content')
->from('15554443333');
}
添加客户端引用
如果您想跟踪每个用户、团队或客户的成本,您可以在通知中添加“客户参考”。Vonage 将允许您使用此客户端参考生成报告,以便您可以更好地了解特定客户的 SMS 使用情况。客户端引用可以是最多 40 个字符的任何字符串:
php
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage/SMS 表示形式
*/
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;
/**
* Vonage 频道的路由通知
*/
public function routeNotificationForVonage(Notification $notification): string
{
return $this->phone_number;
}
}
Slack 通知
先决条件
在发送 Slack 通知之前,您应该通过 Composer 安装 Slack 通知通道:
shell
composer require laravel/slack-notification-channel
此外,您必须为 Slack 工作区创建 Slack 应用程序。
如果你只需要向创建 App 的同一 Slack 工作区发送通知,则应确保你的 App 具有 chat:write
、chat:write.public
和 chat:write.customize
范围。这些范围可以从Slack中的“OAuth和权限”应用管理选项卡添加。
Next, copy the App's "Bot User OAuth Token" and place it within a slack
configuration array in your application's services.php
configuration file. This token can be found on the "OAuth & Permissions" tab within Slack:
php
'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 机器人令牌。
格式化 Slack 通知
如果通知支持作为 Slack 消息发送,则应在通知类上定义 toSlack
方法。此方法将接收一个 $notifiable
实体,并应返回一个 Illuminate\Notifications\Slack\SlackMessage
实例。您可以使用 Slack 的 Block Kit API 构建丰富的通知。以下示例可以在 [Slack 的 Block Kit 构建器](https://app.slack.com/block-kit-builder/T01KWS6K23Z#{"blocks":[{"type":"header","text":{"type":"plain_text","text":"Invoice Paid"}},{"type":"context","elements":[{"type":"plain_text","text":"Customer %231234"}]},{"type":"section","text":{"type":"plain_text","text":"An invoice has been paid."},"fields":[{"type":"mrkdwn","text":"Invoice No:\n1000"},{"type":"mrkdwn","text":"Invoice Recipient:\ntaylor@laravel.com"}]},{"type":"divider"},{"type":"section","text":{"type":"plain_text","text":"Congratulations!"}}]})中预览:
php
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('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 交互性
Slack 的 Block Kit 通知系统提供了强大的功能来处理用户交互。要使用这些功能,您的 Slack 应用程序应启用“交互性”,并配置指向应用程序提供的 URL 的“请求 URL”。这些设置可以从Slack中的“交互性和快捷方式”应用管理选项卡进行管理。
在以下示例中使用 actionsBlock
方法,Slack 将向您的“请求 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;
/**
* 获取通知的 Slack 表示形式
*/
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;
/**
* 获取通知的 Slack 表示形式
*/
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');
}
);
});
}
检查 Slack 块
如果您想快速检查您一直在构建的块,您可以在 SlackMessage
实例上调用 dd
方法。dd
方法将生成一个 URL 并将其转储到 Slack 的 Block Kit Builder,该 URL 在浏览器中显示有效负载和通知的预览。您可以将 true
传递给 dd
方法来转储原始有效负载:
php
return (new SlackMessage)
->text('One of your invoices has been paid!')
->headerBlock('Invoice Paid')
->dd();
路由 Slack 通知
要将 Slack 通知定向到相应的 Slack 团队和通道,请在 notifiable 模型上定义 routeNotificationForSlack
方法。此方法可以返回以下三个值之一:
- ``null
- 将路由推迟到通知本身中配置的通道。在构建
SlackMessage时,你可以使用
to` 方法来配置通知中的频道。 - 一个字符串,指定要将通知发送到的 Slack 通道,例如
#support-channel
。 - 一个
SlackRoute
实例,它允许您指定 OAuth 令牌和频道名称,例如SlackRoute::make($this->slack_channel, $this->slack_token)
.此方法应用于向外部工作区发送通知。
例如,从 routeNotificationForSlack
方法返回 #support 通道
会将通知发送到与位于应用程序services.php
配置文件中的机器人用户 OAuth 令牌关联的工作区中的 #support 通道
通道:
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;
/**
* 为 Slack 通道路由通知
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return '#support-channel';
}
}
通知外部 Slack 工作区
NOTE
在向外部 Slack 工作区发送通知之前,必须分发您的 Slack 应用程序。
当然,您经常希望向应用程序用户拥有的 Slack 工作区发送通知。为此,您首先需要为用户获取 Slack OAuth 令牌。值得庆幸的是,Laravel Socialite 包含一个 Slack 驱动程序,可让您使用 Slack 轻松验证应用程序的用户并获取机器人令牌。
获取机器人令牌并将其存储在应用程序的数据库中后,您可以使用 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;
/**
* 为 Slack 通道路由通知
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return SlackRoute::make($this->slack_channel, $this->slack_token);
}
}
本地化通知
Laravel 允许您在 HTTP 请求的当前 locale 以外的 locale 中发送通知,如果通知排队,它甚至会记住这个 locale。
为了实现这一点,该 Illuminate\Notifications\Notification
类提供了一个 locale
方法来设置所需的语言。当评估通知时,应用程序将更改为此区域设置,然后在评估完成后恢复为以前的区域设置:
php
$user->notify((new InvoicePaid($invoice))->locale('es'));
多个可通知条目的本地化也可以通过通知
Facade实现:
php
Notification::locale('es')->send(
$users, new InvoicePaid($invoice)
);
用户首选区域设置
有时,应用程序会存储每个用户的首选区域设置。通过在 notifiable model 上实现 HasLocalePreference
契约,您可以指示 Laravel 在发送通知时使用此存储的 locale:
php
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* 获取用户的首选区域设置
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
一旦你实现了这个接口,Laravel 将在向模型发送通知和 mailables 时自动使用首选的 locale。因此,使用此接口时无需调用 locale
方法:
php
$user->notify(new InvoicePaid($invoice));
测试
您可以使用 Notification
Facade 的 fake
方法来阻止发送通知。通常,发送通知与您实际测试的代码无关。最有可能的是,简单地断言 Laravel 被指示发送给定的通知就足够了。
在调用 Notification
Facade 的 fake
方法后,你可以断言通知被指示发送给用户,甚至检查通知收到的数据:
php
断言未发送任何通知...<?php
use App\Notifications\OrderShipped;
use Illuminate\Support\Facades\Notification;
test('orders can be shipped', function () {
Notification::fake();
// 执行订单发货...
// Assert that no notifications were sent...
Notification::assertNothingSent();
// 断言已向给定用户发送通知...
Notification::assertSentTo(
[$user], OrderShipped::class
);
// 断言未发送通知...
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// 断言已发送给定数量的通知...
Notification::assertCount(3);
});
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();
// 执行订单发货...
// 断言未发送任何通知...
Notification::assertNothingSent();
// 断言已向给定用户发送通知...
Notification::assertSentTo(
[$user], OrderShipped::class
);
// 断言未发送通知...
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// 断言已发送给定数量的通知...
Notification::assertCount(3);
}
}
你可以将一个闭包传递给 assertSentTo
或 assertNotSentTo
方法,以断言已发送的通知通过了给定的 “真值测试”。如果发送了至少一个通过给定真值测试的通知,则断言将成功:
php
Notification::assertSentTo(
$user,
function (OrderShipped $notification, array $channels) use ($order) {
return $notification->order->id === $order->id;
}
);
按需通知
如果您正在测试的代码发送按需通知,则可以测试按需通知是否通过 assertSentOnDemand
方法发送:
php
Notification::assertSentOnDemand(OrderShipped::class);
By passing a closure as the second argument to the assertSentOnDemand
method, you may determine if an on-demand notification was sent to the correct "route" address:
php
Notification::assertSentOnDemand(
OrderShipped::class,
function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
return $notifiable->routes['mail'] === $user->email;
}
);
通知事件
通知发送事件
发送通知时, Illuminate\Notifications\Events\NotificationSending
通知系统将调度事件。这包含 “notifiable” 实体和通知实例本身。您可以在应用程序中为此事件创建事件侦听器:
php
use Illuminate\Notifications\Events\NotificationSending;
class CheckNotificationStatus
{
/**
* 处理给定的事件
*/
public function handle(NotificationSending $event): void
{
// ...
}
}
如果 NotificationSending
事件的事件侦听器从其 handle
方法返回 false
,则不会发送通知:
php
/**
* 处理给定的事件
*/
public function handle(NotificationSending $event): bool
{
return false;
}
在事件侦听器中,您可以访问事件的 notifiable
、notification
和 channel
属性,以了解有关通知收件人或通知本身的更多信息:
php
/**
* 处理给定的事件
*/
public function handle(NotificationSending $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
}
通知已发送事件
发送通知时, Illuminate\Notifications\Events\NotificationSent
通知系统将调度事件。这包含 “notifiable” 实体和通知实例本身。您可以在应用程序中为此事件创建事件侦听器:
php
use Illuminate\Notifications\Events\NotificationSent;
class LogNotification
{
/**
* 处理给定的事件
*/
public function handle(NotificationSent $event): void
{
// ...
}
}
在事件侦听器中,您可以访问事件的 notifiable
、notification
、channel
和 response
属性,以了解有关通知收件人或通知本身的更多信息:
php
/**
* 处理给定的事件
*/
public function handle(NotificationSent $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
// $event->response
}
自定义频道
Laravel 附带了一些通知通道,但您可能希望编写自己的驱动程序以通过其他通道发送通知。Laravel 让它变得简单。首先,定义一个包含 send
方法的类。该方法应接收两个参数:$notifiable
和 $notification
。
在 send
方法中,您可以调用通知上的方法,以检索通道理解的消息对象,然后根据需要将通知发送到 $notifiable
实例:
php
<?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
<?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
{
// ...
}
}