4

我想在我的应用程序中实现用户的朋友系统,所以我发现 rails space 解决方案非常好,这个想法是在其中创建两行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”行

第二种解决方案是与RailsCast #163 Self-Referential Associationhas_many through中解释的反向关联:

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

如果你有一张巨大的友谊桌,这根本不是最好的方法......

我想知道的是以上两种方法中最好的一种方法是什么,那么有一种方法可以优化它吗?如果还有另一种超级方法,我将不胜感激

4

1 回答 1

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

这只是第一个想法,肯定缺少一些验证器和回调......

于 2013-10-12T22:46:12.703 回答