I am trying to set up a polymorphic entity has_one location, but a location can belong to many polymorphic entities. The confusion I'm running into is where I need to specify polymorphic associations, and guess check isn't working, lol.
To clarify, any "locatable" should have one location, but a location should be able to have many locatables associated with it.
Current setup:
class User
has_one :locatable_location, as: :locatable
has_one :location, through: :locatable_locations
end
class Entity
has_one :locatable_location, as: :locatable
has_one :location, through: :locatable_locations
end
class LocatableLocation
belongs_to :locatable, polymorphic: true
belongs_to :location
end
class Location
has_many :locatable_locations
has_many :locatables, polymorphic: true, through: :locatable_locations
end
Any help greatly appreciated :)
CodePudding user response:
Gonna answer my own question since I ended up figuring it out.
The error was in the Location model. Change to
class Location
has_many :locatable_locations
has_many :users, through: :locatable_locations, source: :locatable, source_type: :User
has_many :entities, through: :locatable_locations, source: :locatable, source_type: :Entity
end
and if you wanted to have a relation to "locatables", i.e. get all records associated you'll have to define a method in location like
def locatables
users entities
end
If your use case has many, many locatables like mine, so you can also do something like
class Location
RELATIONS = %i[User Entity]
has_many :locatable_locations
RELATIONS.each do |associated_klass|
has_many associated_klass.to_s.snake_case.to_sym, through: :locatable_locations, source: :locatable, source_type: associated_klass
end
def locatables
RELATIONS.reduce([]) { |all, curr| all.append(send(curr.to_s.snake_case)) }
end
CodePudding user response:
I would consider if you're not just overcomplicating this needlessly. The only actual advantage to using a polymorphic join table is that you make additional models "locatable" without adding a column to the table. You can't actually use it as a homogenious collection without risking N 1 queries as polymorphic assocations don't support eager loading.
The way this is implemented there is also no guarentee that a user can only have one "current location" - duplicates can occur due to simple race conditions. A has_one
assocation is actually just putting a LIMIT 1
on the query. belongs_to
on the other hand can only ever have a single value.
The simplest solution would be to simply add a foreign key column to the users table and a separate assocation:
module Locatable
extend ActiveSupport::Concern
included do
belongs_to :location
end
end
class User < ApplicationRecord
include Locatable
end
class Entity < ApplicationRecord
include Locatable
end
class Location < ApplicationRecord
has_many :users
has_many :entities
end
Magic always comes at a price - in this case its completely crippling your database design.