Laravel查询范围无结果时返回空数组,如何改为返回null?
解决Laravel关联预加载返回空数组而非null的问题
看起来你是想给每个Task预加载它最新的已完成Action,没匹配结果时希望返回null,但现在得到的是空数组对吧?问题出在你用了查询范围(scope)来处理这个需求,而没有利用Laravel的关联特性。
问题根源
当你在with()里尝试加载upcomingAction时,如果你是把它当作查询范围来用,那本身就用错了——with()接受的是关联名称,不是scope名称。而且就算你正确应用了scope,take(1)返回的仍然是一个查询构建器,预加载时Laravel会把查询结果组装成集合,空集合就会表现为空数组,而不是单个null。
正确解决方案:定义一对一关联
既然你要的是每个Task对应的单个最新Action,直接在Task模型里定义一个hasOne关联就可以了,语义更清晰,也能自动返回单个实例或null:
// 在你的Task模型中 public function upcomingAction() { // 关联到Action模型,按最新排序,过滤completed_at不为空的记录 return $this->hasOne(Action::class)->latest()->whereNotNull('completed_at'); }
然后你原来的预加载代码不需要大改,直接用这个关联名称即可:
return $subject->load([ 'tasks' => function($query) { $query->with('upcomingAction'); } ]);
这样处理后,当某个Task没有匹配的Action时,upcoming_action字段就会返回null,而不是空数组,完全符合你的需求。
为什么这个方法可行?
hasOne关联的设计就是返回单个关联模型实例,当没有匹配记录时默认返回null。预加载时Laravel会批量查询所有需要的Action记录,然后自动匹配到对应的Task上,既避免了N+1查询问题,又能保证返回格式符合预期。
如果你坚持要用查询范围?
如果因为某些原因必须用scope,那你需要调整使用方式——不能通过with()加载,而是在获取Task集合后逐个处理,但这样会带来N+1查询问题,不推荐。比如:
// 获取tasks后逐个调用scope $subject->tasks->each(function($task) { $task->upcoming_action = $task->upcomingAction(); });
但显然,定义一对一关联是更优雅、高效的方案。
内容的提问来源于stack exchange,提问作者Blagoh
相关产品推荐
相关产品推荐

