Laravel/MySQL:筛选关联模型最高版本且非草稿状态的最新记录
解决方案
1. 定义正确的hasOne关联
在Contract模型中修改latestVersion关联,通过子查询锁定每个合约对应的最高版本记录,同时排除status为draft的情况:
public function latestVersion() { return $this->hasOne(ContractVersion::class, 'contract_id') // 子查询获取当前合约的最高版本号 ->whereRaw('version = (SELECT MAX(version) FROM contract_versions WHERE contract_id = ?)', [$this->id]) // 排除草稿状态的记录 ->where('status', '!=', 'draft'); }
2. 执行查询并过滤无效父记录
使用has('latestVersion')确保只返回存在符合条件关联记录的父合约,再结合with('latestVersion')加载关联数据并分页:
$contracts = Contract::with('latestVersion') ->has('latestVersion') // 过滤掉没有有效最新版本的合约 ->paginate();
为什么之前的方法无效?
latest()仅对结果集排序,无法保证只取每个合约的最高版本记录;groupBy('version')是按版本号分组,而非按合约ID分组,不符合需求逻辑。
替代写法(子查询关联)
如果需要更清晰的关联逻辑,也可以在查询时直接指定关联条件:
$contracts = Contract::with(['latestVersion' => function ($query) { $query->whereRaw('version = (SELECT MAX(version) FROM contract_versions WHERE contract_id = contracts.id)') ->where('status', '!=', 'draft'); }]) ->has('latestVersion') ->paginate();
基于Join的实现方式
如果需要更高性能,可以通过子查询预取每个合约的最高版本,再通过Join关联筛选:
// 先获取每个合约的最高版本号 $maxVersions = ContractVersion::select('contract_id', DB::raw('MAX(version) as max_version')) ->groupBy('contract_id'); // 关联查询并筛选有效记录 $contracts = Contract::joinSub($maxVersions, 'max_versions', function ($join) { $join->on('contracts.id', '=', 'max_versions.contract_id'); })->join('contract_versions', function ($join) { $join->on('contracts.id', '=', 'contract_versions.contract_id') ->on('contract_versions.version', '=', 'max_versions.max_version'); })->where('contract_versions.status', '!=', 'draft') ->select('contracts.*') // 只选择合约表字段 ->with('latestVersion') // 按需加载关联模型 ->paginate();
内容的提问来源于stack exchange,提问作者Eugen Stranz
相关产品推荐
相关产品推荐

