17

could some one point me to the right direction:

I try to build a model for rails that build up the following:

ClassA -id

ClassA has a relation to many "ClassA" (so it is a reference to itself)

I am searching for the migration and the model.

I a not sure what the correct join table is (I think its a simple 2 columns table ClassA_id, ClassARel_ID -> both point to ClassA) and how to build the model

Thanks!

awex
  • 353
  • 1
  • 4
  • 12
  • 1
    Can you give us a concrete example? Is this like a Human who has a parent (who is obviously a human as well)? Or do you want to say like "Cars.all.green" to return all green cars? – Jesse Wolgamott Aug 03 '10 at 13:22
  • 1
    Yes, it is like Person has many Friends (Persons) – awex Aug 03 '10 at 13:27
  • This post has a good working example: http://blog.hasmanythrough.com/2007/10/30/self-referential-has-many-through There is a related question here as well: https://stackoverflow.com/questions/364934/problem-with-self-referential-has-many-throug – Amala Nov 22 '10 at 03:58

3 Answers3

35

I'd use something like

class Person < ActiveRecord::Base
   has_many :friendships, :foreign_key => "person_id", 
      :class_name => "Friendship"

   has_many :friends, :through => :friendships
end

class Friendship < ActiveRecord::Base
   belongs_to :person, :foreign_key => "person_id", :class_name => "Person"
   belongs_to :friend, :foreign_key => "friend_id", :class_name => "Person"  
end

And the tables would be like

people: id; name; whatever-you-need    
friendships: id; person_id; friend_id
Ju Nogueira
  • 8,435
  • 2
  • 29
  • 33
  • thank you, just a question to understand the background: how does rails know that belongs_to :friend, :foreign_key => "friend_id", :class_name => "Person" is defined in the friendships table (the field friend_id) ? – awex Aug 03 '10 at 13:44
  • I'm not sure I understood your question. As we can read in railsapi.com, "the mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases". And this is the case here, because of the class and table names. – Ju Nogueira Aug 03 '10 at 13:58
  • 3
    Don't know why this wasn't accepted. Is a very good answer. I need to try this and i will see if it behaves as expected. – Amala Nov 22 '10 at 03:34
  • Wouldn't this create an asymmetric relationship between users? In other words, if Person A is a in the `friends` for Person B with this modeling, then there is one record in `friendships` to reflect this relationship. But in order for Person B to also be in the `friends` for Person A, there would need to be a second, mirrored record. Correct? – RocketGuy3 Aug 16 '16 at 07:06
  • @RocketGuy3, for each friendship created, you need to insert 2 rows into friendships table. – bkdir Sep 17 '17 at 19:47
18

If it doesn't make too much sense to create another class to join the two, an alternative approach could be:

class Word < ActiveRecord::Base 
  has_and_belongs_to_many :synonyms, class_name: "Word", 
                                     join_table: "word_synonyms",
                                     association_foreign_key: "synonym_id"
end

The join table would look like this:

create_table :word_synonyms do |t|
  t.integer :word_id
  t.integer :synonym_id
end
Ibrahim Muhammad
  • 2,808
  • 4
  • 29
  • 39
0

Unfortunately whistler's answer might not be suitable in many cases. For instance, it doesn't work both ways. For instance, suppose you create a new word:

word = Word.create(:word_name => 'tremble')
['shake', 'vibrate'].each { |syn| word.synonyms.create(:word_name => syn) }

Now, if you do:

word = Word.find_by_word_name('tremble')
p word.synonyms # this would print out the Words with the word_name 'shake' and 'vibrate'.

however, if you did it the other way around:

word = Word.find_by_word_name('vibrate')

p word.synonyms # this would print an empty association.

This is saying the word 'vibrate' has NO synonyms.

So basically, this method won't work both ways (i.e. vibrate is a synonym of tremble, and tremble is a synonym for vibrate)

Edit: In a sense, you could use this approach, however, you would have to explicitly assign the synonyms for each word. So although you specified synonyms of tremble (which are 'vibrate' and 'shake'), you would have to still specify synonyms of 'shake' ('which are 'tremble' and 'vibrate') and 'vibrate' (which are 'tremble' and shake') as well.

ad56
  • 13
  • 4