Rails has_many through for has_many with multiple models
Asked Answered
G

2

2

What would be the best way to model the following situation:

Word
  belongs_to :wordable, :polymorphic => true


Phrase
  has_many :words, :as => :workable
  belongs_to :story

Line
  has_many :words, :as => :wordable    
  belongs_to :story


Story
 has_many :lines      
 has_many :phrases
 has_many :words, :through => :phrases
 has_many :words, :through => :lines

I want to be able to do

 @story.words 

to get list of all words that are linked to a story either via lines or via phrases...

Is that possible?

Glucoside answered 25/6, 2012 at 16:48 Comment(0)
P
6

Try this:

class Story

  has_many :lines      
  has_many :phrases

  def words(reload=false)
    @words = nil if reload
    @words ||= Word.where("(wordable_type = ? AND wordable_id IN (?)) OR
                            (wordable_type = ? AND wordable_id IN (?))", 
                           "Phrase", phrase_ids, "Line", line_ids)    
  end
end

Now

story.words # returns line and phrase words
story.words.limit(5) 
Preparatory answered 25/6, 2012 at 17:51 Comment(3)
Thanks, this is perfect. Could you please explain what the = nil if reload is for? Thanks much.Lives
The Story object caches the results in an instance variable called @words. Under certain scenarios you might want to bust the cache. You can do so by passing true value for the reload parameter.Preparatory
Oh, I see, thanks. I see it could be good to have this option to access from the console or a rake task if the need arises.Lives
R
0

You can remove the 2 has_many :words, :through => XXX relations from the Story class, and define a method words instead :

def words 
  ([] << lines.collect {|line| line.words} << phrases.collect {|phrase| phrase.words}).flatten
end
Retina answered 25/6, 2012 at 17:19 Comment(6)
Thank you, switching from polymorphic to a relation via phrase_id and line_id in Word model is a bad idea?Glucoside
With the polymorphic relation, you will be able to add a new Wordable more easily. With your second suggestion, you'll need a migration to add a column for each new WordableRetina
thanks, since you were the first, I will accept this as an answer.Glucoside
@stpn, You will not be able to chain the sql and paginate the result-set when you append the lists in Ruby (rather than SQL).Preparatory
@stpn The solution of KandadaBoggu is better.Retina
ok guess I will read more about it.. accepting KandadaBoggu's answer now thanks a lot.Glucoside

© 2022 - 2024 — McMap. All rights reserved.