Skip to main content
当你想从包中增加通知的投递方式时,需要把渠道的选择与通知目标的确定分开设计。本页以一个把运维事件记录到日志的小型渠道为例,说明包所提供类的解析路径,以及更新时需要进行的验证。即使替换为向外部服务发送,渠道与通知之间的职责划分也是一样的。 本页基于对 Laravel Framework v13.38.0 实现的确认。通知的基本操作请参阅通知,包的测试环境请参阅包的测试。

类名决定投递方式,路由决定目标

via() 返回的渠道类名指定要调用哪个对象的 send()。而从 routeNotificationFor() 获取的值则是该渠道使用的目标。两者不必使用相同的名称。 NotificationSender 会把 via() 选出的渠道名传给 ChannelManager::driver()。如果 ChannelManager 在内置名称和已注册名称中都找不到,而该名称是一个实际存在的类,就会通过容器解析这个类。如果只按类名使用,则不需要 Notification::extend() 或包的服务提供者。你只需确保 Composer 的自动加载能找到该类,并且容器能够解析其构造函数的依赖。

在包中提供渠道本体

示例中的 Acme\OperationsLog 包会把不含敏感信息的事件名与通知目标运维团队名一起记录下来。在 Laravel 应用中,Psr\Log\LoggerInterface 被绑定到日志服务,因此可以通过构造函数接收。
接收的对象预期是使用 Notifiable trait 的模型,或由 Notification::route() 创建的按需通知目标。前者返回 routeNotificationForOperationsLog() 的值,后者返回通过 route('operationsLog', ...) 注册的值。为避免在没有目标的情况下被当作已投递处理,本示例会抛出异常。替换为实际的外部投递时,也应确认目标和发送结果,设计成不会把失败吞掉并当作成功。
请不要在记录到日志的事件名或目标中包含访问令牌或个人信息。本示例是用于演示渠道解析与测试的日志发送,并不是向外部服务投递的实现。

在使用方定义通知与目标

应用中的通知返回渠道类,并且只定义传给渠道的消息。使用 ShouldQueue 和 Queueable 后,普通的 notify() 调用会经由队列处理。
当场指定目标时,Notification::route() 的键不是渠道类名,而是渠道内部调用的 routeNotificationFor('operationsLog', ...) 的键。
如果从应用的模型发送,请在使用 Notifiable 的模型中准备以下方法。
对该模型调用 $user->notify(new HealthCheckPassed()) 也会选中同一个渠道。请配置队列连接,并启动处理 operations-log 队列的 worker。
OperationsLogChannel 本身会按渠道保留在 Manager 中,因此不要把通知目标或通知内容保存在渠道的属性中,而应每次从 send() 的参数中取出。这样即使是长时间运行的队列 worker,也不会把上一条通知的信息带入下一次处理。

需要简短别名时

只有当你希望使用者在 via() 中指定简短名称而不是类名时,才需要在包的普通服务提供者中使用 Notification::extend()。
请通过包自动发现等方式,让服务提供者在投递处理开始前被加载。在选择别名的通知中,把 via() 改为 ['acme-operations-log'],并把 viaQueues() 的键也改为同一名称。用于获取目标的 operationsLog 是另一个键,无需修改。请使用不会与内置渠道名冲突的名称。 Manager 会按名称保留已解析的渠道。即使在同一进程中重新注册别名的创建处理,已经创建的实例也不会被替换。注册方式的变更,也请在新启动的应用或 worker 中进行验证。

在 fake 之外测试投递处理

Notification::fake() 用于验证发送通知的意图。它无法确认渠道的 send() 或路由获取是否正常运作。在投递测试中,请使用真实的通知 Manager,并把 LoggerInterface 替换为测试用实现。 下面是从 Testbench 的 TestCase 运行的示例。使用 notifyNow() 后,即使是 ShouldQueue 通知,也可以不启动 worker 而直接执行渠道的处理。
此外,还要确认没有目标时会抛出异常,以及提供别名时能够在全新启动后解析。如果经由队列使用,还应在另外运行队列 worker 的验证应用中,测试任务能够到达 operations-log 队列并被处理。 NotificationSender 会把 ShouldQueue 通知按渠道拆分为任务,并根据 viaQueues() 的键选择队列。如果渠道的 send() 抛出异常,会触发 NotificationFailed 并重新抛出异常;正常返回时则触发 NotificationSent。队列的重试设置遵循应用的配置。请注意,如果在渠道内部吞掉异常,即使投递失败也会被当作成功处理。

参考的一手资料

最后修改于 2026年10月22日