多态关联

多态关联让一个模型可以关联多种不同的模型。评论可以属于文章、视频、图片;图片可以属于用户、商品、店铺。这种灵活性在复杂业务中很有用。

什么是多态关联

传统关联需要预先知道关联的模型类型。评论表有 article_id 字段,只能关联文章。

多态关联用两个字段解决:

  • commentable_id - 关联对象的 ID
  • commentable_type - 关联对象的类型

这样评论可以关联文章、视频、图片等任何模型。

多态一对多

评论可以属于文章或视频:

type Comment struct {
    ID             uint
    Content        string
    CommentableID   uint
    CommentableType string
}

type Article struct {
    ID       uint
    Title    string
    Comments []Comment `gorm:"polymorphic:Commentable;"`
}

type Video struct {
    ID       uint
    Title    string
    Comments []Comment `gorm:"polymorphic:Commentable;"`
}

创建评论:

article := Article{Title: "文章标题"}
db.Create(&article)

comment := Comment{
    Content:         "这是评论",
    CommentableID:   article.ID,
    CommentableType: "articles",
}
db.Create(&comment)

或者通过关联创建:

article := Article{
    Title: "文章标题",
    Comments: []Comment{
        {Content: "评论1"},
        {Content: "评论2"},
    },
}
db.Create(&article)

CommentableType 自动设为表名 articles

查询多态关联

查询文章的评论:

var article Article
db.Preload("Comments").First(&article, 1)
fmt.Println(article.Comments)

查询视频的评论:

var video Video
db.Preload("Comments").First(&video, 1)
fmt.Println(video.Comments)

多态一对一

图片可以属于用户或商品:

type Image struct {
    ID          uint
    URL         string
    ImageableID   uint
    ImageableType string
}

type User struct {
    ID      uint
    Name    string
    Avatar  Image `gorm:"polymorphic:Imageable;"`
}

type Product struct {
    ID    uint
    Name  string
    Image Image `gorm:"polymorphic:Imageable;"`
}

创建用户头像:

user := User{
    Name: "张三",
    Avatar: Image{
        URL: "avatar.jpg",
    },
}
db.Create(&user)

多态多对多

标签可以用于文章、视频、图片:

type Tag struct {
    ID       uint
    Name     string
    Articles []Article `gorm:"many2many:taggables;polymorphic:Taggable;"`
    Videos   []Video   `gorm:"many2many:taggables;polymorphic:Taggable;"`
}

type Article struct {
    ID   uint
    Title string
    Tags []Tag `gorm:"many2many:taggables;polymorphic:Taggable;"`
}

type Video struct {
    ID    uint
    Title string
    Tags  []Tag `gorm:"many2many:taggables;polymorphic:Taggable;"`
}

中间表 taggables 结构:

CREATE TABLE taggables (
    tag_id INT,
    taggable_id INT,
    taggable_type VARCHAR(255),
    PRIMARY KEY (tag_id, taggable_id, taggable_type)
)

自定义类型值

默认 CommentableType 存储表名。自定义:

type Comment struct {
    ID             uint
    Content        string
    CommentableID   uint
    CommentableType string
}

type Article struct {
    ID       uint
    Title    string
    Comments []Comment `gorm:"polymorphic:Commentable;polymorphicValue:article"`
}

type Video struct {
    ID       uint
    Title    string
    Comments []Comment `gorm:"polymorphic:Commentable;polymorphicValue:video"`
}

CommentableType 存储自定义值 articlevideo

查询关联对象

从评论反向查询关联对象:

var comment Comment
db.First(&comment, 1)

switch comment.CommentableType {
case "articles":
    var article Article
    db.First(&article, comment.CommentableID)
case "videos":
    var video Video
    db.First(&video, comment.CommentableID)
}

封装成方法:

func (c *Comment) GetCommentable(db *gorm.DB) interface{} {
    switch c.CommentableType {
    case "articles":
        var article Article
        db.First(&article, c.CommentableID)
        return &article
    case "videos":
        var video Video
        db.First(&video, c.CommentableID)
        return &video
    }
    return nil
}

实际案例

评论系统

评论可以属于文章、视频、动态:

type Comment struct {
    gorm.Model
    Content        string
    CommentableID   uint
    CommentableType string
    UserID         uint
    User           User
}

type Post struct {
    gorm.Model
    Content  string
    Comments []Comment `gorm:"polymorphic:Commentable;"`
}

type Article struct {
    gorm.Model
    Title    string
    Comments []Comment `gorm:"polymorphic:Commentable;"`
}

收藏系统

用户可以收藏文章、商品、店铺:

type Favorite struct {
    gorm.Model
    UserID         uint
    FavoritableID   uint
    FavoritableType string
}

type User struct {
    gorm.Model
    Favorites []Favorite
}

type Article struct {
    gorm.Model
    Title     string
    Favorites []Favorite `gorm:"polymorphic:Favoritable;"`
}

type Product struct {
    gorm.Model
    Name      string
    Favorites []Favorite `gorm:"polymorphic:Favoritable;"`
}

点赞系统

type Like struct {
    gorm.Model
    UserID      uint
    LikeableID   uint
    LikeableType string
}

type Article struct {
    gorm.Model
    Title string
    Likes []Like `gorm:"polymorphic:Likeable;"`
}

type Comment struct {
    gorm.Model
    Content string
    Likes   []Like `gorm:"polymorphic:Likeable;"`
}

注意事项

索引

多态关联需要复合索引:

type Comment struct {
    ID             uint
    CommentableID   uint
    CommentableType string `gorm:"index:idx_commentable"`
}

或数据库层面:

CREATE INDEX idx_commentable ON comments(commentable_type, commentable_id);

外键约束

多态关联无法使用数据库外键约束,因为 CommentableID 可能指向不同表。需要在应用层保证数据一致性。

查询性能

按类型查询时,确保有索引:

db.Where("commentable_type = ? AND commentable_id = ?", "articles", 1).Find(&comments)

小结

多态关联提供了灵活的关联方式,适合评论、收藏、点赞等通用功能。注意索引优化和应用层约束。