Laravel队列通知ShouldQueue:数据库仅存裸模型无关联问题
问题原因分析
这是因为Laravel队列对Eloquent模型的序列化机制导致的:
当你的通知实现ShouldQueue接口后,整个通知实例会被序列化并存储到队列系统中。默认情况下,Laravel会把Eloquent模型序列化为"模型标识符"(只包含模型的主键、类名等基础信息),而不是完整的模型数据(包括预加载的关联)。
当队列处理器从队列中取出通知并执行时,它会根据这个标识符重新查询数据库获取模型实例,但这个重新查询的操作不会自动带上你之前预加载的关联关系,所以最终存入Notifications表的data字段就只有裸模型数据,丢失了预加载的关联。
而当你不实现ShouldQueue时,通知是同步执行的,直接使用你在控制器中已经预加载好关联的内存模型实例,所以data字段能保存完整的关联数据。
解决方案
这里有几种常用的解决办法,你可以根据需求选择:
1. 提前将模型转为包含关联的数组(推荐)
在通知类中,将预加载好的模型直接转为数组,这样序列化的时候存储的是数组而非模型实例,队列执行时就不会重新查询模型,直接使用这个包含关联的数组:
修改通知类的toDatabase方法:
public function toDatabase($notifiable) { // toArray()会自动包含已经预加载的关联数据 return [ 'service_order' => $this->serviceOrder->toArray() ]; }
或者也可以在构造函数中就转成数组,避免后续操作的意外:
public function __construct(ServiceOrder $serviceOrder) { // 提前转换为包含关联的数组 $this->serviceOrder = $serviceOrder->toArray(); } public function toDatabase($notifiable) { return [ $this->serviceOrder ]; }
2. 在队列执行时重新加载关联
如果希望队列执行时能获取最新的模型数据,可以在toDatabase方法中检查并重新加载所需的关联:
public function toDatabase($notifiable) { // 如果关联未加载,则重新加载 if (!$this->serviceOrder->relationLoaded('customer') || !$this->serviceOrder->relationLoaded('serviceOrderItem')) { $this->serviceOrder->load([ 'serviceOrderItem', 'serviceOrderItem.serviceType', 'serviceOrderItem.customerOrderAsset', 'customer' ]); } return [ $this->serviceOrder ]; }
这种方法的好处是数据始终是最新的,但会额外增加一次数据库查询。
内容的提问来源于stack exchange,提问作者HyperT
相关产品推荐
相关产品推荐

