Notifier需通过通道路由机制实现多渠道通知,必须正确配置transport、动态返回channels并确保Recipient实现对应方法,否则渠道静默失效。

能同时发,但不是“调一次 send() 就全渠道齐飞”,得靠 Notifier 的通道路由机制 + 正确的 getChannels() 实现。硬编码 ['email', 'sms', 'slack'] 很容易漏掉校验、触发静默失败。
Notifier 必须启用 email/sms/slack 三个 transport
默认只有 chat 通道可用,其他全得手动配。缺一个,对应渠道就完全不走——而且不报错、不抛异常、日志里也难查。
- 邮件:装
symfony/mailer,在config/packages/notifier.yaml里加transports: { email: 'smtp://...' } - 短信:选网关(如 Twilio 或阿里云),装对应包(
symfony/twilio-notifier或symfony/aliyun-notifier),再配sms: 'twilio://...';环境变量名必须严格匹配文档,比如TWILIO_AUTH_TOKEN写成TWILIO_TOKEN就静默失效 - Slack:装
symfony/slack-notifier,DSN 格式是slack://token@default?channel=#general;token是 Slack App 的 Bot User OAuth Token,不是 Webhook URL
Notification::getChannels() 必须动态判断,不能写死
写 return ['email', 'sms', 'slack'] 看似省事,实则埋雷:用户没填手机号却仍尝试发短信,TwilioTransport 会因 getPhone() 返回 null 而跳过,你根本收不到失败反馈。
- 推荐逻辑:
return array_filter([ $recipient->getEmail() ? 'email' : null, $recipient->getPhone() ? 'sms' : null, $recipient->getChatName() ? 'slack' : null ]) - 如果 Slack 通知只发给管理员,就别依赖
getChatName()是否存在,而应查$recipient instanceof AdminRecipient或加个canReceiveSlack()方法 - 避免在
__construct()里传渠道数组——Notifier 的设计目标就是解耦渠道和业务逻辑
Recipient 必须实现全部渠道对应的方法
Notifier 不认字符串或数组,只认实现了 RecipientInterface 的对象。它会按 channel 名自动调方法:email → getEmail(),slack → getChatName(),sms → getPhone()。少一个方法,对应渠道就直接跳过。
- 即使某渠道暂时不用,也要提供空实现,比如
public function getPhone(): ?string { return null; } -
getChatName()返回的是 Slack 用户名(如wang)或频道名(如#alerts),不是 Webhook URL - 别用
User::class直接当 recipient——它大概率没实现这些方法;要么继承它加方法,要么建个NotifiableUser包装一层
发不出时优先检查 ChannelPolicy 和 transport 别名
最常被忽略的点:你配了 transports: { sms: 'twilio://' },但 getChannels() 返回的是 ['textmessage'],Notifier 就找不到对应 transport,结果所有短信都丢进黑洞。
- 确保
getChannels()返回的字符串和notifier.yaml里transports:下的 key 完全一致(大小写、下划线、连字符都不能错) - 如果要用复杂策略(比如 VIP 用户三渠道、普通用户只发邮件),别在
Notification类里写 if-else,而是实现自定义ChannelPolicy并在配置中指定 - 开发阶段用
bin/console debug:notifier查当前启用的 transport 和默认 policy,比翻配置快得多











