Has_one :item, :polymorphic => true

I have a relationship were a person can have 1 item, and this item is
either a A || B || C.
Rails supports the belongs_to polymorphic, but I don’t see how can a
Model have one of several models as opposed to belong_to one of
several models.
Can anyone tell me how to map this relationship ?

Is this a one-to-one, or one-to-many relationship?

Which class should has the foreign key? Do your A, B, and C classes
have a foreign key to person, or do you want person to have a foreign
key to one of the other 3 models?

Yes I’d like person to have the foreign key to have a foreign key to
one of the other 3 models.

Yup, then like Marnen said, Person should belong to Item:

belongs_to :item, :polymorphic => true

In your migration, you can use:

t.references :item, :polymorphic => true

Which will create item_type and item_id columns.

Then each class A, B, and C can have one person:

has_one :person, :as => :item

dpal wrote in post #958489:

I have a relationship were a person can have 1 item, and this item is
either a A || B || C.
Rails supports the belongs_to polymorphic, but I don’t see how can a
Model have one of several models as opposed to belong_to one of
several models.
Can anyone tell me how to map this relationship ?

You want Person belongs_to Item. Remember, belongs_to is used for the
table that contains the foreign key field.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Awesome thank you so much, I had come to that conclusion although it
didn’t seem natural.
This is what my spec looks like, I think its correct:

Database Associations

What we want:

There are users, construction companies, real state companies and
natural persons. A user controls one of the 3 entities(construction
companie, real state company or natural person). And each of this
entities has listings, and in the case of construction company also
projects. The relationships are as followed:

  1. user has_one :construction_company or :real_state_company
    or :natural_person

  2. construction_company has_many —> :projects which has_many —> :
    listings

  3. real_state company has_many ----> :listing

  4. natural_person has_one----> :listings

How this looks in code:

We need to use polymorphic associations to be able to express this
relationships, because for example listings belongs to all 3 type of
entities, thus listings has to be polimorphic.

:listings belongs to-> projects, real_state_company, natural_person

We start by looking at what this 3 entities have in common, mainly
that they are the sellers, so we will add to the databes a foreign key
that lists the seller identification(seller_id) and which type of
seller he is(construction company or real state company etc).

In rails this is accomplished by:

$rails generate model Listing . seller_id:integer seller_type:string

This will ad a seller_id and seller_type to the migrations. Rails has
a way of simplifying this by replacing the 2 keys with:

t.references :seller, :polymorphic => true
which will create both keys for use.

After, inside the listing model then we can say that the listing
belongs to a seller instead of explicitly saying which
seller(company…) this is expressed using the following code

class Listing < ActiveRecord::Base

belongs_to :seller, :polymorphic => true

end

Then we can add the relationship to each one of the entities using :as

class Real_State_Company < ActiveRecord::Base

has_many :listings, :as => :seller

end

Until here we have taken care of the relationship were a listing
belongs to any 3 entities(projects, real_state_company and
natural_person). Now we need to look at the case were the construction
company, real state company or natural user belongs to a User. Since
this is the reverse, we start looking what does the user has, he
has_one of the 3 things(company, real state company or
natural_person), we’ll call this entities, thus in rails we can say:

class User < ActiveRecord::Base

has_one :entity, :polymorphic => :true

end

And then each of the entities will have:

class Real_State_Company < ActiveRecord::Base

belongs_to :user, :as => :entity

end

We need to modify the user model with entity_id and entity_type, or:

t.references :entity, :polymorphic => true

Unfortunatelly has_one polymorphic is not supported, therefore we need
to think in reverse.

class User < ActiveRecord::Base

belongs_to :ownable, :polymorphic => :true

end

And then each of the entities will have:

class Real_State_Company < ActiveRecord::Base

has_one :user, :as => :ownable

end

We need to modify the user model with entity_id and entity_type, or:

t.references :entity, :polymorphic => true

Then to get if the user owns a company or natural person do
@user.ownable, similary to see which user owns a company
@company.user.

Now there is also the fact that user has to know what they own.
If I go with the

belongs_to :ownable, polymorphic => true.

From user then user.ownable will either return company,
real_state_company or natural user.
I would have to do user.ownable_type to see which one and use a string
comparisson.

If I go the other route:

has_one :real_state_company
has_one :construction_company
has_one :natural_person

I can say,

if(user.construction_company != nil)
{
company = user.company
redirect_to company
}
else if (user.real_state_company)
{

}
etc.

Which one is cleaner, better design ?

Thanks
-dan

On Nov 1, 2:18pm, dpal [email protected] wrote:

Unfortunatelly has_one polymorphic is not supported, therefore we need
to think in reverse.

That’s because if the “belongs_to” is on the company models, it’s no
longer a polymorphic relationship. At that point it’s just a regular
one-to-one relationship, like the following example. Each of the
companies has a foreign key to Person.

class RealStateCompany
belongs_to :user
end

class ConstructionCompany
belongs_to :user
end

class NaturalPerson
belongs_to :user
end

class User
has_one :real_state_company
has_one :construction_company
has_one :natural_person
end

This works. Reversing the relationship also works. Just depends on how
you want to design it and where you want the foreign keys.

Awesome help, thanks everyone. Just finished implementing the
polymorphic way, and the code looks really clean.
Thanks
-dan

dpal wrote in post #958568:

Now there is also the fact that user has to know what they own.
If I go with the

belongs_to :ownable, polymorphic => true.

From user then user.ownable will either return company,
real_state_company or natural user.

(I think you mean “real estate”, not “real state”.)

I would have to do user.ownable_type to see which one and use a string
comparisson.

Or user.ownable.class and use a class comparison.

Or better yet, do neither and use object polymorphism to automatically
dispatch. Remember, one of the nice things about OO development is that
objects know how to respond to messages – you often don’t need
conditionals.

If I go the other route:

has_one :real_state_company
has_one :construction_company
has_one :natural_person

I can say,

if(user.construction_company != nil)
{
company = user.company
redirect_to company
}
else if (user.real_state_company)
{

}

This isn’t Java. Lose the braces, parens, and “!= nil” and use “elsif”
instead of “else if”.

etc.

Which one is cleaner, better design ?

The polymorphic design. It allows you to actually harness the object
model to your advantage.

Thanks
-dan

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]