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

Laravel动态约束下嵌套预加载实现同编号歌曲自关联查询

实现同number歌曲嵌套预加载方案

基础信息

songs表结构:

idversionnumber
1AAA1
2BBB1
3CCC1
4DDD2
5EEE3
6FFF4
7GGG4

需求:为每首歌加载所有number字段值和自身相同的歌曲集合,例如id=1的歌曲number为1,需关联返回id为1、2、3的歌曲。
当前Song模型已有关联定义:

public function songs()
{
    return $this->hasMany(Song::class, 'id_hierarchy');  
}

预期效果:直接通过children.songs.sameNumber实现嵌套预加载,无需重复编写子查询逻辑。


方案可行性结论

在Song模型中定义自关联实现sameNumber关联的方案完全可行,是Laravel框架下的标准实现方式,相比临时闭包子查询更易维护、逻辑更清晰,且不会产生N+1查询问题。


具体实现步骤

1. 在Song模型中定义sameNumber自关联

同表内通过number字段匹配同值记录属于一对多自关联,直接在Song模型中添加关联方法即可:

public function sameNumber()
{
    // 关联逻辑:外键为songs表的number字段,关联本地的number字段
    return $this->hasMany(Song::class, 'number', 'number');
}

如果不需要把当前歌曲本身包含在关联结果中,可以加条件排除自身id:

public function sameNumber()
{
    return $this->hasMany(Song::class, 'number', 'number')
        ->where('id', '!=', $this->id);
}

2. 直接调用嵌套预加载

关联定义完成后,业务查询代码可以直接使用标准的嵌套预加载写法,无需额外编写闭包子查询:

public function readSongsVersions()
{
    $data = Hierarchy::query()
        ->whereNull('parent_id')
        ->with([
            'children.songs',
            'children.songs.sameNumber'
        ])
        ->get();

    return $data;
}

原有闭包写法的问题

你之前尝试的闭包约束写法无法实现预期效果,存在两个核心问题:

  • 重复定义了两次children.songs预加载规则,后者会覆盖前者的加载逻辑
  • 子查询存在语法错误(DB::raw('i.number)')多了闭合括号),且该逻辑仅对songs主查询加过滤条件,无法生成嵌套的关联歌曲集合层级,最终返回结构不符合预期。

性能说明

该自关联预加载由Laravel框架底层自动处理,全程不会产生N+1查询,总共仅执行4类查询:

  1. 查询parent_id为null的Hierarchy数据
  2. 查询Hierarchy对应的children数据
  3. 查询children关联的songs数据
  4. 通过where in匹配所有songs对应number值的同组歌曲,即sameNumber关联数据
    性能和手写原生子查询一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 02:01:33