laravel-polymorphic-relations

test12026-10-2517 次阅读

痛点:评论表跟着资源爆炸

文章、视频、商品都要评论,如果每种资源各建一张评论表,迁移和逻辑都会重复。多态关联让一张 comments 表靠 commentable_type 和 commentable_id 两个字段,同时挂载到任意多种模型上。

建表:morphs 帮你生成两列

morphs('commentable') 一次生成 commentable_type 和 commentable_id,类型列存的是模型类名。

Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->text('body');
    $table->foreignId('user_id');
    $table->morphs('commentable'); // commentable_type + commentable_id
    $table->timestamps();
});

模型里声明两端关系

评论侧用 morphTo,资源侧用 morphMany,字段名要和迁移里的前缀一致。

class Comment extends Model
{
    protected $fillable = ['body', 'user_id'];

    public function commentable()
    {
        return $this->morphTo();
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

class Post extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

class Video extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

增删查都用同一套 API

无论评论的是文章还是视频,写法完全一致;反向从评论也能还原出被评论的对象。

$post = Post::find(1);
$post->comments()->create(['body' => '好文', 'user_id' => 1]);

$comment = Comment::find(1);
echo get_class($comment->commentable); // App\Models\Post

用 morphMap 别把类名写进数据库

默认 type 列存完整类名,一旦改名或改命名空间历史数据就错位。用 MorphMap 映射成短名,数据库只存短键。

use Illuminate\Database\Eloquent\Relations\MorphMap;

MorphMap::set([
    'post' => Post::class,
    'video' => Video::class,
]);

记得预加载,别踩 N+1

列表里要带评论时,用 with('comments') 一次性取出;跨多种资源时给每种资源分别 with 即可。

多态关联是 Laravel 关系里最省表的设计之一,评论、点赞、附件、标签都该优先考虑它。

T

test1

文章作者

痛点:评论表跟着资源爆炸 文章、视频、商品都要评论,如果每种资源各建一张评论表,迁移和逻辑都会重复。多态关联让一张 c...

分类
技术
发布时间
2026-10-25
字数
约 1619 字
阅读
17 次