Laravel如何获取三层深度关联的Related对象列表
解决多层关联下获取深层多态关联对象的问题
针对你的模型层级(Question → hasMany Decision → hasMany Impact → morphTo Related),hasManyThrough确实只支持两层关联的穿透,这里提供几种可行的实现方案:
方案一:嵌套预加载+手动合并数据
这是最直观的方案,适合数据量不大的场景:
- 先预加载所有层级的关联数据:
$questions = Question::with('decisions.impact.related')->get();
- 遍历每个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的关联类,实现三层关联的穿透:
- 创建自定义关联类:
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()}" ); } }
- 在
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
相关产品推荐
相关产品推荐

