GAミント至上主義

Web Monomaniacal Developer.

TypeORMのEntityでGetterを使うのをやめてAfterLoadでセットするようにした

TypeORMを使っているプロジェクトで、よくやるEntityのfirstNameとlastNameをくっつけてfullnameを返す方法として、getterを使って実装していました。

getterはTypeScriptというかJavaScriptの標準機能なのでまあ良いかと思っていましたが、 TypeScript: Documentation - Classes

↓この仕様があるので

プロパティが参照された時に関数が呼び出されるようにします ゲッター - JavaScript | MDN

EntityをそのままJSONレスポンスとして返すときには付いておらず、いちいちサーバー側で明示的に参照してセットする必要がありました。

擬似コードですがこんな感じ。特にUserが配列のとき、他のEntityに含まれている時などは特に面倒なことになってました。

before

user.entity.ts

@Entity()
export class User {
  @Column({ comment: '姓' })
  lastName: string

  @Column({ comment: '名' })
  firstName: string

  public get fullname(): string {
    return `${this.lastName} ${this.firstName}`
  }
}

コントローラーで使うとき

// fullnameは入ってない
const user = await userRepository.findOne({ id })
return { ...user, fullname: user.fullname }

試してはないけど、おそらくEntityのtoJSONなどを書き換えれば自動でつくようにもできそうだけどやりたくない。

そこでこの記事にもあるAfterLoadを使った形式にしてみたところ、今回の用途では問題なく使えそうでした。

javascript - How I can use getter and setter in TypeORm - Stack Overflow

typeorm/listeners-and-subscribers.md at master · typeorm/typeorm · GitHub

after

user.entity.ts

@Entity()
export class User {
  @Column({ comment: '姓' })
  lastName: string

  @Column({ comment: '名' })
  firstName: string

  fullname = ''

  @AfterLoad
  updateVirtualField {
    this.fullname = `${this.lastName} ${this.firstName}`
  }
}

コントローラーで使うとき

// fullname入ってる!
return await userRepository.findOne({ id })

DB側にカラムはいらないので@Columnはつけずにただのメンバー変数として定義しておきます。 そこにAfterLoadをつけたメソッドで必要な値をセットするだけ。

デメリットとして、名前の通りロード時にセットするだけなので、変更に対してリアルタイム性はないというのがあります。

が、表示以外の処理で使う予定もなさそうなので問題なさそう。

今のプロジェクトでは、姓名をくっつけたフルネームのほか、enum値の表示用ラベルなどをこの形式で追加するようにしています。

今回はSetterはまだ使ってないけど、同じように@BeforeInsertを使ってやれば問題なさそう