Laravel中不使用join关联如何对Eloquent查询按父模型排序
Laravel Eloquent 无Join按父模型字段排序实现
原代码不生效原因
whereHas('users')底层生成的是EXISTS子查询,仅用来筛选「存在关联用户」的Post记录,主查询本身不会加载users表,直接调用orderBy('users.created_at')时,主查询上下文中不存在users表字段,自然无法得到预期排序结果。
注意:在whereHas的闭包内添加orderBy是无效的,该排序规则仅作用于内部的EXISTS子查询,不会对主查询结果排序产生任何影响。
实现代码
不需要手动编写主表Join语句,通过关联子查询把需要用到的父表排序字段临时追加到主查询,再基于该字段排序即可,完全符合不使用Join关联的要求:
$posts = Post::whereHas('users') ->addSelect([ // 临时追加关联用户的创建时间作为排序字段 'user_create_time' => User::select('created_at') // 根据实际关联关系修改外键匹配逻辑 // 若为Post属于User的一对多关联(posts表带user_id外键)用以下条件: ->whereColumn('id', 'posts.user_id') // 若为Post和User多对多关联(存在post_user中间表),替换为以下匹配逻辑: // ->join('post_user', 'users.id', '=', 'post_user.user_id') // ->whereColumn('post_user.post_id', '=', 'posts.id') ->latest() // 一个Post对应多个User时,取最新创建的User时间排序,按需调整 ->limit(1) ]) ->orderBy('user_create_time', 'desc') // 排序规则按需调整为asc/desc ->get();
补充说明
- 多对多场景下子查询内部用到的join仅作用于取排序值的子查询,主查询依然没有使用join关联父表,符合需求。
- 临时追加的
user_create_time是查询过程的辅助字段,如果不需要在结果中展示,遍历结果时unset即可,不会污染原模型的字段结构。 - 单Post对应多User的场景必须明确子查询的取值规则(最新/最早/符合指定条件的User),否则排序结果会出现歧义。
内容的提问来源于stack exchange,提问作者Ahmad Reza Azimi
相关产品推荐
相关产品推荐

