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

Laravel如何获取三层深度关联的Related对象列表

解决多层关联下获取深层多态关联对象的问题

针对你的模型层级(Question → hasMany Decision → hasMany Impact → morphTo Related),hasManyThrough确实只支持两层关联的穿透,这里提供几种可行的实现方案:

方案一:嵌套预加载+手动合并数据

这是最直观的方案,适合数据量不大的场景:

  1. 先预加载所有层级的关联数据:
$questions = Question::with('decisions.impact.related')->get();
  1. 遍历每个Question,逐层收集所有Related对象:
$result = $questions->map(function ($question) {
    $relateds = collect();
    // 遍历决策
    foreach ($question->decisions as $decision) {
        // 遍历每个决策对应的影响
        foreach ($decision->impact as $impact) {
            // 确保关联对象存在,避免空值
            if ($related = $impact->related) {
                $relateds->push($related);
            }
        }
    }
    return [
        'question' => $question->id,
        'relateds' => $relateds->toArray()
    ];
});

执行后就能得到你需要的question与relateds对应格式的数组。

方案二:直接通过SQL JOIN查询

如果数据量较大,手动合并效率偏低,可以通过数据库层面的JOIN直接聚合结果(注意多态关联需要处理related_type和related_id):

$questions = Question::select('questions.id as question')
    // 根据你的Related模型实际字段调整JSON_OBJECT内的内容
    ->selectRaw("JSON_ARRAYAGG(JSON_OBJECT('id', r.id, 'type', i.related_type)) as relateds")
    ->join('decisions', 'decisions.question_id', '=', 'questions.id')
    ->join('impacts', 'impacts.decision_id', '=', 'decisions.id')
    // 多态关联的JOIN需要匹配id和类型
    ->join('related_models as r', function ($join) {
        $join->on('r.id', '=', 'impacts.related_id')
             ->whereColumn('r.type', 'impacts.related_type');
    })
    ->groupBy('questions.id')
    ->get();

注:related_models是泛指多态关联对应的表,如果你的多态关联对应多个不同表,可能需要用UNION合并不同表的数据后再关联。

方案三:自定义三层穿透关联(适合Laravel)

如果需要在多个地方复用这个关联逻辑,可以自定义一个扩展HasManyThrough的关联类,实现三层关联的穿透:

  1. 创建自定义关联类:
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class HasManyThroughMany extends HasManyThrough
{
    protected $secondThroughModel;
    protected $thirdKey;

    public function __construct(
        Builder $query,
        Model $parent,
        Model $through,
        Model $secondThrough,
        string $firstKey,
        string $secondKey,
        string $thirdKey,
        string $localKey
    ) {
        parent::__construct($query, $parent, $through, $firstKey, $secondKey, $localKey);
        $this->secondThroughModel = $secondThrough;
        $this->thirdKey = $thirdKey;
    }

    // 重写关联查询逻辑,添加第二层中间表的关联
    protected function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
    {
        $query = parent::getRelationExistenceQuery($query, $parentQuery, $columns);
        $secondThroughTable = $this->secondThroughModel->getTable();
        return $query->join(
            $secondThroughTable,
            "{$this->through->getTable()}.{$this->secondKey}",
            '=',
            "{$secondThroughTable}.{$this->localKey}"
        )->whereColumn(
            "{$secondThroughTable}.{$this->thirdKey}",
            '=',
            "{$this->related->getTable()}.{$this->related->getKeyName()}"
        );
    }
}
  1. 在Question模型中定义这个关联:
public function relateds()
{
    return $this->hasManyThroughMany(
        Related::class,
        Decision::class,
        Impact::class,
        'question_id', // Question -> Decision 的外键
        'decision_id', // Decision -> Impact 的外键
        'related_id', // Impact -> Related 的外键
        'id' // Question的主键
    );
}

之后就可以像普通关联一样使用:$questions = Question::with('relateds')->get();,再自行整理成需要的格式即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:20:40