Laravel 8队列处理通知时NumberFormatter不允许序列化错误如何解决?
问题根因分析
你遇到的报错核心是序列化时机和序列化对象的差异,和toArray()方法本身无关:
- 你
PaymentLog模型的total_amount_formatted访问器大概率返回的不是纯字符串,而是一个内置了NumberFormatter实例的货币格式化对象(比如常用的Money包返回的格式化对象、或者自定义的实现了__toString()的货币类),该对象只会在直接输出时自动转字符串,序列化时会保留内部所有属性,包括不可序列化的NumberFormatter实例。 - 部分任务成功是因为对应订单没有关联
paymentLog,optional()返回null,没有触发序列化问题。
为什么database通道正常、broadcast通道报错
两个通道的队列执行逻辑完全不同:
database通道执行流程
- 通知推队列时,仅序列化
NewOrderNotification自身的两个属性:$orders(Laravel序列化模型时仅会保存主键,不会序列化模型属性)、$sendingToTeamMembers(布尔值),两个属性都不涉及NumberFormatter,所以推队列成功。 - 队列执行时反序列化通知,调用
toArray()得到结果后直接写入数据库,写入时会自动把返回的货币对象转成字符串,全程不需要序列化toArray()的返回结果,所以不会触发报错。
broadcast通道执行流程
- 通知推队列时序列化逻辑和database通道一致,所以不会报错。
- 队列执行到broadcast通道时,会生成
BroadcastNotification广播事件,这个事件本身默认会被推到广播队列二次序列化,此时toArray()的返回结果会作为事件payload的一部分被序列化,里面的货币对象携带的NumberFormatter实例就会触发序列化失败的报错。
修复方案
最简单的修复方式是强制把格式化结果转成纯字符串,避免任何对象被序列化:
修改getPaymentAmountSafely方法:
protected function getPaymentAmountSafely(Order $order) { return (string) optional($order->paymentLog)->total_amount_formatted; }
也可以直接修改PaymentLog的getTotalAmountFormattedAttribute访问器,直接返回字符串,避免后续其他地方再出现同类问题。
内容的提问来源于stack exchange,提问作者Louis L
相关产品推荐
相关产品推荐

