0

I have a following model structure. I have an Itinerary that has many itinerary nodes. Each itinerary node is a wrapper around either a place, hotel, activity etc. So for example.

Itinerary = "Trip to Paris" Itinerary.itinerary_nodes = [Node1, Node2, Node3] where Node1 = "JFK Airport" Node2 = "CDG Airport" Node3 = "Eiffel Tower"

So essentially nodes represents the places you will visit in your itinerary. In my model structure; lets assume that my airports are modeled different from monuments or hotels. Now I want to create a association such that;

class ItineraryNode
  include Mongoid::Document
  has_one :stopover
end

Where each stopover can be a different object. It's type and id is stored by default and is later inflated using that.

So how do I declare multiple models to be associated to ItineraryMode? I can implement this specifically by ensuring that I set these attributes manually in initializer; but curious if something like this is supported by default.

Cheers

Priyank
  • 14,231
  • 18
  • 78
  • 107

1 Answers1

4

This is not a "has_one", it is a "belongs_to" (polymorphic)

class ItineraryNode
  include Mongoid::Document
  belongs_to :stopover, :polymorphic => true
  belongs_to :itinerary
end

class Airport
  include Mongoid::Document
  has_many :itinerary_nodes, :as => :stopover
end

class Place
  include Mongoid::Document
  has_many :itinerary_nodes, :as => :stopover
end

So now you can get:

@itinerary.itinerary_nodes.each do |node|
  if node.stopover.is_a? Airport
    puts "Welcome to #{note.stopover.name}"
  elsif node.stopover.is_a? Event
    puts "Bienvenue, would you like a drink?"
  elsif node.stepover.is_a? Place
    puts "The ticket line is over there"
  end
end

(I used an if construct just to show better the polymorphism, you would use a case construct here...)

You see that node.stepover can be of many classes.

EDIT (after the comment, I understand that the ItineraryNodemodel is an attempt to a handcrafted polymorphism for a many-to-many association.

From the Mongoid documentation:

Polymorhic behavior is allowed on all relations with the exception of has_and_belongs_to_many.

So you need to use an intermediate model (ItineraryNode). The provided solution is the simplest one I can think of.

rewritten
  • 16,280
  • 2
  • 47
  • 50
  • My intention is to have itinerary have many nodes; where a node wraps around a stopover. I understand the representation you have given; but I don't want to say in my Itinerary that Itinerary has_many airports; has_many place etc. I want to say Itinerary has_many nodes. And then node can abstract away the underlying place. So I want Node to be able to say has_one :something and then that something can either be an airport or a place or whatever. I don't see it possible to achieve that from what you have shown above. – Priyank Oct 06 '12 at 07:06
  • 1
    The point is that "has_one" implies a "belongs_to" on the other side. You don't want an Airport to belong to a node (it would belong **only to that node**). So what you want is that the node **belongs_to** something, and that something can be whatever needed (that is polymorphism). I'll edit the responses to be clearer. – rewritten Oct 06 '12 at 07:09
  • Excellent! It's all clear now. Thanks for taking all the effort to explain it in such detail. I see what you meant, now. Cheers. – Priyank Oct 06 '12 at 11:17
  • Awesome, I didn't even know, that this was possible. – adbeel Jul 22 '15 at 21:36