Laravel 11中如何限制hasMany关联仅获取最后1条数据?
Laravel 11 关联模型获取单条最新记录及时间过滤实现方案
问题背景
在Laravel 11 / MySQL 8环境下,Currency模型定义了如下关联方法用于关联CurrencyHistory表:
public function latestCurrencyHistory(): HasMany { return $this->hasMany(CurrencyHistory::class)->latest(); }
执行预加载查询时:
$this->currencies = Currency ::getByActive(CurrencyActiveEnum::ACTIVE) ->with('latestCurrencyHistory') ->orderBy('ordering', 'asc') ->get();
生成的SQL会通过row_number()窗口函数按currency_id分组,取每个分组的最新1条记录。但给关联方法添加->limit(1)后,生成的SQL无变化,limit未生效。需要实现两个需求:
- 确保每个
Currency仅关联CurrencyHistory的最后1条数据 - 添加
CurrencyHistory.created_at ≥ 最近24小时的过滤条件
解决方法
1. 使用hasOne关联 + latestOfMany(推荐)
Laravel提供latestOfMany方法专门用于获取关联模型的最新单条记录,替代hasMany更符合“单条记录”的业务场景,同时支持添加过滤条件:
use Illuminate\Database\Eloquent\Relations\HasOne; public function latestCurrencyHistory(): HasOne { return $this->hasOne(CurrencyHistory::class) // 添加最近24小时的过滤条件 ->where('created_at', '>=', now()->subDay()) // 指定取最新的1条记录 ->latestOfMany(); }
执行原预加载查询后,Laravel会生成正确的SQL,确保每个Currency仅关联符合时间条件的最新CurrencyHistory记录。
2. 保持hasMany关联,通过子查询预加载(灵活场景)
如果需要保留hasMany关联的定义,可在查询时使用withSub自定义子查询,实现单条记录获取和时间过滤:
$this->currencies = Currency::getByActive(CurrencyActiveEnum::ACTIVE) ->withSub([ 'latestCurrencyHistory' => function ($query) { $query->latest() ->where('created_at', '>=', now()->subDay()) ->limit(1); } ], 'latest_currency_history') ->orderBy('ordering', 'asc') ->get();
查询结果中,每个Currency实例的latest_currency_history属性会包含符合条件的最新1条CurrencyHistory记录。
原limit(1)不生效的原因
当使用hasMany关联进行预加载时,Laravel会自动检测到“需要为每个父模型获取子记录”的需求,此时直接添加的limit(1)会被框架忽略,转而使用row_number()窗口函数实现按分组取1条记录的逻辑,因此生成的SQL无变化。
内容的提问来源于stack exchange,提问作者mstdmstd
相关产品推荐
相关产品推荐

