Search code examples
ruby-on-railsmany-to-many

Two associations between same tables with Ruby On Rails


I have an app with Two Models Stadium & Team, they have a many-to-many relationship and are joined in the middle by a join table.

My current structure looks like this:

class Team < ActiveRecord::Base
    has_many :stadiumteams, :class_name => 'StadiumTeam'
    has_many :stadiums, :through => :stadiumteams
end

class Stadium < ActiveRecord::Base
    has_many :stadiumteams, :class_name => 'StadiumTeam'
    has_many :teams, :through => :stadiumteams
end

class StadiumTeam < ActiveRecord::Base
    belongs_to :stadium
    belongs_to :team
end

This relationship show “current residents for a stadium”. But now I also want to have a relationship that can display old residents.

For example Tottenham (Team) used to play at White Hart Lane (Stadium) But now play at (Wembley) Stadium.

I am planning to do the following. But I am unsure if this will work or if there is a better way to do it?

Team

class Team < ActiveRecord::Base
    has_many :stadiumteams, :class_name => 'StadiumTeam'
    has_many :stadiums, :through => :stadiumteams

    #has_many :oldstadiums, :class_name => 'OldStadium'
    #has_many :stadiums, :through => :oldstadiums
end

Stadium

class Stadium < ActiveRecord::Base

    has_many :stadiumteams, :class_name => 'StadiumTeam'
    has_many :teams, :through => :stadiumteams

    #has_many :oldstadiums, :class_name => 'OldStadium'
    #has_many :teams, :through => :oldstadiums
end

New Join Table

class OldStadium < ActiveRecord::Base
    belongs_to :stadium
    belongs_to :team
end

Any help is very appreciated!

EDIT: (Input fields) Here's how the code for my inputs currently looks:

<%= f.association :teams, label: 'Current Residents', class:'select2-field', placeholder: "Select teams", collection: @teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.'%>

<%= f.association :teams, label: 'Old Residents', class:'select2-field', placeholder: "Select teams", collection: @teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.' %>

And my params

   def stadium_params
      params.require(:stadium).permit(:name, :capacity, :city, :country, :location_name, :address, :longitude, :latitude, :image, :surface, :official_opening_date, :cost, :web_url, :also_known_as, :record_attendance, :team_ids => [])
    end

Solution

  • You can define relations with a lambda to append an additional where clause in the relation:

    class Team < ActiveRecord::Base
        has_many :stadiumteams, :class_name => 'StadiumTeam'
    
        has_many stadiums, :through => :stadiumteams
        has_many :current_stadiums, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Stadium', :source => :stadium
        has_many :previous_stadiums, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Stadium', :source => :stadium
    end
    
    class Stadium < ActiveRecord::Base
        has_many :stadiumteams, :class_name => 'StadiumTeam'
    
        has_many :teams, :through => :stadiumteams
        has_many :current_teams, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Team', :source => :team
        has_many :previous_teams, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Team'. :source => :team
    end
    
    class StadiumTeam < ActiveRecord::Base
        belongs_to :stadium
        belongs_to :team
    end
    

    And then you should be able to do (in rails console):

    team = Team.first
    stadium = Stadium.first
    team.previous_stadiums <<
    team.save
    team.reload.previous_stadiums.include?(stadium) 
    # => should return true
    

    So this way you can simply use in your UI a input for the association :previous_teams and :current_teams:

    f.association :current_teams, #...