ruby-on-railsaliashas-many-throughpolymorphic-associations

Rails has_many through multiple alias for same model


I have 3 Models. Archive -> ArchiveJoin <- Contacts

ArchiveJoin is a polymorphic join table. I want to be able to associate Archive and contacts through the join table and be able to query them using an alias.

For example a contact can be referenced as Author, Co-author, publisher etc.

My solution for this is on the archive table I reference the association like this.

    has_many :author, :through => :archive_joins, :source => :archiveable, :source_type => 'Contact'
    has_many :co_author, :through => :archive_joins, :source => :archiveable, :source_type => 'Contact'
    has_many :publisher, :through => :archive_joins, :source => :archiveable, :source_type => 'Contact'

The problem is that when I call any one of these I get the same contact rather then differentiating between there aliased names.

irb(main):082:0> Archive.first.author
=> #<ActiveRecord::Associations::CollectionProxy [#<Contact id: 1, created_at: "2022-06-06 14:26:35", updated_at: "2022-06-06 14:26:35">, #<Contact id: 2, created_at: "2022-06-06 15:36:24", updated_at: "2022-06-06 15:36:24">]>

irb(main):083:0> Archive.first.co_author
=> #<ActiveRecord::Associations::CollectionProxy [#<Contact id: 1, created_at: "2022-06-06 14:26:35", updated_at: "2022-06-06 14:26:35">, #<Contact id: 2, created_at: "2022-06-06 15:36:24", updated_at: "2022-06-06 15:36:24">]>

irb(main):084:0> Archive.first.publisher
=> #<ActiveRecord::Associations::CollectionProxy [#<Contact id: 1, created_at: "2022-06-06 14:26:35", updated_at: "2022-06-06 14:26:35">, #<Contact id: 2, created_at: "2022-06-06 15:36:24", updated_at: "2022-06-06 15:36:24">]>

Is there any way to resolve through rails or would I have to create custom methods to differentiate? Any help would be appreciated.


Solution

  • Yes it can be done with Rails, but we need an extra column on ArchiveJoin table to indicate which kind of association it has. (Contact can stay the same so that one can be both Author, Co-author or Publisher without duplications).

    Assume that we call the extra column "archive_join_type", we can do this:

    class Contact < ApplicationRecord
      has_many :author_archive_joins, -> { where(archive_join_type: :author) }, class_name: "ArchiveJoin"
      has_many :coauthor_archive_joins, -> { where(archive_join_type: :coauthor) }, class_name: "ArchiveJoin"
      has_many :publisher_archive_joins, -> { where(archive_join_type: :publisher) }, class_name: "ArchiveJoin"
    
      has_many :author, through: :author_archive_joins, source: :archiveable, source_type: 'Contact'
      has_many :co_author, through: :coauthor_archive_joins, source: :archiveable, source_type: 'Contact'
      has_many :publisher, :through: :publisher_archive_joins, source: :archiveable, source_type: 'Contact'
    end