Gawyn
Gawyn

Reputation: 1156

Naming convention in association between objects of the same class in Rails

I am developing an application with the typical social features: essentially, two users can be friends. This is done through an associative class called "Friendship" that contains the id of the two users. Obviously both keys can't be called "user_id". The normal way would be to give different names to both keys and make it work with :class_name.

The problem is that the relation is completely reciprocal, as the friendship can't be only one-side: it works like Facebook, not like Twitter. I don't want to add information naming the keys "creator" and "acceptor", and using numbers "user_1_id" or "user_2_id" sounds terrible.

The question: is there any convention in Rails for this naming problem?

Upvotes: 0

Views: 474

Answers (2)

Harish Shetty
Harish Shetty

Reputation: 64363

I typically normalize the data in such situations.

class User
  has_many :friendships
  has_many :friends
end

class Friendship
  belongs_to :user
  belongs_to :friend, :class_name => "User"

  after_create :reciprocate_friendship

  def reciprocate_friendship    
    return if friend.friends.exists?(user) # do nothing if reciprocal friendship exists
    friend.friends << user
  end
end

This way you are optimizing the friend queries.

Edit

If you must store the friend relationship in one row then add an additional association to the User model:

has_many :network_friends, :class_name => "User", :finder_sql => Proc.new {
  %Q{
    SELECT * FROM users
    JOIN friendships 
      ON (users.id = friendships.user_id OR users.id = friendships.friend_id)
    WHERE users.id = #{id}
  }
}

Now you can use the new association as follows:

user1.friends  << user2
user3.friends << user1
user1.network_friends # [ user2, user3]

Upvotes: 1

ramblex
ramblex

Reputation: 3057

There's a railscasts tutorial on this subject using user and friend which seems sensible. It also has support for the reciprocal relation.

Upvotes: 1

Related Questions