You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel实现hasMany关联数据拆分为独立记录并支持分页查询

Laravel 实现方案

核心思路

使用 UNION 合并两类数据集,完全在数据库层面完成查询逻辑,天然支持原生分页,性能远高于 ORM 读取后手动拼装:

  1. 第一类是所有 Post 本身的记录,评论相关字段统一设为 null
  2. 第二类是所有关联了 Post 的 Comment 记录,同步携带对应 Post 的公共字段

具体代码实现

首先引入依赖门面:

use Illuminate\Support\Facades\DB;

编写查询逻辑:

// 子查询1:所有Post本身的记录,评论字段置空
$postRecords = Post::select(
    'post_id',
    DB::raw('NULL as comment_id'),
    'name',
    'owner_name',
    DB::raw('NULL as comment')
);

// 子查询2:关联了Post的评论记录,携带Post公共字段
$commentRecords = Comment::join('posts', 'comments.post_id', '=', 'posts.post_id')
    ->select(
        'posts.post_id',
        'comments.comment_id',
        'posts.name',
        'posts.owner_name',
        'comments.comment'
    );

// 合并两个查询并排序,保证同一个Post的自身记录排在对应评论前
$mergedQuery = $postRecords->unionAll($commentRecords)
    ->orderBy('post_id')
    ->orderByRaw('ISNULL(comment_id) DESC')
    ->orderBy('comment_id');

// 直接使用Laravel原生分页,每页10条可自定义
$perPage = 10;
$paginatedResult = $mergedQuery->paginate($perPage);

如果需要将查询结果转为 Post 模型实例,可补充调用 hydrate 方法:

$modelList = Post::hydrate($paginatedResult->items());

注意事项

  • 两个子查询的 select 字段数量、顺序、字段类型必须完全一致,否则 UNION 会报错
  • 优先使用 unionAll 代替 union,两类数据不存在重复,unionAll 无需去重性能更高
  • 如果需要添加过滤条件(如筛选指定 owner 的内容),需要在两个子查询中同时添加相同的过滤规则,避免结果遗漏
  • 若遇到 UNION 分页自动计算总数错误的情况,可手动传入总条数:
    $total = $postRecords->count() + $commentRecords->count();
    $paginatedResult = $mergedQuery->paginate($perPage, ['*'], 'page', request('page', 1), $total);
    

内容的提问来源于stack exchange,提问作者madusanka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 08:18:02