我想在我的应用中实现用户的朋友系统,所以我发现rails空间解决方案非常好,我想在the Friendships table
中创建两行:第一行发件人邀请,第二个是接收者
用户之间的关系设置为has_many
关联,如下所示:
has_many :friendships
has_many :friends, :through => :friendships, :conditions => "status = 'accepted'"
接受用户为朋友的方法是这样的:
# Accept a friend request.
def self.accept(user, friend)
transaction do
accepted_at = Time.now
accept_one_side(user, friend, accepted_at)
accept_one_side(friend, user, accepted_at)
end
end
accept_one_side()方法是:
# Update the db with one side of an accepted friendship request.
def self.accept_one_side(user, friend, accepted_at)
request = find_by_user_id_and_friend_id(user, friend)
request.status = 'accepted'
request.accepted_at = accepted_at
request.save!
end
这样做的好处是我们可以执行一个请求来获取双方的所有朋友(用户是发送邀请的人或朋友是谁发送的)
但我认为这有不利之处,例如在现实中有500个朋友,友谊表将包含“500X2 = 1000”行
第二个解决方案是与has_many through
进行反向关联,如RailsCast #163 Self-Referential Association中所述:
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
但是如果你想为一个用户获取所有朋友,你应该使用两个请求:
user.friends
user.inverse_friends
如果你有一个庞大的友谊表,那么这不是最好的方式...
我想知道的是上述两种方法中哪种方法最好,那么有一种优化方法吗?如果还有另一种超级方法,我将感激不尽
答案 0 :(得分:4)
我更喜欢在朋友之间需要两个连接的版本,每个方向一个。原因与您提到的相同:它允许对用户的朋友进行更多类似Rails的查询。
此外,我认为为友谊请求(一个方向)和现有友谊(两个方向)设置不同的表会更清楚
由于你在中间有一个友谊模型,我建议使用回调的魔力。如果你定义了一些回调,那么你必须只需要为连接的一端带蛋糕,回调应该能够创建(或删除)匹配的补充。
# in friendship_requests
after_save :created_friendship
def accept
update_attributes(:status => 'accepted')
end
private
def created_friendship
sender.friends << receiver if status_changed? && status == 'accepted'
end
# in user.rb
has_and_belongs_to_many :friends, after_add: :create_complement_friendship,
after_remove: :remove_complement_friendship
private
def create_complement_friendship(friend)
friend.friends << self unless friend.friends.include?(self)
end
def remove_complement_friendship(friend)
friend.friends.delete(self)
end
这只是第一个想法,确实缺少一些验证器和回调......