I'm having a hard time getting a list of the games involved from a hierarchical parent relationship when multiple foreign keys are implemented on a relationship in the middle.
Given League Object NFC
, find all of its Game objects [G1,G3,G4]
# id :integer not null, primary key
# name :string
class League
has_many :teams
# has_many :games, :through => :teams (Is there some way to do this?)
end
# id :integer not null, primary key
# team_name :string
# league_id :integer
class Team
belongs_to :league
has_many :home_games, :foreign_key => team_a_id, :source => :game
has_many :away_games, :foreign_key => team_b_id, :source => :game
end
# id :integer not null, primary key
# game_name :string
# team_a_id :integer not null
# team_b_id :integer not null
class Game
belongs_to :home_team, :class_name => Team
belongs_to :away_team, :class_name => Team
end
Data Examples:
LEAGUE - TEAM - GAME
---------------------------------
AFC -
PATRIOTS -
Home Away
G1(PATRIOTS vs DALLAS)
G2(PATRIOTS vs PITTSBURG)
PITTSBURG -
G2(PATRIOTS vs PITTSBURG)
NFC -
DALLAS -
G1(PATRIOTS vs DALLAS)
G3(DALLAS vs GREENBAY)
G4(DALLAS vs SEATTLE)
GREENBAY
G3(DALLAS vs GREENBAY)
SEATTLE
G4(DALLAS vs SEATTLE)
The answer will contain a Rails 4 compliant answer. Special consideration may be awarded to a RAILS 5 answer if the Rails 4 alternative is very inefficient.
nfc = League.where(name: 'NFC').first
# <answer>
puts nfc.games
## array containing objects [G1,G2,G3]
The challenge Im having with is the home_team
/ away_team
and combining data from the foreign keys.
They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user . To determine who "has" the other object, look at where the foreign key is.
Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.
The Relation Class. Having queries return an ActiveRecord::Relation object allows us to chain queries together and this Relation class is at the heart of the new query syntax. Let's take a look at this class by searching through the ActiveRecord source code for a file called relation.
I'm going to give an answer, because the first solution by @meagar requires two SQL queries instead of one (also, isn't that a SQL syntax error if a league has no teams?), and the second solution will contain duplicate Game instances if both teams were from the same league.
In general I try to avoid joins in my reusable scopes, since they force the query into a certain "shape". So I would write something like this:
class Game
# ...
scope :for_league, ->(league_id) {
where(<<-EOQ, league_id)
EXISTS (SELECT 1
FROM teams t
WHERE t.id IN (games.team_a_id, games.team_b_id)
AND t.league_id = ?)
EOQ
}
# ...
end
This SQL technique is called a "correlated sub-query" by the way. I admit it looks strange the first time you see it, but it is a pretty normal thing to do. You can see that the subquery "reaches out" to reference games
. Your database should have no problem optimizing it (given indexes on your foreign keys of course), but conceptually speaking it runs the subquery once per row in the games
table.
A possible solution is to define a games
method on League
that finds all of the games where either foreign key points to one of its teams:
class League
has_many :teams
def games
Game.where('team_a_id in (:ids) or team_b_id in(:ids)', ids: teams.pluck(:id))
end
end
You can accomplish the same thing with a join
:
Game.joins('inner join teams on teams.id = games.team_a_id or teams.id = games.team_b_id').where('teams.league_id = ?', id)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With