GraphQl / Sequalize在单个列表中组合多个hasMany / BelongsToMany

时间:2019-09-29 09:35:23

标签: graphql sequelize.js

我目前正在研究一个项目,让我的玩家以1v1或2v2的概念与对方玩游戏。

所以我创建了以下2个sequelize类:

player.js

export class Player extends Model {
  static init(sequelize, DataTypes) {
    return super.init(
      {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true
        },

        createdAt: DataTypes.DATE,
        updatedAt: DataTypes.DATE,
        gender: DataTypes.STRING,
        firstName: DataTypes.STRING,
        lastName: DataTypes.STRING
      },
      {
        sequelize
      }
    );
  }

  // Associations
  static associate() {
    this.games1 = this.hasMany(Game, { as: 'player1_team1', foreignKey: 'player1Team1Id' });
    this.games2 = this.hasMany(Game, { as: 'player1_team2', foreignKey: 'player1Team2Id' });
    this.games3 = this.hasMany(Game, { as: 'player2_team1', foreignKey: 'player2Team1Id' });
    this.games4 = this.hasMany(Game, { as: 'player2_team2', foreignKey: 'player2Team2Id' });
  }  
}

game.js

export class Game extends Model {
  static init(sequelize, DataTypes) {
    return super.init(
      {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
          autoIncrement: true
        },
        createdAt: DataTypes.DATE,
        updatedAt: DataTypes.DATE,

        playedAt: DataTypes.DATE,

        set1_team1: {
          type: DataTypes.INTEGER,
          allowNull: true
        },
        set1_team2: {
          type: DataTypes.INTEGER,
          allowNull: true
        },

        set2_team1: {
          type: DataTypes.INTEGER,
          allowNull: true
        },
        set2_team2: {
          type: DataTypes.INTEGER,
          allowNull: true
        },

        set3_team1: {
          type: DataTypes.INTEGER,
          allowNull: true
        },
        set3_team2: {
          type: DataTypes.INTEGER,
          allowNull: true
        }
      },
      {
        sequelize
      }
    );
  }

  // Associations
  static associate() {   
    this.player1_team1 = this.belongsTo(Player, {
      as: 'player1_team1',
      foreignKey: 'player1Team1Id'
    });
    this.player1_team2 = this.belongsTo(Player, {
      as: 'player1_team2',
      foreignKey: 'player1Team2Id'
    });
    this.player2_team1 = this.belongsTo(Player, {
      as: 'player2_team1',
      foreignKey: 'player2Team1Id'
    });
    this.player2_team2 = this.belongsTo(Player, {
      as: 'player2_team2',
      foreignKey: 'player2Team2Id'
    });
  }
}

然后为GraphQl提供以下方案

gql`  
  type Player {
    id: ID!
    firstName: String!
    lastName: String!
    games: [Game]
  }

  type Game {
    id: ID!
    player1_team1: Player!
    player1_team2: Player!
    player2_team1: Player!
    player2_team2: Player!
  }

  type Query {
    player(id: ID!): Player
  }
`

现在,当您查询Player时,无论他是player1_team1,player2_team2 ...,您都可以获取所有游戏。

但是我在确定如何执行此操作时有点受阻:/

我尝试添加getGames()并将4个数组组合到我的类中,但是我没有找到如何调用该方法的方法

getGames() {
  return [...this.games1, ...this.games2, ...this.games3, ...this.games4];
}

我尝试搜索一种方式,该方式可以使用结合了4个子代(player1_team1 ...)的别名进行查询,但是没有成功(在GraphQl中还是新功能)

有人可以帮我吗?

1 个答案:

答案 0 :(得分:1)

Game。调用new Game或类似Game.create的静态方法将生成该类的 instance 。所以我们有

const Game = require('the location of the model')
const game = new Game()

在此示例中,静态方法将在Game变量上可用,因为它们适用于该类,而不是该类的实例。同样,您在类中定义的非静态方法将在实例上可用,而不在类(即game变量)上可用。

hasMany方法在两个模型之间创建关联,并返回HasMany类的实例。通过编写以下内容:

this.games1 = this.hasMany(Game, { ... })

您正在设置games1 静态属性,因为您是在静态方法中进行此操作的。结果,该属性在类而不是实例上可用。将结果关联对象保存为静态属性可能会有所帮助,但也没有必要。您可以轻松地做到这一点:

this.hasMany(Game, { ... })

重要的一点是,通过调用hasMany,您实际上是在该类的实例上创建一个getter。在这种情况下,将根据您提供的别名(getPlayer1_team1参数)将这四个getter分别命名为getPlayer2_team1getPlayer1_team2getPlayer2_team2as。 / p>

因此您可以添加如下方法:

async getGames() {
  const [games1, games2, games3, games4] = await Promise.all([
    this.getPlayer1_team1(),
    this.getPlayer1_team2(),
    this.getPlayer2_team1(),
    this.getPlayer2_team2(),
  ])
  return [...this.games1, ...this.games2, ...this.games3, ...this.games4]
}

然后从某个实例调用它:

const player = await Player.findByPk(1)
const games = await Player.getGames()

如果您的架构在games类型上公开了Player字段,则可以在该字段的解析程序中执行此操作:

function resolve(parent, args, context, info) {
  return parent.getGames()
}

或者...

您可以延迟加载关联的模型并在获取玩家时获取游戏。通常,这比先获取玩家然后再获取游戏更有效。因此,在获取播放器时,您可以执行以下操作:

const player = await Player.findByPk(1, {
  include: [
    { as: 'player1_team1', model: Game },
    { as: 'player1_team2', model: Game },
    { as: 'player2_team1', model: Game },
    { as: 'player2_team2', model: Game },
  ]
})

注意:务必包含定义关联时使用的相同的as值。现在,生成的player变量将为关联的游戏(player1_team1player1_team2等)具有4个属性。

假设您在获取Player实例时像这样延迟加载了相关的游戏,则现在可以执行类似的操作来解决GraphQL中的games字段:

function resolve(parent, args, context, info) {
  return [
    ...parent.player1_team1,
    ...parent.player1_team2,
    ...parent.player2_team1,
    ...parent.player2_team2,
  ]
}