iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 23
0
Modern Web

讀官網文件邊走邊學nest.js系列 第 23

Day23-'TypeORM(十) Embedded Entity及Entity Listener& Subscriber

今日內容參考TypeORM官網Embedded EntityEntity Listener/Subscriber

Embedded Entity

對於每個Entity都會用的欄位/屬性,可以用partial entity的概念,把相關的屬性獨立出來一個class,增加code reuse。

例如,每個Entity都要建立時間跟上次更新時間

則可以新增EntityDate class如下

@Entity()
export class EntityDate {
    @Column({
        nullable: true,
    })
    LastUpdatedDate: Date;

    @Column({
        nullable: true,
    })
    createDate: Date;
}

在User、Role及Department都加入下面這段

@Column(type => EntityDate) // 指定column為EntityDate Entity
entityDate: EntityDate; // 型別為EntityDate

在下面Listener& Subscriber部分測試

Entity Listener

簡單來說就是Entity有讀取、新增、修改、刪除的動作之前或之後可以做一些事情

主要有以下

  • @AfterLoad()
    • 適用QueryBuilder及Repository find
  • @BeforeInsert()/@AfterInsert()
    • 僅適用Repository save方法
  • @BeforeUpdate()/@AfterUpdate()
    • 僅適用Repository save方法,update不行
  • @BeforeRemove()/@AfterRemove()
    • 僅適用Repository remove方法

大致來看,Listener用在用Repository操作比較多,無法QueryBuilder無法觸發Entity Listener。

以Role Entity為例,假設新增或修改Role時,EntityDate屬性要更新。

@Entity()
export class Role {
    ...

    @ManyToMany(type => User, user => user.roles)
    users: User[];

    @Column(type => EntityDate)
    entityDate: EntityDate ;

    @BeforeInsert()
    updateDatesWhenInsert(){
        // 新增entity前指定現在時間給下列欄位
        this.entityDate.createDate = new Date(); 
        this.entityDate.LastUpdatedDate = new Date();
    }

    @BeforeUpdate()
    updateDateWhenUpdate(){
        // 更新entity前更新LastUpdatedDate
        this.entityDate.LastUpdatedDate = new Date();
    }
}

使用Postman測試

新增Role


對應的SQL指令

更新Role


對應的SQL指令

Event Subscriber

Event Listener沒辦法用在User Entity上,因為User Service已經用Query Builder改寫。

如果希望User新增前填入目前時間,新增後加入user權限,根據typeorm官網,可以用Event Subscriber

新增UserSubscriber.ts,注意目錄要放在TypeORModule.forRoot()指定的位置

imports: [
        TypeOrmModule.forRoot(
        {
           type: "postgres",
           host: "localhost",
           ...
           subscribers: [ //要放在這裡
              "src/shared/subscriber/**/*.ts"
           ],
        },
        ),
    ],

可以用typorm cli

typeorm subscriber:create -n UserSubscriber

@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<User> {
    
    listenTo(){
        return User;
    }
    
    async beforeInsert(event: InsertEvent<User>){
        Logger.log(`-----Before Insert------`);
        Logger.log(event.entity); // 顯示insert之前entity的資訊
        event.entity.entityDate.createDate = new Date();
        event.entity.entityDate.LastUpdatedDate = new Date();
    }

    async afterInsert(event: InsertEvent<User>){
        Logger.log(`-----After Insert------`);
        // 希望在insert user後自動加入user權限
        // 使用Subscriber的好處是可以從event取得entity manager物件
        // 進而可以使用querybuilder做任何SQL操作
        const role = await event.manager.createQueryBuilder(Role, 'r')
                   .where('r.roleName = :name', {name: 'user'})
                   .getOne();
        // 要加入user使用relationquerybuilder
        event.manager.createQueryBuilder(User, 'u')
                     .relation('roles')
                     .of(event.entity)
                     .add(role);
    }
}

不指定roleIds,把userService稍微改寫以後用Postman測試

console畫面可以看到對應的SQL指令

Github source code


上一篇
Day 22-TypeORM(九) RelationQueryBuilder(下) & Refactor
下一篇
Day24 Http Bearer Token保護API End Point-使用Passport
系列文
讀官網文件邊走邊學nest.js31

尚未有邦友留言

立即登入留言