概念

多态只存在于 Has One 和 Has Mang 中,这东西有些难以表述,我尽力讲得比较清晰

假如你正在开发一个类 B 站平台,平台上可以上传视频,也可以发布文章

现在需要加入评论功能,你将如何设计评论表?

众所周知,视频是可以评论的,文章也是可以评论的 (暂时不考虑评论的评论)

最简单的方式就是以视频表的主键为外键,建一个视频评论表,再另外以文章表的主键为外键,建一个文章评论表

也就是说,视频 has many 视频评论,文章 has many 文章评论,两种评论互不相干

但是,这就搞出来两张表,搞出来两种评论类型,有没有一种关联模式,能在一张表中存储所有评论呢?

或者在某些场景下,不止 A 和 B 要拥有某一类的东西,而有 ABCDEF… 那怎么办?

那自然是有的,在这里只需这样设计评论表

  • 字段1:自增主键
  • 字段2:评论内容
  • 字段3:拥有者的 ID (这个拥有者可以是指视频,也可以是指文章)
  • 字段4:拥有者类型(视频/文章)(A/B/C/D/E/F…)

这就是多态,感觉就像是可以同时以多个表作为外键,但每一条数据都只被一个拥有者拥有

(多说一句,多态仅仅是 GORM 完成的,数据库中并没有外键索引约束)


使用方法

还是接上面的视频与文章的例子,假设它们的原始模型如下

1
2
3
4
5
6
7
8
9
10
11
type Video struct {
Id int
Title string
URL string
}

type Post struct {
Id int
Title string
Body string
}

假设评论的原始模型如下

1
2
3
4
type Comment struct {
Id int
Body string
}

现在要使用多态形式关联到视频和文章上,只需这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Video struct {
Id int
Title string
URL string

Comments []Comment `gorm:"polymorphic:Owner;"` // 多态关联(多态一对多)
}

type Post struct {
Id int
Title string
Body string

Comments []Comment `gorm:"polymorphic:Owner;"` // 多态关联(多态一对多)
}

type Comment struct {
Id int
Body string

OwnerID int // 拥有者ID
OwnerType string // 拥有者类型
}

当你把表创建出来,你就能实现上面说的结构

1
GLOBAL_DB.AutoMigrate(&Video{}, &Post{}, &Comment{})

image-20221008203542623

浅浅地试用一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GLOBAL_DB.Create(&Video{
Title: "video1",
URL: "https://video1.com",
Comments: []Comment{
{Body: "video1 comment1"},
{Body: "video1 comment2"},
},
})
GLOBAL_DB.Create(&Post{
Title: "post1",
Body: "body1",
Comments: []Comment{
{Body: "post1 comment1"},
{Body: "post1 comment2"},
},
})

image-20221008204337380

应该很容易理解多态的关系了

上面讲的是 has many 的情况(视频/文章 has many 评论)

而 has one 其实是差不多的,例如简介(视频/文章 has one 简介)

你只需这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Video struct {
Id int
Title string
URL string

Introductions Introduction `gorm:"polymorphic:Owner;"` // 多态关联(多态一对一)
}

type Post struct {
Id int
Title string
Body string

Introductions Introduction `gorm:"polymorphic:Owner;"` // 多态关联(多态一对一)
}

type Introduction struct {
Id int
Body string

OwnerID int // 拥有者ID
OwnerType string // 拥有者类型
}

然后都是差不多的使用


polymorphicValue 标签

这个东西可以改变 owner_type 中的对应名称,试一下就很清楚了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Video struct {
Id int
Title string
URL string

Comments []Comment `gorm:"polymorphic:Owner;polymorphicValue:video_tag"`
}

type Post struct {
Id int
Title string
Body string

Comments []Comment `gorm:"polymorphic:Owner;polymorphicValue:post_tag"`
}

type Comment struct {
Id int
Body string

OwnerID int // 拥有者ID
OwnerType string // 拥有者类型
}

image-20221008205432573