如何在Laravel Eloquent中获取关联表不同Key的首条记录?
解决方案:Laravel关联查询同时获取多key的最早记录
你遇到的问题是因为两次调用with('history')时,后一次的查询条件会覆盖前一次,最终只返回最后一个key的结果。下面提供几种可行的解决方法:
方法一:定义独立关联(最直观)
在Task模型中为每个需要的key单独定义hasOne关联:
// Task.php public function historyKey1() { // 取key=1的最早记录 return $this->hasOne(History::class)->where('key', 1)->orderBy('date', 'asc'); } public function historyKey2() { // 取key=2的最早记录 return $this->hasOne(History::class)->where('key', 2)->orderBy('date', 'asc'); }
查询时同时加载这两个关联:
$task = Task::where('id', 1) ->with(['historyKey1', 'historyKey2']) ->first();
如果需要将结果合并到history数组中,可以在模型里添加访问器:
// Task.php public function getHistoryAttribute() { $histories = []; if ($this->historyKey1) $histories[] = $this->historyKey1; if ($this->historyKey2) $histories[] = $this->historyKey2; return $histories; }
方法二:数据库层面直接筛选(效率最优)
通过子查询获取每个key对应的最早日期,再关联查询目标记录:
$task = Task::where('id', 1) ->with(['history' => function ($query) { $query->whereIn('key', [1, 2]) ->whereRaw('(task_id, `key`, date) IN ( SELECT task_id, `key`, MIN(date) FROM histories WHERE task_id = ? AND `key` IN (1,2) GROUP BY task_id, `key` )', [1]); }]) ->first();
注意:histories是History表的默认复数表名,若你的表名不同请替换;?绑定task_id用于避免SQL注入。
方法三:集合层面处理(灵活)
先查询所有符合key的记录,再通过集合分组提取最早记录:
$task = Task::where('id', 1) ->with(['history' => function ($q) { $q->whereIn('key', [1, 2])->orderBy('date', 'asc'); }]) ->first() ->transform(function ($task) { // 按key分组,取每组第一条(已按日期升序,第一条即为最早) $task->history = $task->history->groupBy('key') ->map(fn($group) => $group->first()) ->values(); return $task; });
原问题原因说明
Laravel中多次调用with()加载同一个关联时,后续的查询闭包会完全覆盖之前的设置,不会合并条件。所以你第二次调用with('history')会替换第一次的关联逻辑,最终只返回key=2的结果。
内容的提问来源于stack exchange,提问作者KoRi
相关产品推荐
相关产品推荐

