简体   繁体   中英

Rails: How do I map out this has_many, has_many association?

I'm currently working on a game for a student project. I'm spinning in circles engineering the associations. Any guidance from the community would be greatly appreciated.

There are 3 models/classes: Games, Characters and Quotes.

Game:

  • has_many Characters (two historical characters with a bank of quotes, you will have to choose who said the quote)
  • has_many Quotes (through games)

Character:

  • has_many Games (to keep track of games a character has appeared in)
  • has_many Quotes

Quote:

  • belongs_to Character
class Game < ActiveRecord::Base
  has_many :characters
  # Each game will have exactly 2 characters 
  # @game.characters will return the two characters
  has_many :quotes, through: :characters   
end

class Characters < ActiveRecord::Base
  has_many :games
  # @character.games will return all games the character appeared in
  has_many :quotes 
end

class Quote < ActiveRecord::Base
  belongs_to :character
end

These are the migrations I've created:

class CreateGames < ActiveRecord::Migration[5.1]
  def change
    create_table :games do |t|
      t.text :game_state
    end
  end
end

class CreateCharacters < ActiveRecord::Migration[5.1]
  def change
    create_table :characters do |t|
      t.string :name
      t.string :title
    end
  end
end

class CreateQuotes < ActiveRecord::Migration[5.1]
  def change
    create_table :quotes do |t|
      t.string :content
      t.belongs_to :character, index: true
    end
  end
end

Goals:

  1. instantiate a new game with two characters: @game = Game.new(@character1, @character2)
  2. @game.characters should return the two characters.
  3. @character1.games should return all games the character appeared in.
  4. @game.quotes should return all the quotes from the two characters.

My first instinct is that I need a join table for the has_many has_many relationship and to keep track of the games. For example:.

class GamesPlayed < ApplicationRecord
  belongs_to :character1
  belongs_to :character2
  belongs_to :game
end

Thanks in advance if you can offer me any guidance or suggestions.

To model a many-to-many relationship you can use either a has_and_belongs_to_many which would rely on a join table (no model), or a model that represents the relationship between the two models.

I wonder if you don't really have a many-to-many relationship though. Consider this instead:

class Game
  belongs_to :player_one, class_name: 'Player'
  belongs_to :player_two, class_name: 'Player'
  scope :for_player, ->(player) { where(player_one: player).or(where(player_two: player)) }

  def players
    Player.where(id: [player_one_id, player_two_id])
  end
end

class Player
  def games
    Game.for_player(self)
  end
end

# In use:
@game = Game.find(1)
@game.players
@player = Player.find(1)
@player.games
@player.games.where(created_at: 1.week.ago..Date.today)

Note that both game.players and player.games return an ActiveRecord_Relation that you can use in additional scopes, you just don't have a has_many on the Player model.

The way you've written your associations, you haven't really fleshed out the many to many association between the Game & Character models. Basically you'll need a join table for that.

In it's simplest form, it will look something like:

class Game < ActiveRecord::Base
  has_many :game_character_joins
  has_many :characters, through: :game_character_joins
end

class Characters < ActiveRecord::Base
  has_many :game_character_joins
  has_many :games, through: :game_character_joins

end

class GameCharacterJoin < ActiveRecord::Base
  belongs_to :game
  belongs_to :character
end

The migrations for the join table would be:

class CreateGameCharacterJoin < ActiveRecord::Migration[5.1]
  def change
    create_table :game_character_joins do |t|
      t.integer :game_id
      t.integer :character_id
    end
  end
end

That's really the easier part of what I think you're asking.

Then, the association between Quote and Game is kind of odd looking to me. Is this because you need to get all the Quotes for a single Game? There are a lot of ways to model that--is the idea of an association between Quote and Game a matter of convenience? It seems to me that a quote is

  1. requires both a character and a game,
  2. owned by a character
  3. is about a game

How you approach this really becomes a matter of preference. Depending on the importance of the associations, you can have a many to many between character & quote and another between game & quote, or you can just rely on additional data in the quote table to specify which game it's about.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM