Laravel 5.6 队列Job中withCount生成字段无法访问问题
问题根因
这个问题是Laravel队列中SerializesModels trait的固有机制导致的:
- 你当前的Job类引入了
SerializesModelstrait,该trait会重写模型的序列化逻辑:传入Job构造函数的Eloquent模型不会被完整序列化,只会保存模型主键、数据库连接等基础标识信息。 - 哪怕你使用的是
sync同步队列驱动,任务分发时依然会走完「序列化存储→反序列化解析」的全流程。反序列化阶段,框架会根据之前保存的模型主键,重新从数据库查询一次对应的模型实例。 - 这次重新查询不会自动带上你在控制器中定义的
withCount('AbstractNews')关联计数逻辑,也不会保留你在构造函数中手动给模型动态挂载的abcount属性,最终就出现handle方法中abstract_news_count字段丢失的现象。
解决方案
以下方案任选其一即可:
方案1:Job执行时重新查询目标模型(推荐)
这是最稳妥的实现方式,还能避免队列等待过程中模型数据变更导致的数据不一致问题:
- 控制器分发Job时只传模型ID,不传完整模型实例
- 在Job的handle方法中重新执行带
withCount的查询,获取需要的模型数据
代码修改示例:
// 控制器分发代码调整 $conferenceId = $unserializeData['confid']; $this->dispatch(new SendtronAutomatedEmailJob($conferenceId, $unserializeData, $attachments));
// Job类调整 class SendtronAutomatedEmailJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $conferenceIterationId; protected $confrenceData; private $formData; private $files; public function __construct($conferenceIterationId, $formData, $files) { // 只存ID,不存完整模型 $this->conferenceIterationId = $conferenceIterationId; $this->formData = $formData; $this->files = $files; } public function handle() { // 任务执行时重新查询,带上需要的关联和计数 $this->confrenceData = ConferenceIteration::with('AbstractNews') ->withCount('AbstractNews') ->findOrFail($this->conferenceIterationId); // 后续原有逻辑不变,此时可以正常拿到abstract_news_count字段 } }
方案2:单独存储需要的计数值为类属性
如果不想改查询逻辑,可以把需要的计数值从模型中取出来,单独存为Job的普通类属性,这类基础类型数据序列化时不会被框架重新解析,不会丢失:
class SendtronAutomatedEmailJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $confrenceData; // 新增单独的属性存计数 protected $abstractNewsCount; private $formData; private $files; public function __construct($confrenceData, $formData, $files) { $this->confrenceData = $confrenceData; // 把计数单独存,不要挂在模型实例上 $this->abstractNewsCount = $confrenceData->abstract_news_count; $this->formData = $formData; $this->files = $files; } public function handle() { // 直接通过$this->abstractNewsCount即可拿到计数值 } }
方案3:移除SerializesModels trait(不推荐)
直接删除Job类中use的SerializesModels trait,框架会把构造时传入的模型完整序列化,不会触发重新查库的逻辑,原有abstract_news_count字段和动态挂载的属性都会保留。
注意:该方式会将模型所有属性、已加载的关联全部序列化存入队列,关联数据量大时会导致队列存储体积暴涨,且Job执行时拿到的是任务分发时刻的模型快照,不会同步数据库的最新数据,非特殊场景不建议使用。
内容的提问来源于stack exchange,提问作者Bhimani Rutvik
相关产品推荐
相关产品推荐

