Laravel Eloquent关联预加载仅首条返回latest关联结果怎么解决?
问题原因
在hasMany关联上直接加take(1)的作用范围是整个关联查询的结果集,而非为每个Site模型各取1条关联数据。预加载时会一次性查询所有符合条件的Test记录后仅保留全局最新的1条,因此只有第一个匹配的Site能拿到关联数据。另外你原代码里关联方法名是latestTests(复数),但预加载时写的是latestTest(单数),注意要保持命名一致。
解决方案
方案1:Laravel 8.42+ 官方推荐写法(性能最优)
Laravel 8.42版本开始提供ofMany系列方法,专门用于获取hasMany关联的单条最新/最旧记录,预加载时会自动优化查询逻辑,不会加载全量Test数据。
修改Site模型的关联定义即可:
class Site extends Model { use HasFactory; public function tests() { return $this->hasMany(Test::class,'requestedUrl','frontpage'); } public function latestTest() { return $this->hasOne(Test::class,'requestedUrl','frontpage') ->latestOfMany(); } }
原有查询代码无需修改,直接使用即可:
$sites = Site:: ->with('latestTest') ->orderBy($sort, $direction) ->paginate($size);
方案2:低版本Laravel 子查询实现
如果使用的Laravel版本低于8.42,可以通过子查询实现相同效果:
public function latestTest() { return $this->hasOne(Test::class, 'requestedUrl', 'frontpage') ->where('id', function ($query) { // 若按created_at排序取最新,可替换为selectRaw('max(created_at)'),同时把where条件的id改为created_at $query->selectRaw('max(id)') ->from('tests') ->whereColumn('tests.requestedUrl', 'sites.frontpage'); }); }
性能优化建议
- 给tests表的
requestedUrl字段添加普通索引,可大幅提升关联查询的执行效率。 - 若需要自定义排序规则,
latestOfMany支持传入排序字段和排序方向参数,也支持链式调用where添加额外筛选条件。
内容的提问来源于stack exchange,提问作者Christoffer
相关产品推荐
相关产品推荐

