用户关注功能中的关联关系
关于多对多的关联关系,其实并不简单,最典型的例子就是类似 Twitter 这样的关注功能。
一个用户可以被很多个用户关注,同时,也关注很多个用户。这很明显就是多对多的关系。
一般的多对多关系都是发生在不同的两个 model 之间,比如说 Group 和 User,建立关联的方法是,通过一个中间表格,比如说 GroupRelationship,这个中间表格记录了 group_id
和 user_id
的一一对应关系,再通过 has_many ... through ...
来建立关联关系。
关注这个动作是发生在用户与用户之间的,在 model 层级来看,关注和被关注,都是发生在同一个 model 内的动作。这要怎样来实现呢?
同样,也需要建立一个中间表格,这个中间表格需要建立用户与用户之间的一一对应关系,也就是关注者和被关注者之间的对应关系。显然就不能定义两个 user_id
作为栏位,可以命名为 follower_id
和 following_id
。
class CreateRelationships < ActiveRecord::Migration[5.0]
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :following_id
t.timestamps
end
add_index :relationships, :follower_id
add_index :relationships, :following_id
add_index :relationships, [:follower_id, :following_id], unique: true
end
end
回想 Group 和 User 之间建立多对多关系的时候,对于 Group 要 has_many
关系表格和 User ,而 User 要 has_many
关系表格和 Group。
class Group < ApplicationRecord
has_many :group_relationship
has_many :users, through: :group_relationship, source: :user_id
end
class User < ApplicationRecord
has_many :group_relationship
has_many :groups, through: :group_relationship, source: :group_id
end
而现在,这些关联关系的定义全部要发生在 User 这一个 Model 里,这就需要重命名属性,还有详细定义关联关系的各个参数。
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "following_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :following
has_many :followers, through: :passive_relationships, source: :follower
.
.
.
end
这里面对于中间表格,分别命名为 active_relationships
和 passive_relationships
,对应的是「作为关注者的关系表格」和「作为被关注者的关系表格」。这两张表格其实都是来源于同一张表格,也就是 class_name: "Relationship"
,但是对应的外键 foreign_key
是不一样的。
对照着 Group 和 User 的关联例子,再去审视 User 和 User 本身的关联关系,会理解得更加清晰。在用户关注功能的实现里,User 这一个 model 其实充当了关联关系中的两个角色。