I'm currently working on a small social networking application and right now I'm trying to create a model that represents friendships between users. This is what I came up with so far:
class User < ActiveRecord::Base
# ...
has_many :friendships
has_many :friends, :through => :friendships
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
end
My friendship model has a field confirmed as boolean which I'd like to use to define a friendship as pending or confirmed.
How can I access all pending request for a certain user? Can I somehow define this using Rails' scope method? Something like
current_user.friendships.requests # => [Friendship, Friendship, ...]
would be great.
How can I make this association bidirectional? Do I simply add another friendship once one has confirmed a friend request such that my friendship table would look similar to开发者_JAVA百科 this:
| user_id | friend_id | confirmed |
-----------------------------------
| 1 | 2 | true |
| 2 | 1 | true |
To access all pending friendships you can use an association:
has_many :pending_friends,
:through => :friendships,
:source => :friend,
:conditions => "confirmed = 0" # assuming 0 means 'pending'
To make the friendship bidirectional, you may want to replace your boolean confirmed column with a string status column that has one of the following three values: 'pending', 'requested' and 'accepted' (optionally 'rejected'). This will help keep track of who made the friendship request.
When a friendship request is sent (say from Foo to Bar), you create two friendship records (encapsulated in a transaction): one requested and one pending to reflect resp. that Bar has a requested friendship from Foo and Foo has a pending friendship with Bar.
def self.request(user, friend)
unless user == friend or Friendship.exists?(user, friend)
transaction do
create(:user => user, :friend => friend, :status => 'pending')
create(:user => friend, :friend => user, :status => 'requested')
end
end
end
When the friendship is accepted (e.g. by Bar), both friendship records are set to accepted.
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
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
This is largely covered in chapter 14 of the Railspace book by Michael Hartl and Aurelius Prochazka. Here's the source code which should help you refine your solution.
The ultimate reference:
http://railscasts.com/episodes/163-self-referential-association
A short answer is yes. Just make another friendship record to represent bidirectional association.
I wrote a gem called has_friendship for this kind of problem. All you need to do is drop in has_friendship
in your model, and all the associations and methods will be taken care of.
Hope this helps!
Read up on this tutorial illustrating a relationship model on Rails Tutorials. It should be exactly what you are looking for.
I recommend you the book RailsSpace: Building a Social Networking Website. It clearly explains how to implement a friendship model. Though it isnt extensible to other forms of relationship but is pretty applicable for your problem.
精彩评论