2013-10-12 4 views
4

Я хочу реализовать систему друзей пользователя в своем приложении, поэтому я нашел решение для решения проблем с рельсами очень красивым, идея состоит в том, чтобы создать две строки в 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 

, который не является лучшим способом на всех, если вам есть огромная таблица друзей ...

Что я хочу знать, это лучший метод из двух выше, то есть способ его оптимизировать? если есть еще один супер метод, я буду благодарен

ответ

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 

Это только первая идея, наверняка некоторые валидаторы и обратные вызовы отсутствуют ...

+0

Привет @spickermann спасибо за ответ, что вы думаете о размере таблицы, которая будет X2? – medBo

+0

Двойной размер стола не будет проблемой в течение длительного времени. У меня есть таблица дружбы в производственной базе данных, которая содержит более 4 миллионов записей и даже не принимает 200 МБ на диске. – spickermann

+0

спасибо, ваш комментарий успокаивает меня, еще один вопрос о вашем коде, пожалуйста, что вы подразумеваете под after_save: define_friendship – medBo

Смежные вопросы