1

I need to create a user Friendship (user1_id, user2_id) model which connects users.

I would like to avoid having to create two Friendship records for every user / friend as a friendship goes both ways.

How would you do this so while having a somewhat simple

# User.rb
has_many :friendships
has_many :friends, :through => :friendships, :class_name => "User"

EDIT My solution was to mirror the records:

class Friendship < ActiveRecord::Base
    belongs_to :user1
    belongs_to :user2

    after_create :create_mirror!
    after_destroy :destroy_mirror!

    validate :does_not_exist

    def mirror_record
      Friendship.where(:user1_id => user2.id, :user2_id => user1.id).first
    end

    private

    def does_not_exist
      errors.add(:base, 'already exists') if Friendship.where(:user1_id => user1.id, :user2_id => user2.id) rescue nil
    end

    def create_mirror!
      Friendship.create(:user1 => user2, :user2 => user1)
    end

    def destroy_mirror!
      mirror_record.destroy if mirror_record
    end
end
user545139
  • 935
  • 11
  • 27

2 Answers2

1

Maybe this will code snippet will work or provide you with some inspiration. It is from the amistad gem.

class User
  has_many :friendships
  has_many :invited, :through => :friendships, :source => :friend
  has_many :invited_by, :through => :inverse_friendships, :source => :user

  def friends
    self.invited + self.invited_by
  end

class Friendships
  belongs_to :user
  belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"

So in your controllers, you can write something like user.friends to fetch all friends.

rocketscientist
  • 2,428
  • 3
  • 19
  • 18
  • I now release that what I want to do is nightmarish without mirroring records, which is the best and only solution. – user545139 Oct 21 '11 at 04:35
0

You should only need one record per friendship. The Friendship class simply has two attributes, each pointing to one of the friends.

class Friendship
  belongs_to user1, :class_name => "User"
  belongs_to user2, :class_name => "User"

...and your table is...

friendship_id | user1_id | user2_id
-----------------------------------

In Rails 2.x this was called as "has and belongs to many" relationship (HABTM).

I believe you have to explicitly specify the class names for each of the belongs_to statements, because they both point to the same type of parent record (a User), and as such you can't call both fields user - you have to differentiate them in name somehow.

jefflunt
  • 33,527
  • 7
  • 88
  • 126
  • this makes the `Friendship` model work, but the `User` HABTM relationship still does not work. You cannot call `User.first.friends << User.last` or even `User.first.friends` as `:source` can only have one value, `:game1` or `:game2`. – user545139 Oct 16 '11 at 23:26
  • Correct me if I'm wrong, but isn't the `has_many :friendships` and `has_many :friends, :through => :friendships` redundant? Maybe I'll have to spin up a test project to see what happens. – jefflunt Oct 17 '11 at 02:09