Rails + GraphQL - неизвестный атрибут «персонажи» для игры - PullRequest
0 голосов
/ 01 мая 2020

Я новичок как в rails, так и в graphql. Я пытаюсь реализовать какой-то простой API, содержащий 3 типа объектов, игр, персонажей и цитаты. Каждая игра может иметь несколько персонажей. Вот типы и мутации, которые я написал. (Проблема связана с играми и персонажами, поэтому я их вставлю.)

GameType:

module Types
  class GameType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false

    field :characters, [Types::CharacterType], null: true do
      #pagination, and cap number of characters retrieved to 20
      argument :first, Integer, default_value: 20, required: false, prepare: ->(limit, ctx) {[limit, 20].min}
      argument :offset, Integer, default_value: 0, required: false
    end

    field :quotes, [Types::QuoteType], null: true do
      #pagination, and cap number of quotes retrieved to 20
      argument :first, Integer, default_value: 20, required: false, prepare: ->(limit, ctx) {[limit, 20].min}
      argument :offset, Integer, default_value: 0, required: false
    end

    field :character_count, Integer, null: true
    field :quote_count, Integer, null: true

    def characters(first:, offset:)
      object.characters.first(first).offset(offset)
    end

    def quotes(first:, offset:)
      object.quotes.first(first).offset(offset)
    end

    def character_count
      object.characters.count
    end

    def quote_count
      object.quotes.count
    end
  end
end

CharacterType:

module Types
  class CharacterType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :game, Types::GameType, null: false

    field :quotes, [Types::QuoteType], null: true do
      #pagination, and cap number of quotes retrieved to 20
      argument :first, Integer, default_value: 20, required: false, prepare: ->(first, ctx) {[first, 20].min}
      argument :offset, Integer, default_value: 0, required: false
    end

    field :quote_count, Integer, null: true

    def quotes(first:, offset:)
      object.quotes.first(first).offset(offset)
    end

    def quote_count
      object.quotes.size
    end
  end
end

Мутация CreateGame:

class Mutations::CreateGame < Mutations::BaseMutation
  argument :name, String, required: true

  field :game, Types::GameType, null: false
  field :errors, [String], null: false

  def resolve(name:)
    game = Game.new(name: name)
    if game.save
      # Successful creation, return the created object with no errors
      {
        game: game,
        errors: [],
      }
    else
      # Failed save, return the errors to the client
      {
        game: nil,
        errors: game.errors.full_messages
      }
    end
  end
end

Мутация CreateCharacter:

class Mutations::CreateCharacter < Mutations::BaseMutation
  argument :name, String, required: true
  argument :gameId, Integer, required: true

  field :character, Types::CharacterType, null: false
  field :errors, [String], null: false

  def resolve(name:, gameId:)
    game = Game.find(gameId)

    if game.nil?
        return {
                    character: nil,
                    errors: game.errors.full_messages,
        }
    end

    character = Character.new(name: name, game: game)
        game = game.update(characters: game.characters + [character]) 
        ^^^^^^ error on this line 
    if character.save
      # Successful creation, return the created object with no errors
      {
        character: character,
        errors: [],
      }
    else
      # Failed save, return the errors to the client
      {
        character: nil,
        errors: character.errors.full_messages + game.errors.full_messages
      }
    end
  end
end

app / models / game.rb:

class Game < ApplicationRecord
end

app / models / character.rb:

class Character < ApplicationRecord
  belongs_to :game
end

db / schema.rb:

ActiveRecord::Schema.define(version: 2020_04_30_071223) do

  create_table "characters", force: :cascade do |t|
    t.integer "game_id", null: false
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["game_id"], name: "index_characters_on_game_id"
  end

  create_table "games", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "quotes", force: :cascade do |t|
    t.integer "game_id", null: false
    t.integer "character_id", null: false
    t.string "text"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["character_id"], name: "index_quotes_on_character_id"
    t.index ["game_id"], name: "index_quotes_on_game_id"
  end

  add_foreign_key "characters", "games"
  add_foreign_key "quotes", "characters"
  add_foreign_key "quotes", "games"
end

CreateGame работает правильно, но CreateCharacter выдает мне эту ошибку (строка помечена в CreateCharacter):

undefined method `characters' for #<Game:0x00007fdf4c207c30>
/var/lib/gems/2.5.0/gems/activemodel-6.0.2.2/lib/active_model/attribute_methods.rb:431:in `method_missing'
/home/krypt/myStuff/projects/gameq/gameq_api/app/graphql/mutations/create_character.rb:19:in `resolve'

Как мне почини это? Спасибо!

1 Ответ

1 голос
/ 01 мая 2020

В вашей модели персонажа у вас есть связь с игрой:

class Character < ApplicationRecord
  belongs_to :game
end

Генерирует метод Character#game и работает, просматривая запись Game с соответствующей game_id. Rails делает это связывание автоматически на основе имени переменной - так что отношение под названием game будет использовать game_id.

Вам не хватает другой половины ассоциации - как получить всех персонажей в игре ? Ну, это довольно просто:

class Game < ApplicationRecord
  has_many :characters
end

Это сгенерирует метод Game#characters, который вам не хватает. Rails magi c снова вступает в игру. Поскольку вы используете имя characters, оно будет искать все записи Character, в которых game_id указывает на игру. Как узнать, что game_id используется в качестве внешнего ключа? Используя название класса - class Game.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...