GraphQl + Typeorm  @ManyToMany で作成された中間テーブルへのInsert、Deleteをするためのメモ


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 が入る
        }
```