Reputation: 555
In my rails app I have two models:
Person
and Company
. I need to specify many-to-many relationship between any pairs of this objects. So I should be able do like this:
@connections = @person.connections
where @connections
is an array of Person
and Company
objects.
Right now I've created ConnectionBinding
model for this and it does not work as wanted:
class ConnectionBinding < ActiveRecord::Base
belongs_to :connect_from, polymorphic: true
belongs_to :connect_to, polymorphic: true
end
It thougths ActiveRecord::HasManyThroughAssociationPolymorphicSourceError
exception
Does anybody already solve this problem? Any suggestions appreciated.
Upvotes: 3
Views: 2024
Reputation: 1388
A Concern:
module Linkable
extend ActiveSupport::Concern
included do
has_many :links_to, as: :linkable_to, class_name: 'Link' # [this]->[other object]
has_many :links_from, as: :linkable_from, class_name: 'Link' # [other object]->[this]
end
end
Links Table:
class Link < ActiveRecord::Base
#The idea for this class is to by a double polymorphic table, linking an object to another object
belongs_to :linkable_from, :polymorphic => true
belongs_to :linkable_to, :polymorphic => true
end
Upvotes: 0
Reputation: 555
Thanx Chris for showing the idea. But I need to do some changes to make this work.
The problem of Chris example is that if I call person_connections
method it will generate wrong query.
Person.find(720).person_connections
generates this SQL
SELECT "people".* FROM "people"
INNER JOIN "connection_bindings"
ON "people"."id" = "connection_bindings"."connect_to_id"
WHERE "connection_bindings"."person_id" = 720
AND "connection_bindings"."connect_to_type" = 'Person'
That person_id
column should be connect_from_id
. So I need to add :as => :connect_from
option to has_many :connection_bindings
to show ActiveRecord that its a polymorphic association.
has_many :connection_bindings, :as => :connect_from
has_many :person_connections, :through => :connection_bindings,
:source => :connect_to, source_type: 'Person'
has_many :company_connections, :through => :connection_bindings,
:source => :connect_to, source_type: 'Company'
But it is still not enough. I need to be able to get reverse connections. So if Person @a
adds connection to Person @b
? method connections should show that connection in both directions @a.connections
and @b.connections
.
And now I think I can do this by adding couple of additional associations and aggregation methods.
Upvotes: 2
Reputation: 12181
You need to tell ActiveRecord the associated column that it is looking for. I'm guessing you want to have the following:
class Person < ActiveRecord::Base
has_many :connection_bindings
has_many :companies, :through => :connection_bindings
has_many :people, :through => :connection_bindings
end
class company < ActiveRecord::Base
has_many :connection_bindings
has_many :companies, :through => :connection_bindings
has_many :people, :through => :connection_bindings
end
The problem there is that you have two tables putting there id in one column and Rails doesn't know which table to look up.
For example, on any given connection_binding row in the database, the connect_from can be either a company_id or a person_id and the same is true of connect_to. So you say: 'Hey Rails, load up my associated ConnectionBindings' and it gets a row where the connect_from is 11 and the connect_to is 12. But does it do Person.find(12) or Company.find(12)? There is no way to tell!
Instead, you'll have to give Rails some more information:
class Person < ActiveRecord::Base
has_many :connection_bindings
has_many :person_connections, :through => :connection_bindings, :source => :to_connect, :source_type => 'Person'
has_many :company_connections, :through => :connection_bindings, :source => :to_connect, :source_type => 'Company
def connections
person_connections + company_connections
end
end
You'll need to build that on the other side (as well as the associated :from_connect's) but it depends on how you're using these connections. Should be enough to get you started.
It is a lot more typing than you're used to in the magical world of Ruby and Rails, but it is a very complex data pattern that you're trying to construct. That doesn't mean it is impossible – Rails, as a good framework, won't stop you from doing anything you really want to – but it is uncommon enough to require some explicitness on your end.
Upvotes: 7