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

Laravel9 按关联模型日期排序查询并获取关联数据方法

问题原因

你的两种写法不符合需求的核心原因如下:

  • 第一种whereHas写法仅做关联存在性校验:只会筛选出「至少有1篇已发布Post」的用户,完全不会根据Post的更新时间对用户排序,最终取4个用户时默认按用户表主键排序,和「拥有最新Post的用户」筛选逻辑完全无关。
  • 第二种join写法未做用户去重:连表后每一行结果对应一篇Post,按Post更新时间倒序后,若最新的4篇Post都属于同一个用户,取前4条自然全是同一个用户的重复记录;另外你在with闭包中直接调用get()是错误写法,会提前触发SQL执行,破坏预加载逻辑。
正确实现代码

实现逻辑拆为两步:先从已发布Post中按更新时间倒序,取出不重复的前4个用户ID;再查询对应ID的用户,同时预加载每个用户的2篇最新已发布Post即可。

// 1. 取出拥有最新已发布Post的4个不重复用户ID,顺序和Post更新时间倒序一致
$targetUserIds = Post::query()
    ->where('state', PostStateEnum::PUBLISHED)
    ->orderByDesc('updated_at')
    ->distinct()
    ->pluck('user_id')
    ->take(4);

// 2. 查询目标用户,同时预加载每个用户的2篇最新已发布Post
$users = User::query()
    ->whereIn('id', $targetUserIds)
    // 保证用户顺序和Post最新时间排序一致
    ->orderByRaw('FIELD(id, ' . $targetUserIds->join(',') . ')')
    ->with([
        'posts' => function ($query) {
            $query->where('state', PostStateEnum::PUBLISHED)
                ->orderByDesc('updated_at');
        }
    ])
    ->get()
    // 每个用户只保留2篇最新Post,兼容所有Laravel版本,无需额外扩展
    ->each(fn ($user) => $user->setRelation('posts', $user->posts->take(2)));

优化说明

  • 上述写法没有在预加载闭包中直接写take(2),是因为原生Eloquent预加载时的limit是针对整个关联查询的,会导致所有用户总共只查出2条Post,而非每个用户2条。用查询后集合截取的方式兼容所有Laravel版本,不需要安装额外扩展包。
  • 如果你的项目使用Laravel 10+,或者安装了staudenmeir/eloquent-eager-limit扩展包,可以直接在User模型中定义专门的关联来实现预加载阶段的limit,减少内存占用:
// User模型内新增关联
public function latestPublishedPosts()
{
    return $this->hasMany(Post::class)
        ->where('state', PostStateEnum::PUBLISHED)
        ->orderByDesc('updated_at')
        ->limit(2);
}

查询时直接加载该关联即可:

$users = User::query()
    ->whereIn('id', $targetUserIds)
    ->orderByRaw('FIELD(id, ' . $targetUserIds->join(',') . ')')
    ->with('latestPublishedPosts')
    ->get();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:36:25