如何在Laravel中将多态系统应用于反向关联的多类型文章翻译场景
结论
不需要新增额外中间表,你当前的数据库结构可以直接被Laravel支持,之前的代码报错是因为多态关联的方向写反了。
适配方案
我们可以通过「双关联预加载 + 动态属性」的方式实现你要的调用效果,完全不需要修改表结构,哪怕TextPostTranslation用的是parent_id作为外键也可以正常适配:
1. 改写Post模型
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class Post extends Model { // 视频类型翻译关联 public function videoTranslations(): HasMany { return $this->hasMany(VideoPostTranslation::class, 'post_id'); } // 文字类型翻译关联,适配非标准外键parent_id public function textTranslations(): HasMany { return $this->hasMany(TextPostTranslation::class, 'parent_id'); } // 动态返回对应类型的翻译集合 public function getTranslationsAttribute() { return match ($this->type) { 'video' => $this->videoTranslations, 'text' => $this->textTranslations, default => collect(), }; } // 实现translations关联的预加载支持 public function scopeWithTranslations($query) { return $query->with(['videoTranslations', 'textTranslations']); } }
2. 翻译模型反向关联写法
class VideoPostTranslation extends Model { public function post() { return $this->belongsTo(Post::class, 'post_id'); } } class TextPostTranslation extends Model { public function post() { return $this->belongsTo(Post::class, 'parent_id'); } }
3. 调用方式和你预期完全一致
// 预加载所有翻译,仅会执行3次查询:查Post、查所有VideoPostTranslation、查所有TextPostTranslation,开销极低 $posts = Post::withTranslations()->get(); foreach($posts as $post) { switch($post->type) { case 'video': // 直接获取video_specific_data字段 foreach($post->translations as $translation) { echo $translation->video_specific_data; } break; case 'text': // 直接获取text_specific_data字段 foreach($post->translations as $translation) { echo $translation->text_specific_data; } break; } }
进阶优化(可选)
如果你希望完全使用Post::with('translations')的写法,不需要调用自定义scope,可以把两个翻译关联加入模型的$with属性实现自动预加载,或者通过Laravel的关联宏自定义translations关联的预加载逻辑即可。
内容的提问来源于stack exchange,提问作者Sebastian
相关产品推荐
相关产品推荐

