significance
significance

Reputation: 5105

how to get all instances of objects by polymorphic type?

I have the following models set up:

class Sound < ActiveRecord::Base  
  has_many :tenders, :as => :tenderable
  belongs_to :event
end

class Stage < ActiveRecord::Base
  has_many :tenders, :as => :tenderable
  belongs_to :event
end

class Tender < ActiveRecord::Base
  attr_accessible :event_id, :user_id, :estimate, :tenderable_id, :tenderable_type
  belongs_to :tenderable, :polymorphic => :true
end

class Event < ActiveRecord::Base
  attr_accessible :name
  has_one :stage
  has_one :sound

  accepts_nested_attributes_for :stage, :allow_destroy => true
  accepts_nested_attributes_for :sound, :allow_destroy => true
end

Each Event a variety of Tenderable's attached to it (eg. stage, sound), I can access these through Event.find(id).tenderables, but I need to work out which 'opportunities' are available regardless of the Event they're attached to.

At the moment I don't have a model for 'Opportunity' as I'm trying to keep things simple.

Really what I want to do is something like Tenderables.all which will return all the 'Sounds', 'Stages' and anything else I define as 'tenderable'.

What's the best way to achieve this?

Thanks ;)

Upvotes: 2

Views: 1357

Answers (3)

brainbag
brainbag

Reputation: 1057

The accepted answer is correct, but inefficient.

  1. To select all of the types:

Tender.distinct.pluck(:tendable_type)

This does more work in the database, and doesn't require each unique object to be instantiated in order to call a method on it.

  1. To select all of the objects:

Tender.where.not(tendable: nil).includes(:tendable).map(&:tendable)

This eager loads the association data all at once, instead of every time the loop runs. This will still load every record into memory, but it may be significantly more performant depending on the number of records you have. The accepted answer will hit the database on every Tender.

Upvotes: 0

nathanvda
nathanvda

Reputation: 50057

Afaik the list of all Tenderables, in the meaning of things that could have a Tender attached to it, is not possible to build from the database or model structure. In the database you would be able to find all Tenderable that have a Tender attached to it, but not in an easy way (think union of queries for each tenderable_type).

But the primary property of a polymorphic relation is actually that any kind of object could be attached to it, so you will have to manage the complete list of objects that could have a tender yourself.

I am not complete sure why you would want that, but you must do a union of Sound.all and Stage.all and whatever table will become tenderable later.

Now when writing this, I am thinking: to be able to do a union, those tables have to be pretty similar. If they are, in your case, you might need to introduce the Tenderable object for real, and use STI. For what you want right now that would be the best solution.

Whatever is Tenderable has to fit in a single table, with a type (Sound, Stage, ...) and then you get a normal relation to Tenderable. A Stage or Sound inherit from Tenderable. You can then easily select all Tenderables, or all Sounds/Stages seperately as well.

Hope this helps.

Upvotes: 3

davidb
davidb

Reputation: 8954

Tender.select("DISTINCT tendable_type").collect(&:tendable_type).compact

Will give you the name of all associated Models that have already been used at least once.

For all Objects that have been associated as tendable

Tender.where("tendable_type IS NOT NULL").collect(&:tendable)

Upvotes: 3

Related Questions