Polymorphic relation question


#1

I am trying to get polymorphic relations to work for my app. What I am
trying to do is explained by this picture
http://iroll.org/code/
I can’t figure out the code for in the controller to create a new
phone_number and assign it to a person. I have tried everything I can
think of.

A) do my models look correct?
B) what would the commands look like in the console or a controller
action look like to save something to a person?

I have been messing with this all afternoon, I got the example in the
recipe book working, I just can’t get this working.


#2

scott <scottnj@…> writes:

action look like to save something to a person?

I have been messing with this all afternoon, I got the example in the
recipe book working, I just can’t get this working.

These models will definitely work. I have a very similar structure in
my app,
except that I reuse the same contact-item PK ID in the
address/phone/email
tables (which was probably more hassle than it was worth).

The creation code in controller goes something like this:

@person = Person.find(params[:id])
@person.create_phone(params[:phone])

Tough, huh? It all happens in the model:

class Person

end


#3

Kian <kianwright@…> writes:

tables (which was probably more hassle than it was worth).
end

Well that was weird. Guess I accidentally posted the response. Anyway,
something
like this:
class Person
def create_contact_item(type)
contact_item = ContactItem.new({:person_id => self.id,
:addressable_type =>
type})
contact_item.save && contact_item
end

def create_phone(params)
contact_item = create_contact_item(AddressableType::PHONE)
phone = Phone.new(params)
phone.id = contact_item.id
phone.save && phone
end
end

The trouble with your model is going to be that you need to get a unique
ID for
the phone/address/email to be the PK, but you also need it to be already
present
in the contact-item table to satisfy the foreign key. Unless you
generate it
external to the database (not recommended), I don’t see how you will be
able to
easily do it. I’d recommend trying to use the same ID for the
contact-item and
the child tables too.

If you’re really ambitious, you can set up a person has many phones and
probably
override the << method on the person.phones to accept your request
parameters
and create a new phone and contact item. That’d be cool.


#4

These models will definitely work. I have a very similar structure in
my app,
except that I reuse the same contact-item PK ID in the
address/phone/email
tables (which was probably more hassle than it was worth).

The creation code in controller goes something like this:

@person = Person.find(params[:id])
@person.create_phone(params[:phone])

Tough, huh? It all happens in the model:

class Person

end

That does not seem to work for me.
In the console I type:
p = Person.find(:first)
p.create_email(:label => “work”, :address => "removed_email_address@domain.invalid)

The second line returns:
NoMethodError: undefined method ‘create_email’ for #Person:0x37a66e8

What’s different?


#5

On 9-May-06, at 7:46 PM, scott wrote:

@person.create_phone(params[:phone])
p.create_email(:label => “work”, :address => "removed_email_address@domain.invalid)

The second line returns:
NoMethodError: undefined method ‘create_email’ for #Person:0x37a66e8

What’s different?

I too have virtually the same data model, and have the same problem
as you Scott.

…which leads me to ask if there’s a way to query all methods for an
object, which may help debug such a symptom.

Jodi


#6

scott <scottnj@…> writes:

Sorry, I didn’t expand on it in more detail. I assumed that you had a
model or
class or module that enumerates the addressable-type values. Instead of
AddressableType::Phone, put whatever value you use to for your
addressable-type
field in the database to indicate that the contact-item is a phone. At
least
that’s what I think that field means. Ignore this comment if that’s not
what the
field is in your model. Maybe I misunderstand…


#7

Kian wrote:

Well that was weird. Guess I accidentally posted the response. Anyway,
something
like this:
class Person
def create_contact_item(type)
contact_item = ContactItem.new({:person_id => self.id,
:addressable_type =>
type})
contact_item.save && contact_item
end

def create_phone(params)
contact_item = create_contact_item(AddressableType::PHONE)
phone = Phone.new(params)
phone.id = contact_item.id
phone.save && phone
end
end

The trouble with your model is going to be that you need to get a unique
ID for
the phone/address/email to be the PK, but you also need it to be already
present
in the contact-item table to satisfy the foreign key. Unless you
generate it
external to the database (not recommended), I don’t see how you will be
able to
easily do it. I’d recommend trying to use the same ID for the
contact-item and
the child tables too.

If you’re really ambitious, you can set up a person has many phones and
probably
override the << method on the person.phones to accept your request
parameters
and create a new phone and contact item. That’d be cool.

I think I see what you are doing here, but when I try
p = Person.find(:first)
p.create_phone(:label => “work”)

I get
NameError: unitialized constant AddressableType

How do you get type passed for the polymorphic relations? I also tried
putting it in quotes,that stores “AddressableType::PHONE” in the table
but addressable_id is still null.


#8

scott, you wrote,

I am trying to get polymorphic relations to work for my app. What I am
trying to do is explained by this picture
http://iroll.org/code/
I can’t figure out the code for in the controller to create a new
phone_number and assign it to a person. I have tried everything I can
think of.

You’ve got your polymorph relationships around the wrong way. The
people
have many contact_items as addressable, ie.:

class Person < ActiveRecord::Base
has_many :contact_items, :as => :addressable

# then your create_phone_number method would look something like 

this
def create_phone_number(params)
ci = ContactItem.new(:person_id => id)

  # this next line *is* the polymorphism
  ci.addressable = PhoneNumber.create(params)
  ci.save
  contact_items << ci
end

end

Note that one of the benefits of polymorphism is that you DON’T need to
worry
about overlapping PKs in the various address tables, and you DON’T need
to
worry about doing the addressable_type yourself. Rails handles the type
for
you, and the fact that there’s a type means that the PKs can (and
should)
overlap, that’s not a problem because the foreign key is really a
multi-column foreign key including the addressable_type.

So, just to complete the picture, contact_item.rb was correct, but all
your
individual address tables need to drop the :as => :addressable, here’s
the
PhoneNumber one, the rest are the same idea:

class PhoneNumber < ActiveRecord::Base
has_one :contact_item
end

To see this in the console:

p = Person.create( :first_name => “Foo” )
p.create_phone_number(:first_numbers => “123”, :second_numbers =>
“456”)

p.contact_items[0].addressable_type
p.contact_items[0].addressable.first_numbers