tommers
tommers

Reputation: 51

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

Upvotes: 1

Views: 1300

Answers (2)

MrYoshiji
MrYoshiji

Reputation: 54902

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, #...

Upvotes: 1

yeuem1vannam
yeuem1vannam

Reputation: 957

There are several ways to fulfill your requirement. Your approach also enough, but for efficiency, since the number of Stadium is small ( thousands ), you can use STI for Stadium entity.

class Stadium < ActiveRecord::
  has_many :stadiums_teams
  has_many :teams, through: :stadiums_teams
end

class OldStadium < Stadium; end

class RecentStadium < Stadium; end

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

class Team < ActiveRecord::Base
  has_many :stadiums_teams
  has_many :stadiums, through: :stadiums_teams, source: :stadium
  has_many :old_stadiums, through: :stadiums_teams,
    source: :stadium, class_name: OldStadium.name
  has_many :recent_stadiums, through: :stadiums_teams,
    source: :stadium, class_name: RecentStadium.name
end

So, in most case, you will work with OldStadium / RecentStadium model, but when you want to check if a team ever played at a specific stadium or not, just use Stadium model.

Upvotes: 1

Related Questions