简体   繁体   中英

How should I define multiple relationships between the same two models in Rails 5

I have two models:

A Game that keeps track of the game state in a card game.

A Deck that manages how cards are stored, shuffled, dealt, etc.

The Game has many different Decks that it has to keep track of at once. For example, two separate different draw decks and a discard deck.

I'm a little stumped on how I should define these relationships. Ideally, I would have Game.draw_type_a Game.draw_type_b and Game.discard that I could work with, and they would each point to a single Deck . I've experimented with has_one :through relationships and traditional one-to-one relationships with defined foreign_keys and classes, but I haven't been able to get this working.

I'd really like to keep all the decks as a single model, since there is so much overlap in how they behave. How should I define the relationships between the three types of deck in the Game and the Deck model. What should the migrations look like to implement them?

I've done something similar in my Adj game . This is what I would do:

class Game < ApplicationRecord
  has_many :decks
  has_one :discard_deck, -> { where(deck_type: :discard) }, foreign_key: :game_id, class_name: 'Deck'
  has_one :draw_deck, -> { where(deck_type: :draw) }, foreign_key: :game_id, class_name: 'Deck'

  # example attributes:
  #   is_finished:boolean(default: false)

  def start
    # populate the decks
    draw_deck.create!
    discard_deck.create!

    # populate random cards to be a draw_deck
    total_draw_deck_cards = 50

    # pick 50 random cards. `RANDOM()` is for Postgresql; else check online
    random_cards = Card.order('RANDOM()').limit(total_draw_deck_cards)

    random_cards.each do |random_card|
      draw_deck.deck_cards.create!(card: random_card)
    end
  end

  def finish
    update!(is_finished: true)
  end

  # `deck` can be any deck_type. Add conditions here if you must for each different deck_type
  def draw_from(deck)
  # or if you have multiple players:
  # def draw_from(deck, player)
    deck_card = deck.deck_cards.first

    # then do something with this deck_card that you just drawed
    # i.e. you might want to put this into a "Hand" (Player), so you'll need a separate PlayerCard model
    # PlayerCard.create!(card: deck_card.card)

    # or if you have multiple players:
    # PlayerCard.create!(card: deck_card.card, player: player) 

    # then lastly remove this deck_card as you've already put it in your hand
    deck_card.destroy
  end
end

class Deck < ApplicationRecord
  enum deck_type: { draw: 0, discard: 1 }

  belongs_to :game
  has_many :deck_cards, -> { order(created_at: :desc) }
  has_many :cards, through: :deck_cards

  # example attributes:
  #   deck_type:integer
end

class DeckCard < ApplicationRecord
  belongs_to :deck
  belongs_to :card

  # example attributes:
  #   is_faced_down:boolean(default: true)
end

class Card < ApplicationRecord
  enum card_type: { monster: 0, magic: 1, trap: 2 }

  has_many :deck_cards
  has_many :decks, through: :deck_cards

  # example attributes:
  #   name:string
  #   image:string
  #   card_type:integer
end

I would use a three table solution, namely: games, decks, game_decks(?).

Games has many game_decks. GameDecks have a game_id, deck_id, and deck_type (draw_type_a, draw_type_b, etc.), so it belongs to games and belongs to decks Decks has many GameDecks.

Additionally, Games has many Decks through GameDecks, and Decks has many Games through GameDecks.

This allows you to do things like game.decks to retreive all game decks and game.decks.where(deck_type: :draw_type_a) for all of the game's draw_type_a decks (this can be further refined with scopes).

As a side note, I would use a rails enum for the GameDecks.deck_type field.

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