Forum: Ruby on Rails polymprphic == confusion

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Scott N. (Guest)
on 2009-01-08 08:36
(Received via mailing list)
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 ???
stonefield (Guest)
on 2009-01-08 10:59
(Received via mailing list)
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
stonefield (Guest)
on 2009-01-08 11:00
(Received via mailing list)
End the link to railsenvy video tutorial:
  http://www.railsenvy.com/2007/8/8/activerecord-tutorial
Scott N. (Guest)
on 2009-01-08 19:10
(Received via mailing list)
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
Ar C. (Guest)
on 2009-01-08 19:22
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.
Scott N. (Guest)
on 2009-01-08 21:28
(Received via mailing list)
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
Scott N. (Guest)
on 2009-01-09 00:26
(Received via mailing list)
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">]
Scott N. (Guest)
on 2009-01-09 01:08
(Received via mailing list)
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">]
This topic is locked and can not be replied to.