如何通过Eloquent/查询构造器获取最后状态为working的工单
解决Laravel Eloquent获取最后状态为"working"的工单问题
嘿,我明白你遇到的问题了——last()方法在这里没法像你想的那样工作,因为它是Eloquent集合的方法,只能在已经把数据从数据库拉出来之后用,没法在数据库查询阶段帮你筛选出每个工单的最后一条状态记录。
要实现你要的效果(只保留那些最新状态是"working"的工单),我们需要先找到每个工单对应的最新状态记录,再从中筛选状态为"working"的。下面给你几种可行的方案:
方案1:用子查询关联筛选(推荐,Eloquent风格)
假设你的Status表有created_at字段用来判断状态的先后顺序(或者用自增的id字段也可以),我们可以先写一个子查询获取每个工单的最新状态记录,再关联筛选:
$tickets = Ticket::whereHas('status_table', function ($query) { // 子查询:获取当前工单的最新状态记录ID $latestStatus = Status::select('id') ->whereColumn('ticket_id', 'tickets.id') ->orderBy('created_at', 'desc') // 按时间倒序,取最新的 ->limit(1); $query->whereIn('id', $latestStatus) ->where('status_column', 'working'); })->get();
方案2:先分组获取最新状态ID,再筛选
这种方式先拿到所有工单的最新状态ID集合,再用这个集合去筛选:
// 第一步:分组获取每个工单的最新状态ID(用MAX(id)或者按created_at排序取最新都可以) $latestStatusIds = Status::selectRaw('MAX(id) as id, ticket_id') ->groupBy('ticket_id') ->pluck('id'); // 第二步:筛选出最新状态是working的工单 $tickets = Ticket::whereHas('status_table', function ($query) use ($latestStatusIds) { $query->whereIn('id', $latestStatusIds) ->where('status_column', 'working'); })->get();
方案3:用查询构造器的JOIN写法
如果你更习惯原生查询风格,也可以用JOIN子查询的方式:
$tickets = Ticket::join( // 子查询:按工单分组,取每个工单的最新状态 DB::raw('(SELECT ticket_id, status_column FROM statuses ORDER BY ticket_id, created_at DESC) AS latest_status'), function ($join) { $join->on('tickets.id', '=', 'latest_status.ticket_id'); } ) ->where('latest_status.status_column', 'working') ->groupBy('tickets.id') // 避免重复(可选,取决于你的数据结构) ->select('tickets.*') ->get();
为什么你的原代码无效?
再解释下你原来的代码问题:$query->last()是试图在查询构造器上调用集合方法,这根本行不通——查询构造器是用来构建SQL语句的,而last()是在数据已经被取出成为Collection之后才会生效的方法,所以数据库根本不会理解这个指令。
用上面的方案测试你的示例数据:
- 工单1的最新状态是
created,会被排除 - 工单2的最新状态是
working,会被保留 - 工单3的最新状态是
finished,会被排除
最终结果正好是你要的工单ID#2。
内容的提问来源于stack exchange,提问作者wakawakapogsSr
相关产品推荐
相关产品推荐

