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

Laravel预加载中使用orderBy()->first()关联数据丢失问题咨询

问题原因

你在关联预加载闭包中调用first()出现数据丢失,本质是Laravel预加载机制的特性导致的:
Laravel的预加载为了避免N+1查询,会先拿到所有父模型(这里是所有符合条件的ServiceProvider)的主键列表,再一次性查询所有关联的sp_services记录,最后按外键匹配到对应父模型。你在闭包中加first()相当于给这个全局的关联查询加了LIMIT 1,最终整个关联查询只会返回1条sp_services记录,只有一个ServiceProvider能匹配到数据,其余服务商的关联自然为空。

最优实现方案

方案1:新增一对一关联取单条记录(推荐,Laravel 8+支持)

直接在ServiceProvider模型中新增一个hasOne类型的关联,专门用于取每个服务商价格最低的服务:

class ServiceProvider extends Model
{
    // 原有关联保留
    public function sp_services()
    {
        return $this->hasMany(SpService::class, 'sp_id', 'id');
    }

    // 新增取最低价服务的关联
    public function cheapest_service()
    {
        // 要取最高价就把oldest改成latest
        return $this->hasOne(SpService::class, 'sp_id', 'id')->oldest('price');
    }
}

修改查询代码直接预加载这个新关联即可:

$branchesQuery = SpBranch::query();
                
$branchesQuery->where('status', true);
                
$branchesQuery->when($request->filled('price'), function($query) {
    return $query->with(['service_provider' => function($query) {
        $query->whereHas('sp_services')
            ->with('cheapest_service');
    }]);
});
                
$branches = $branchesQuery->paginate($perPageLimit);

return $branches;

这个方案性能最优,完全符合Laravel的关联设计规范,不会产生多余查询。

方案2:集合层面过滤(适合数据量小的场景)

如果不想修改模型,也可以在预加载时仅保留排序逻辑,拿到查询结果后用集合方法取每条关联的第一条:

// 预加载时只做排序
$branchesQuery->when($request->filled('price'), function($query) {
    return $query->with(['service_provider' => function($query) {
        $query->whereHas('sp_services')
            ->with(['sp_services' => fn($q) => $q->oldest('price')]);
    }]);
});

$branches = $branchesQuery->paginate($perPageLimit);

// 处理分页结果,只保留每个服务商的第一条服务
$branches->through(function($branch) {
    if (isset($branch->service_provider)) {
        $branch->service_provider->setRelation(
            'sp_services', 
            collect([$branch->service_provider->sp_services->first()])
        );
    }
    return $branch;
});

return $branches;

补充说明

你当前代码里写的orderBy('price', 'DESC')是按价格倒序,取的是最高价的服务,如果你要取最低价,需要把排序方向改成ASC。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:09:02