iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
1
Modern Web

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

Day18-TypeORM(五) 設定多對多

  • 分享至 

  • xImage
  •  

以角色為例,一個使用者擁有多個角色,一個角色包含多個使用者

流程跟設定多對一/一對多一樣

  1. 建立Entity Class並設定@ManyToMany
  2. 新增DTO、Service及修改app controller
  3. 新增@ManyToMany() inverse屬性於另一端Entity class,並以@JoinTable()建立Join Table
  4. 修改UserDTO
  5. 在UsersService裡修改新增程式碼
  6. 在UsersSerive裡修改讀取程式碼,載入關聯屬性
  7. 使用Postman測試

建立Role Entity

@Entity()
export class Role {
    
    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        length: 50,
    })
    roleName: string;

    @ManyToMany(type => User, user => user.roles) //設定bi-directional關聯
    users: User[];
}

新增RoleDTO

import { IsString, MaxLength } from "class-validator";

export class RoleDTO {
    @IsString()
    @MaxLength(100)
    roleName: string;
}

新增RoleService,記得註冊到Module

@Injectable()
export class RolesService {
    constructor(
        @InjectRepository(Role) // 注入 typeorm repository
        private readonly roleRepo: Repository<Role>,
    ) {}
    
    async addRole(roleDto: RoleDTO){
        const role = new Role();
        role.roleName = roleDto.roleName;
        return await this.roleRepo.save(role);
    }

    async getRoleById(id){
        return await this.roleRepo.findOne(id);
    }

    async getRolesByIds(ids){ //用在新增使用者時候要回傳Role[]
        return await this.roleRepo.findByIds(ids);
    }

    async getRoles(){
        return await this.roleRepo.find({relations:['users']}); //載入關聯屬性
    }
}

修改appcontroller

  ...
  @Get('role/list')
  getRoles(){
    return this.roleService.getRoles();
  }

  @Get('role/:roleId')
  getRoleById(@Param('roleId') id){
    return this.roleService.getRoleById(id);
  }

  @Post('role')
  @UsePipes(new ValidationPipe({transform:true}))
  addRole(@Body() roleDTO: RoleDTO){
    return this.roleService.addRole(roleDTO);
  }
  ...

修改user.entitie.ts,新增多對多關聯屬性

    ...
    @ManyToOne(type => Department, dep => dep.users)
    dep: Department;

    @RelationId((user: User) => user.dep)
    depId: number;

    @ManyToMany(type => Role, role => role.users) //建立bi-directional多對多
    @JoinTable() // 告訴typeorm要建立join table
    roles: Role[];
    ...

修改userDTO,加入roleIds屬性

    ...
    @IsNumber()
    depId: number;

    @IsNumber({ 
        allowNaN: false,
        allowInfinity:false,
    },{each:true //檢查陣列每一個元素是否都是數字})
    roleIds: number[];
    ...

修改user.service.ts

...
      async addUser(data: UserDTO): Promise<User>{
        const user = new User();
        user.username = data.username;
        user.email = data.email;
        //user.depId  = data.depId; 不能只指定id,必須傳入物件
        user.dep = await this.depService.getDepById(data.depId);
        // 先要取得role物件陣列,再指給user物件下的roles,save時才會儲存關聯
        user.roles = await this.roleService.getRolesByIds(data.roleIds);
        return await this.userRepo.save(user); // 新增一筆user資料
      }
      async getUsers(): Promise<User[]>{
        return await this.userRepo.find({relations: ['dep', 'roles']}); // 載入dep及roles導覽屬性
      }
      async getUserById(id): Promise<User>{
        return await this.userRepo.findOne(id, {relations: ['dep', 'roles']}); // 載入dep及roles導覽屬性
        // return await this.userRepo.findOneOrFail(id); // 以id搜尋,沒找到會丟出例外
      }
      async updateUser(id, data: UserDTO){
        const user = new User();
        user.username = data.username;
        user.email = data.email;
        user.dep = await this.depService.getDepById(data.depId);
        user.roles = await this.roleService.getRolesByIds(data.roleIds);
        return await this.userRepo.update(id, user); // 用data裡的值更新到資料庫
      }
...

使用postman測試
我已預先建立三個Role,先測試新增使用者

列出使用者清單,看是否有儲存關聯資料

列出Role清單,看是否載入關聯資料

Github source code


上一篇
Day17-TypeORM(四)設定一對多/多對一
下一篇
Day19-TypeORM(六) Query Builder Select(上)
系列文
讀官網文件邊走邊學nest.js31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言