GraphQl + Typeorm 周りはStackFlowを漁らないとわからないことが多すぎるため、メモを残す。
状況
・備品セットテーブルと、工具マスタテーブルで多対多のリレーションを持つ
・備品セットの中には複数の工具がある
・各エンティティのIDはDB INSERT 時のオートインクリメントで採番されるものとする
エンティティ
EquipmentSet(備品セット)
@ObjectType()
@Entity()
export class EquipmentSet {
@Field()
@PrimaryGeneratedColumn()
id?: number
@Field()
@Column({ readonly: true })
name: string = ""
@Field(() => [Tool])
@ManyToMany(() => Tool, (tool) => tools.equimentSets, {
cascade: true,
})
@JoinTable()
tools?: Tool[]
Tool(工具)エンティティ
@ObjectType()
@Entity()
export class Tool {
@Field()
@PrimaryGeneratedColumn()
id?: number
@Field()
@Column()
name: string = ''
@Field(() => [EquipmentSet], { nullable: true })
@ManyToMany(() => EquipmentSet, (equipmentSet) => equipmentSets.tools)
equipmentSets?: EquipmentSet[]
エンティティを作成したら
migration:generateでマイグレーションファイルを作成。
migrate:runでテーブルを作成。
すると、EquipmentSets_Toolという中間テーブルが作成される。
EquipmentSets_Tool テーブル
-- 列名は元になったテーブル名とカラム名の組み合わせで自動的に決定される
Column1: equipmentSet_id, -- PrimaryKey
Column2: tool_id -- PrimaryKey
コード
SELECTの場合
-- 内部では、equipmentSet テーブルに tool テーブル、中間テーブルのequipmentSet_toolテーブルをLEFT JOIN したSQLが発行されている。
const toolInTheEquipmentSet = await getRepository(EquipmentSets).findOne({
relations: ['tools', 'tools.equipments'], // お互いのリレーションを記載する
where: { id: id },
})
-- Tool 情報を持った EquipmentSets エンティティを取得できる
// console.log(toolInTheEquipmentSet)
// EquipmentSets { id, name, tools{[id:1, name:xxx,],[...]} }
INSERTの場合
getRepository() で指定できない中間テーブルへのINSERTを行いたい時
// let toolsNames:[] = ['ruler','screwdriver'] -- 検索項目が複数の場合
// 中間テーブルの'tool_id'のために取得
const tool = await getRepository(Tool).findOne({
relations: ['equipmentSets'],
where: { name: In(toolsNames)},
})
// -- tool { id:3,id:6 }
// EquipmentSetsテーブルへのINSERTデータ
let eq: EquipmentSets = { name: "備品①", ... }
// EquipmentSetsへの登録
const createdEquipmentSets = await transactionalEntityManager.save(EquipmentSets, eq)
eq = createdEquipmentSets
-- console.log(createdEquipmentSets )
-- createdEquipmentSets { id:1 , name:"備品①"}
// 中間テーブル用に tool エンティティに、createdEquipmentSets のオートインクリメントIDの値をセットしている
for (const t of tool) {
t.equipmentSets?.push(createdEquipmentSets)
}
// save(Tool)のため、Tool テーブルにINSERTしているように見えるが、
// 実際は、EquipmentSet_Tool テーブルへのINSERTをしている
const createdEquipmentSetTool = await transactionalEntityManager.save(
Tool,
tool
)
// -- 発行されるSQL
// INSERT INTO `equipmentSet_tool`(`equipmentSet_id`, `tool_id`)
// VALUES (?, ?), (?, ?) -- PARAMETERS: [1,3, 1,6]
DELETEの場合
中間テーブルから消したいレコードがある場合
ここがよくわかっていない、removeやdeleteを使用し、WHERE INのように複数指定できる方法があれば教えてください
// 中間テーブルの equipment_id を指定しレコードを取得する。。
const findEquipmentSetTool = await getRepository(EquipmentSet).find({
relations: ['tools', 'tools.equipmentSet'],
where: { id: 1 },
})
-- console.log(findEquipmentSetTool)
-- findEquipmentSetTool { tool_id: [ 3, 6 ] }
// findEquipmentSetTool には、tool_id情報が含まれている。
for (const tool of findEquipmentSetTool.tools!) {
const removedEquipmentSetTool = await getRepository(EquipmentSet)
.createQueryBuilder()
.relation(EquipmentSet, 'tools')
.of([{ id: 1 }]) // 中間テーブルのequipmentSet_id を指定している。
.remove([{ id: 3 }]) // 中間テーブルのtool_id を指定している。 2回目は 6 が入る
}