应使用 symfony/notifier 统一管理推送通道,避免堆砌多个 sdk;它作为消息路由层支持 slack、sms、fcm 等十余种通道,解耦用户模型、统一错误处理,并通过 transport 配置与 via() 方法实现灵活通道选择。

直接上结论:别堆砌多个推送包,用 symfony/notifier 统一收口,再按需配 transport —— 它不是“又一个推送库”,而是消息路由层,天然支持 Slack、SMS、FCM、WebPush、Mercure 等十几种通道,且不绑定框架。
为什么不用每个平台单独装 SDK?
硬集成 feugene/firebase-notifications-laravel、laravel-notification-channels/webpush、twilio/sdk 这类包,短期能跑通,但很快会暴露三类问题:
- 用户模型要反复加 trait(
HasPushSubscriptions、FcmNotifiable),字段和方法名冲突概率高 - 每新增一个渠道就得重写通知类的
toXxx()方法,逻辑重复、测试难覆盖 - 错误处理分散:Twilio 返回
429、FCM 返回mismatch_sender_id、Slack 返回invalid_attachments,日志里全混在一起,没法统一降级或重试
symfony/notifier 把这些差异收进 transport 层,你只管发 $notifier->send(new ChatMessage('Hi')),底层自动选通道、转格式、捕获 HTTP 错误并打 warning 日志。
transport 配置最容易漏掉的三件事
装完 composer require symfony/notifier 后,90% 的人卡在 transport 不生效——不是代码写错,是配置没闭环:
-
notifier.yaml里必须显式声明chatter_transports或texter_transports,空数组 ≠ 不启用,而是彻底跳过该类型通道 - DSN 必须用环境变量注入,比如
SLACK_DSN=slack://xoxb-xxx:xxx@default,硬编码在 YAML 里会被缓存忽略 - Mercure、FCM 这类需要额外扩展包:
composer require symfony/mercure-notifier或composer require symfony/fcm-notifier,否则mercure://、fcm://协议根本无法解析
验证是否生效?运行 php bin/console notifier:transport:debug,它会列出所有已加载 transport 及 DSN 解析状态,比翻日志快十倍。
WebPush 和 FCM 共存时的 key 冲突
WebPush 要 VAPID 密钥,FCM 要服务账户 JSON,两个都走浏览器推送,但底层协议完全不同。常见踩坑点:
- VAPID 私钥(
VAPID_PRIVATE_KEY)不能和 FCM 的private_key混用 —— 前者是 PEM 格式椭圆曲线密钥,后者是 Google 生成的 RSA JSON,强行复用会导致BadJwtToken或Unauthorized - WebPush 的
VAPID_SUBJECT必须是mailto:或https://开头的 URI,iOS/Safari 2023+ 版本强制校验,漏配就静默失败 - FCM 的
fcm://DSN 中,project_id必须和 Firebase 控制台项目 ID 完全一致(区分大小写),且服务账户 JSON 里的client_email要有 Firebase Cloud Messaging API 的调用权限
复杂点不在装包,而在 transport 之间的边界划分——Notifier 不帮你做决策,只提供路由能力。哪个用户走 WebPush、哪个走 FCM,得靠你在 Notification 类里重写 via() 方法动态返回通道数组,而不是靠配置文件硬编码。











