class Patient < ApplicationRecord
has_many :sites_siteables, as: :siteable
has_many :sites, through: :sites_siteables
end
class Provider < ApplicationRecord
has_many :sites_siteables, as: :siteable
has_many :sites, through: :sites_siteables
end
# this is the join model between Site and Siteable(which is any model)
# you can generate this model by running the command:
# rails g model sites_siteable site:belongs_to siteable:references{polymorphic}
class SitesSiteable < ApplicationRecord
belongs_to :site
belongs_to :siteable, polymorphic: true
end
class Site < ApplicationRecord
has_many :sites_siteables
has_many :patients, through: :sites_siteables, source: :siteable, source_type: 'Patient'
has_many :providers, through: :sites_siteables, source: :siteable, source_type: 'Provider'
end
Usage
Patient.first.sites
# => returns Sites
Provider.first.sites
# => returns Sites
Site.first.patients
# => returns Patients
Site.first.providers
# => returns Providers
Comments
the above code: Site
has_many :patients
, has_many :providers
, and has_many :users
, etc... is required and you cannot simply collectively put them all as one has_many
; i.e. you can't do the following (it will generate an error):
# app/models/site.rb
has_many :siteables, through: :sites_siteables, source: :siteable
... because if this happens let's say you have Site.first.siteables
, then the value returnes will be a collection of differeent kinds of Models: i.e.:
Site.first.siteables[0] # => returns Provider
Site.first.siteables[1] # => returns Provider
Site.first.siteables[2] # => returns Patient
... and this is problematic because there is no single model to represent the query on this, and I guess not compatible with Rails code: i.e. why the following is confusing (at least, in the SQL-string generation side):
Site.first.siteables.where(phone_number: '123456')
... and is probably why Rails specifically does not allow, and raises an error (on a polymorphic association), and that you are required to specify then the source_type
:
has_many :siteables, through: :sites_siteables, source: :siteable
However...
If you really intend to not have so many lines of has_many
in the Site
model like the following:
has_many :patients, through: :sites_siteables, source: :siteable, source_type: 'Patient'
has_many :providers, through: :sites_siteables, source: :siteable, source_type: 'Provider'
has_many :users, through: :sites_siteables, source: :siteable, source_type: 'User'
# ...
# ...
... you can create another "Abstract" table/model to represent the polymorphic record (I'll update the answer if you wish so; let me know). i.e. you can do something like the following instead:
Site.first.abstract_siteables[0].siteable
# => returns a Patient, or a Provider, or a User, etc...
Site.first.abstract_siteables[1].siteable
# => returns a Patient, or a Provider, or a User, etc...
Site.first.abstract_siteables[2].siteable
# => returns a Patient, or a Provider, or a User, etc...