Ok, I just want to set this one in stone.
Rails’ polymorphic associations.
Ask yourself 2 questions:
Can this object belong to more than 1 type of object
class Car < ActiveRecord::Base
end
class House < ActiveRecord::Base
end
class LineItem < ActiveRecord::Base
belongs_to :salable, :polymorphic => true
end
Here’s a domain model from an app in which you can purchase both cars and
houses. They are both salable products that will appear on a line item in an
order. This is a many-to-1 relationship from a LineItem
to its salable
product.
Can this object belong to more than 1 type of object and more than 1 object
class User < ActiveRecord::Base
include Groupable
end
class Account < ActiveRecord::Base
include Groupable
end
class Membership < ActiveRecord::Base
belongs_to :groupable, :polymorphic => true
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :memberships
end
module Groupable
def self.included(clazz)
clazz.class_eval
has_many :memberships, :as => :groupable
end
end
end
Here we have a many-to-many between groupable objects and groups. Since a
Group
can contain members of different types it needs to be polymorphic.
However, in this example a single group can have multiple members all of
different types. Like any many-to-many we need a table between the 2 other
tables. Since habtm doesn’t support polymorphic associations we have to go with
a join model. So we introduced Membership
and made it have a polymorphic
relationship.
It’d be sweet if we didnt need this join model because I can’t stand unnecessary classes, something like:
class User < ActiveRecord::Base
include Groupable
end
class Account < ActiveRecord::Base
include Groupable
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :groupables
end
module Groupable
def self.included(clazz)
clazz.class_eval
has_and_belongs_to_many :groups, :as => :groupable
end
end
end
db schema:
groupables (groupable_id, groupable_type, group_id)
That’s some made up syntax. I’ll say if you have an :as
parameter to
#has_and_belongs_to_many
then Rails will look for a table named after the
polymorphic interface, in this case groupables
, and 2 columns groupable_id
and groupable_type
. Since the #has_and_belongs_to_many
call in the Group
model doesn’t include an :as
parameter, Rails will, like in a normal
#has_and_belongs_to_many
call, look for a table named after the association, in
this case groupables
, and a foreign key in that table referencing this model,
in this case group_id
.