【Typeorm】中間テーブルに追加のデータを保存する

Typeormで中間テーブルに追加の列を持つ方法について書いていきます。

今回の例は、ユーザとグループが「 多 対 多 」の関係になります。

##テーブルの作り方

#### ユーザテーブル

```user.model.ts

import { MembershipModel } from './membership.model';
import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn, UpdateDateColumn } from 'typeorm';

@Entity('user')
export class UserModel {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => MembershipModel, (membership) => membership.user, {
    cascade: true,
  })
  membership: MembershipModel[];
}
```

####グループテーブル

```user.model.ts

import { MembershipModel } from './membership.model';
import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn, UpdateDateColumn } from 'typeorm';

@Entity('group')
export class GroupModel {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => MembershipModel, (membership) => membership.group, {
    cascade: true,
  })
  membership: MembershipModel[];
}
```
#### メンバーシップテーブル (ユーザーとグループを繋ぐテーブル)

```membership.model.ts

import { GroupModel } from 'src/models/group.model';
import { UserModel } from 'src/models/user.model';
import { Column, Entity, ManyToOne } from 'typeorm';


@Entity('membership')
export class MembershipModel {

  @Column()
  customProperties: number; // 追加の行です

  @ManyToOne(() => UserModel, (user) => user.membership, { primary: true })
  user: UserModel;

  @ManyToOne(() => GroupModel, (group) => group.membership, { primary: true })
  group: GroupModel;
}

```

これで、「userId,groupId,customPropertie」を持つテーブルが作成できます。


##データの取得

#### 複数のグループを所持したユーザを取得
```ㅤ.ts
const userId = 1 // ユーザのid
const user = await this.userRepository.findOne({
      relations: ['membership', 'membership.group'],
      where: { id: userId },
    });
```

これで、whereに指定したidを持つユーザを取得します。
取得したユーザの情報の中には、グループの情報も含まれます。

こんな感じで取得できます。

```
UserModel {
  id: 1,
  name: 'test',
  membership: [
    MembershipModel { customProperties: 2, group: [GroupModel] },
    MembershipModel { customProperties: 3, group: [GroupModel] },
    MembershipModel { customProperties: 1, group: [GroupModel] },
  ]
}

```

これで一つ目のグループにアクセスできます。

```ㅤ.ts
user.membership[0].group
```

こんな感じで、順番に全部のグループにアクセスできます。

```ㅤ.ts
user.membership.map((membership) => {
  console.log(membership.group.id);
})
```

## データの取得 + ソート + 絞り込み

```ㅤ.ts
const userId = 1 // ユーザのid
const user = await this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.membership', 'membership')
      .leftJoinAndSelect('membership.group', 'group')
      .where('user.id = :id', { userId })
      .andWhere('membership.customProperties != 2')
      .orderBy('membership.customProperties', 'DESC')
      .getOne();
```

上記の様にすることで以下の様なデータが取得できます。

* 指定したidのユーザ
* customPropertiesでソート
* customPropertiesが2以外

## グループの追加

``` .ts

const groupData = {id: 1, name: test} 　//追加したいgroupのデータ


const membership: MembershipModel = {
      customProperties: 1,
      user,
      group: groupData,
    };
user.membership.push(membership); //　配列にグループを追加
this.userRepository.save(user); //ユーザを更新
```

## 中間テーブル、追加行のデータの更新

``` .ts
const groupId = 1 // 更新したいグループのid
user.membership.map((membership, key) => {
      if (membership.group.id == groupId) {
        user.membership[key].customProperties = 2; // 中間テーブルの値を更新
      }
});

this.userRepository.save(user); // ユーザを更新
```

グループ一覧の中から、指定したidのグループとの中間テーブルの値を更新しました。

## 最後に　

公式ドキュメントにもあまり書かれていないので、探すのに少しだけ苦労しました。
誰かの参考になったら嬉しいです。

正しい書き方などがわかったら修正します。
もし、おかしなところがあれば教えて欲しいです。よろしくお願いします。

## おまけ

グループに所属していないユーザの時に以下の様なデータが取得されていました。

```
{ customProperties: null, group: null }
```

なので、その場合は消しておきました。

```　.ts
if (!user.membership[0].group) {
      user.membership.shift();
}
```

もし原因がわかる方がいましたら教えていただけると嬉しいです。🙇‍♂️

##参考記事

https://github.com/typeorm/typeorm/issues/4567

https://typeorm.io/#/