如何使Laravel查询构造器返回与ORM一致的查询结果
你当前写的查询构造器代码无法得到和ORM一致结果的核心原因是:Eloquent 预加载关联(with())的底层逻辑并不是通过 JOIN 联表实现,而是分两次独立查询后,在PHP层面完成关联数据的匹配挂载,天然不会出现联表带来的主表数据重复问题;而你用 JOIN 直接关联blogs表时,一条分类对应N条博客就会生成N行扁平化结果,仅靠groupBy只能做聚合统计,无法自动组装嵌套的关联博客结构。
推荐实现方案(100%匹配ORM返回结构,性能与原生ORM一致)
完全复刻Eloquent预加载的底层逻辑,分两次查询后手动组装结构,避免JOIN的笛卡尔积问题:
// 1. 查询符合条件的分类,对应原ORM的where、whereHas逻辑 $categories = DB::table('categories') ->where('language_id', $lang->id) // 等价于whereHas('blogs'),用EXISTS判断关联存在性,性能远高于JOIN去重 ->whereExists(function ($query) { $query->select(DB::raw(1)) ->from('blogs') ->whereColumn('blogs.category_id', 'categories.id'); }) // 如果你需要统计每个分类的博客总数,直接加子查询即可,不需要GROUP BY ->addSelect([ 'totalblogs' => DB::table('blogs') ->selectRaw('COUNT(id)') ->whereColumn('category_id', 'categories.id') ]) ->get() ->keyBy('id'); // 以分类ID做数组键,方便后续匹配关联博客 // 2. 查询所有关联博客,对应with()里的查询逻辑 $blogs = DB::table('blogs') ->whereIn('category_id', $categories->keys()) ->latest() ->get() ->groupBy('category_id'); // 按所属分类ID分组 // 3. 挂载关联博客,得到和ORM完全一致的结构 $result = $categories->map(function ($category) use ($blogs) { $category->blogs = $blogs->get($category->id, collect()); return $category; })->values();
最终返回的$result结构和你原ORM查询返回的集合结构完全一致:每个分类对象下挂载blogs属性,值为该分类下按创建时间倒序排列的所有博客集合,无重复分类数据。
为什么不推荐用JOIN实现
如果你非要用单次JOIN查询完成,需要手动遍历结果集做分组和字段映射,不仅要处理字段重名冲突(比如分类和博客都有id、title字段时必须加别名),数据量较大时联表的性能也远低于两次独立查询,组装代码的维护成本更高。
内容的提问来源于stack exchange,提问作者muhammad adnan
相关产品推荐
相关产品推荐

