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

Laravel关联查询问题:按用户最新指定状态事件的procedure_id过滤

按用户最新有效事件的procedure_id过滤用户的解决方案

问题分析

你遇到的核心问题有两个:

  1. 用latest('start')定义的关联搭配whereHas查询时,会匹配用户所有符合状态的事件(而非最新的那条),导致只要用户历史有匹配procedure_id的事件就会被纳入结果,不符合需求。
  2. 直接替换为latestOfMany()后,关联会先取用户全局最新的事件(不管状态),再过滤whereIn条件,若用户最新事件是upcoming状态,就会返回NULL,无法正确获取有效事件。

正确实现方案

方案1:修改关联为带筛选条件的ofMany(推荐,可复用)

Laravel的ofMany方法支持在筛选候选记录时添加条件,确保先过滤出有效事件,再取其中最新的那条。修改关联如下:

public function latestValidEvent(): HasOne
{
    return $this->hasOne(Event::class, 'client_id', 'id')
        ->ofMany(
            // 指定按start字段取最大值(最新)
            ['start' => 'max'],
            // 先筛选出状态为completed/waiting的事件
            function ($query) {
                $query->whereIn('status', ['completed', 'waiting']);
            }
        );
}

之后直接用whereHas过滤即可,此时latestValidEvent关联的是用户最新的有效事件,whereHas只会检查这条事件是否匹配procedure_id:

$procedureFilter = $request->get('procedurе_filter');

$patients = User::with('status', 'userMeta', 'latestValidEvent', 'latestValidEvent.employee', 'latestValidEvent.procedure')
    ->whereHas('role', function ($query) {
        $query->where('name', 'client');
    })
    ->whereHas('latestValidEvent', function ($query) use ($procedureFilter) {
        $query->where('procedure_id', $procedureFilter);
    })
    ->orderBy('created_at', $order)
    ->paginate(10);

方案2:用whereExists子查询直接过滤(无需修改关联)

如果不想修改现有关联,可以直接在查询中用whereExists结合子查询,精准匹配用户最新有效事件的procedure_id:

$procedureFilter = $request->get('procedurе_filter');

$patients = User::with('status', 'userMeta', 'latestEvent', 'latestEvent.employee', 'latestEvent.procedure')
    ->whereHas('role', function ($query) {
        $query->where('name', 'client');
    })
    ->whereExists(function ($query) use ($procedureFilter) {
        $query->select(DB::raw(1))
            ->from('events')
            ->whereColumn('events.client_id', 'users.id')
            ->whereIn('events.status', ['completed', 'waiting'])
            ->where('events.procedure_id', $procedureFilter)
            // 确保这条事件是该用户所有有效事件中最新的
            ->whereRaw('events.start = (
                SELECT MAX(start) 
                FROM events e 
                WHERE e.client_id = users.id 
                AND e.status IN ("completed", "waiting")
            )');
    })
    ->orderBy('created_at', $order)
    ->paginate(10);

方案说明

  • 方案1的优势在于关联可复用,后续任何需要获取用户最新有效事件的场景都能直接调用latestValidEvent。
  • 方案2无需修改现有关联逻辑,适合一次性的查询需求。
  • 两种方案都能避免N+1问题,且效率较高,即使用户有数百条事件,子查询/关联子查询的性能也能满足需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:25:08