Polymprphic == confusion


#1

I have been having trouble setting up some relations.

I want to be able to do this:

user.contact_items # an array of addresses, websites, phone
numbers, etc.
user.addresses # an array of addresses
user.websites # an array of websites
user.phone_numbers # an array of phone numbers
etc.

and the same for businesses, charities, etc.

Since users, businesses, and charities all can have multiple contact
items I assume a polymorphic relation makes sense.

I also want to be able to do
address.owner # a user, business, or charity
website.owner # a user, business, or charity
phone_number.owner # a user, business, or charity
etc.

After much searching, reading and experimenting, my head is beginning
to hurt. Can someone get me pointed in the right direction? How do I
setup my models? Will it be possible to add fields to my join table so
I can flag contact items with things like verified, public, labels,
etc?

OWNERS

class Users
has_many ???
belongs_to ???

class Businesses
has_many ???
belongs_to ???

class Charities
has_many ???
belongs_to ???

JOIN TABLE

class ContactItems
has_many ???
belongs_to ???
#public:boolean
#verified:boolean
#label:string

CONTACT ITEMS

class Addresses
has_many ???
belongs_to ???

class EmailAddresses
has_many ???
belongs_to ???

class PhoneNumbers
has_many ???
belongs_to ???

class Websites
has_many ???
belongs_to ???


#2

Maybe the below will help. I also know that railsenvy has a good video
on this.

First of all, it is the tables and associations that are plural, not
the classes.
I suggest that you would use single table inheritance for the users
business etc.
You need to have a type column defined in the owners table to signify
class for the record

Here’s my suggestion:

class Owner < ActiveRecord::Base
has_many :addresses, :through => :contact_items
has_many :websites, :through => :contact_items
has_many :phone_numbers, :through => :contact_items
end
class User < Owner
end
class Business < Owner
end
class Charities < Owner
end

migration

create_table :owners do |t|

t.string :type
end

Polymorphic Association could be is solved like this

class ContactItem < ActiveRecord::Base
belongs_to :owners
belongs_to :contacts, :polymorphic => true
end

migration

create_table :contact_item do |t|

t.string :contact_type
t.integer :contact_id
end

class Address < ActiveRecord::Base
has_one :owner, :thorugh => :contact_items
end

class EmailAddress < ActiveRecord::Base
has_one :owner, :thorugh => :contact_items
end
class PhoneNumber < ActiveRecord::Base
has_one :owner, :thorugh => :contact_items
end
class Website < ActiveRecord::Base
has_one :owner, :thorugh => :contact_items
end

You can use either belongs_to or has_one on the above classes


#3

End the link to railsenvy video tutorial:
http://www.railsenvy.com/2007/8/8/activerecord-tutorial


#4

google for acts_as_double_polymorphic_join

From my app,

Table name: linkages

id :integer(11) not null, primary key
origin_id :integer(11)
origin_type :string(20)
destin_id :integer(11)
destin_type :string(20)

In linkage.rb:

belongs_to :origin, :polymorphic => true
belongs_to :destin, :polymorphic => true

acts_as_double_polymorphic_join(
:origins => [list your models that can be origins],
:destins => [list your models that can be destins]
)

Pretty sweet plugin.


#5

It looks like that is an option that might work, but is there a way to
do this without using single table inheritance?

Is it possible to do something like this in rails?

Owners

class User < ActiveRecord::Base
has_many :contact_items, :through => :contact_links
has_many :addresses, :through? => :contact_links?, :as?
=> :contact_items?
has_many :…
end
class Business < ActiveRecord::Base
has_many :contact_items, :through => :contact_links
has_many :…
end
class Charity < ActiveRecord::Base
has_many :contact_items, :through => :contact_links
has_many :…
end

Double Polymorphic Join Model???

class ContactLink < ActiveRecord::Base
# id:int
# owner_id:int
# owner_type:string
# contact_item_id:int
# contact_item_type:string

  belongs_to :owners, :polymorphic => true
  belongs_to :contact_items, :polymorphic => true

end

Contact Items

class Address < ActiveRecord::Base
has_one :owner, :through => :contact_links
end
class EmailAddress < ActiveRecord::Base
has_one :owner, :through => :contact_links
end
class PhoneNumber < ActiveRecord::Base
has_one :owner, :through => :contact_links
end


#6

I guess you are using the has_many_polymorphs plugin.
http://github.com/fauna/has_many_polymorphs/tree/master

looks interesting. i’ll give it a try.

thanks,
scott


#7

I couldn’t find a complete example of a double polymorphic
relationship so I will post what I have in case someone comes across
this using Google. It seems to be working so far, but I don’t know if
I have everything setup correctly yet.

install as a plugin or as a gem

git submodule add git://github.com/fauna/has_many_polymorphs.git
vendor/plugins/has_many_polymorphs
git submodule init
git submodule update

setup the models

ruby script/generate model User name:string
ruby script/generate model Business name:string
ruby script/generate model Charity name:string

ruby script/generate model Address street:string street2:string
city:string state:string zip:string
ruby script/generate model EmailAddress address:string
ruby script/generate model Website address:string
ruby script/generate model PhoneNumber number:string

ruby script/generate model Linkage

edit the linkage migration

class CreateLinkages < ActiveRecord::Migration
def self.up
create_table :linkages do |t|
t.references :origin, :polymorphic => true
t.references :destin, :polymorphic => true
t.timestamps
end
end
def self.down
drop_table :linkages
end
end

add the relationships to the models

Origins

class User < ActiveRecord::Base
has_many_polymorphs :destins, :from =>
[:addresses, :email_addresses, :phone_numbers, :websites], :through
=> :linkages
end
class Business < ActiveRecord::Base
has_many_polymorphs :destins, :from =>
[:addresses, :email_addresses, :phone_numbers, :websites], :through
=> :linkages
end
class Charity < ActiveRecord::Base
has_many_polymorphs :destins, :from =>
[:addresses, :email_addresses, :phone_numbers, :websites], :through
=> :linkages
end

Double Polymorphic Join Model

class Linkage < ActiveRecord::Base
# id:int
# origin_id:int
# origin_type:string
# destin_id:int
# destin_type:string

  belongs_to :origin, :polymorphic => true
  belongs_to :destin, :polymorphic => true

  acts_as_double_polymorphic_join(
        :origins => [:users, :businesses, :charities],
        :destins =>

[:addresses, :email_addresses, :phone_numbers, :websites]
)
end

Destins

class Address < ActiveRecord::Base
has_many_polymorphs :origins, :from =>
[:users, :businesses, :charities], :through => :linkages
end
class EmailAddress < ActiveRecord::Base
has_many_polymorphs :origins, :from =>
[:users, :businesses, :charities], :through => :linkages
end
class PhoneNumber < ActiveRecord::Base
has_many_polymorphs :origins, :from =>
[:users, :businesses, :charities], :through => :linkages
end

How It Works

rake db:migrate
ruby script/console

u=User.new
u.name=“Scott”
u.save
w=Website.new
w.address=“http://google.com
w.save
u.destins << w
u.destins
=> [#<Website id: 1, address: “http://google.com”, created_at:
“2009-01-08 21:55:12”, updated_at: “2009-01-08 21:55:12”>]

w.origins
=> [#<User id: 1, name: “scott”, created_at: “2009-01-08 21:54:00”,
updated_at: “2009-01-08 21:54:00”>]

u.websites
=> [#<Website id: 1, address: “http://google.com”, created_at:
“2009-01-08 21:55:12”, updated_at: “2009-01-08 21:55:12”>]


#8

On Jan 8, 5:25 pm, scott removed_email_address@domain.invalid wrote:

I couldn’t find a complete example of a double polymorphic
relationship so I will post what I have in case someone comes across
this using Google. It seems to be working so far, but I don’t know if
I have everything setup correctly yet.

My last post was wrong. The only place we need to define the
relationships is in the join model, the other models can be blank. It
is even cleaner now!!!
One problem I still have is the “destins” (addresses ,phones ,emails,
etc.) can belong to many “origins”. I need to make this work as a
has_one instead of a has_many.

git submodule add git://github.com/fauna/has_many_polymorphs.git
vendor/plugins/has_many_polymorphs
git submodule init
git submodule update

ruby script/generate model User name:string
ruby script/generate model Business name:string
ruby script/generate model Charity name:string

ruby script/generate model Address street:string street2:string
city:string state:string zip:string
ruby script/generate model EmailAddress address:string
ruby script/generate model Website address:string
ruby script/generate model PhoneNumber number:string

ruby script/generate model Linkage

class CreateLinkages < ActiveRecord::Migration
def self.up
create_table :linkages do |t|
t.references :origin, :polymorphic => true
t.references :destin, :polymorphic => true
t.timestamps
end
end
def self.down
drop_table :linkages
end
end

Origins

class User < ActiveRecord::Base
end
class Business < ActiveRecord::Base
end
class Charity < ActiveRecord::Base
end

Double Polymorphic Join Model

class Linkage < ActiveRecord::Base
# id:int
# origin_id:int
# origin_type:string
# destin_id:int
# destin_type:string

  belongs_to :origin, :polymorphic => true
  belongs_to :destin, :polymorphic => true

  acts_as_double_polymorphic_join(
        :origins => [:users, :businesses, :charities],
        :destins =>

[:addresses, :email_addresses, :phone_numbers, :websites]
)
end

Destins

class Address < ActiveRecord::Base
end
class EmailAddress < ActiveRecord::Base
end
class PhoneNumber < ActiveRecord::Base
end

How It Works

ruby script/console

u=User.new
u.name=“Scott”
u.save
w=Website.new
w.address=“http://google.com
w.save
u.destins << w
u.destins
=> [#<Website id: 1, address: “http://google.com”, created_at:
“2009-01-08 21:55:12”, updated_at: “2009-01-08 21:55:12”>]

w.origins
=> [#<User id: 1, name: “scott”, created_at: “2009-01-08 21:54:00”,
updated_at: “2009-01-08 21:54:00”>]

u.websites
=> [#<Website id: 1, address: “http://google.com”, created_at:
“2009-01-08 21:55:12”, updated_at: “2009-01-08 21:55:12”>]