Polymorphic Association(多态关联)这个名字听上去很高大上,好像是一个很厉害又很复杂的功能,其实不然。
多态关联这个概念非常简单,举一个简单的小例子就明白了:
class Comment < ApplicationRecord
end
class Article < ApplicationRecord
end
class Photo < ApplicationRecord
end
这里我们有三个类,Article 和 Photo 都可以被留言,也就是他们分别和 Comment 是一对多的关系。在没有用多态的情况下,我们原来的做法是 Artile 和 ArticleComment(外键: article_id) 之间建立 has_many / belongs_to 的关系,Photo 再和 PhotoComment(外键: photo_id) 之间建立 has_many / belongs_to 的关系,但是两个 comment 其实结构完全相同,同样是一个评论的模型,只不过和别的多个模型之间产生关联关系。
当一个模型需要同时属于其他多个模型的时候,使用多态关联(polymorphic)。
那么如何做到呢?我们来看 Rails 的具体实现:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
class Photo < ApplicationRecord
has_many :comments, as: :commentable
end
通过建立一个 commentable 的虚拟接口,并且指明 polymorphic: true,来让其他多个模型建立关系从而实现多态关联。但是,comment 如何知道自己关联的对象是 article 还是 photo 呢?按原来的模式,外键应该是 article_id 或者是 photo_id,多态是如何做的呢?
秘诀就是新增一个字段: commentable_type。来看 migration:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :content
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
end
end
我们用 content 来储存留言的内容,commentable_id 储存被留言的对象 id ,commentable_type 则用来储存被留言对象的种类,以这个例子来说被留言的对象就是 Article 与 Photo 这两种 Model。
看一下 console 中的实际结果:
article = Article.first
comment_a = article.comments.create(content: "First Article Comment")
# 你可以发现 Rails 很聪明的帮我们指定了被留言对象的 type 和 id
comment_a.id # => 1
comment_a.commentable_type # => "Article"
comment_a.commentable_id # => 1
photo = Photo.first
comment_p = photo.comments.create(content: "First Photo Comment")
# 因为共用一张 comments 表,所有 comment_p 的 id 索引增加了
comment_p.id # => 2
comment_p.commentable_type # => "Photo"
comment_p.commentable_id # => 1
# 也可以通过 commentable 反向回查关联的对象
comment_a.commentable => #<Article id: 1, ....>
comment_p.commentable => #<Photo id: 1, ....>
本文主要介绍 Rails 中多态关联的概念,关于路由和表单中如何使用多态,推荐看 railscasts 的相关介绍,链接在参考资料