首页 > 解决方案 > NestJs - DTO 和实体

问题描述

我试图在我的项目中巧妙地使用 DTO 和实体,但它似乎比想象的要复杂。我正在构建一个用于管理库存的后端,我使用 NestJs 和 TypeOrm。

我的客户正在向我发送一组数据并抛出一个 POST 请求,比如说:

{
  "length": 25,
  "quantity": 100,
  "connector_A": {
    "id": "9244e41c-9da7-45b4-a1e4-4498bb9de6de"
  },
  "connector_B": {
    "id": "48426cf0-de41-499b-9c02-94c224392448"
  },
  "category": {
    "id": "f961d67f-aea0-48a3-b298-b2f78be18f1f"
  }
}

我的控制器有责任通过使用自定义 ValidationPipe 检查该字段:

@Post()
  @UsePipes(new ValidationPipe())
  create(@Body() data: CableDto) {
    return this.cablesService.create(data);
}

我在很多地方读到,在最佳实践中,应该将 RAW 数据转换为 DTO,当涉及到数据插入时,我应该将我的 DTO 转换为 typeOrm 实体。

我对这种方法没问题,但我发现它非常复杂,当我的表和前缀名词之间存在关系时更是如此。

这是我的实体电缆

@Entity('t_cable')
export class Cable {

  @PrimaryGeneratedColumn('uuid')
  CAB_Id: string;

  @Column({
    type: "double"
  })
  CAB_Length: number;

  @Column({
    type: "int"
  })
  CAB_Quantity: number;

  @Column()
  CON_Id_A: string

  @Column()
  CON_Id_B: string

  @Column()
  CAT_Id: string

  @ManyToOne(type => Connector, connector => connector.CON_Id_A)
  @JoinColumn({ name: "CON_Id_A" })
  CON_A: Connector;

  @ManyToOne(type => Connector, connector => connector.CON_Id_B)
  @JoinColumn({ name: "CON_Id_B" })
  CON_B: Connector;

  @ManyToOne(type => Category, category => category.CAB_CAT_Id)
  @JoinColumn({ name: "CAT_Id" })
  CAT: Category;

}

这是我用于电缆交互的 DTO:

export class CableDto {

  id: string;

  @IsOptional()
  @IsPositive()
  @Max(1000)
  length: number;
  quantity: number;

  connector_A: ConnectorDto;
  connector_B: ConnectorDto;
  category: CategoryDto

  public static from(dto: Partial<CableDto>) {
    const it = new CableDto();
    it.id = dto.id;
    it.length = dto.length;
    it.quantity = dto.quantity;
    it.connector_A = dto.connector_A
    it.connector_B = dto.connector_B
    it.category = dto.category
    return it;
  }

  public static fromEntity(entity: Cable) {
    return this.from({
      id: entity.CAB_Id,
      length: entity.CAB_Length,
      quantity: entity.CAB_Quantity,
      connector_A: ConnectorDto.fromEntity(entity.CON_A),
      connector_B: ConnectorDto.fromEntity(entity.CON_B),
       category: CategoryDto.fromEntity(entity.CAT)
    });
  }

  public static toEntity(dto: Partial<CableDto>) {
    const it = new Cable();
    if (dto.hasOwnProperty('length')) {
      it.CAB_Length = dto.length;
    }
    if (dto.hasOwnProperty('quantity')) {
      it.CAB_Quantity = dto.quantity;
    }
    if (dto.hasOwnProperty('connector_A')) {
      it.CON_Id_A = dto.connector_A.id;
    }
    if (dto.hasOwnProperty('connector_B')) {
      it.CON_Id_B = dto.connector_B.id;
    }
    if (dto.hasOwnProperty('category')) {
      it.CAT_Id = dto.category.id;
    }
    return it;
  }
}

我知道这三种双向转换 DTO 和实体的方法感觉很脏,这就是我在这里的原因..

我的简单创建或获取请求的服务知道:

async create(dto: CableDto): Promise<CableDto> {
  const cable = await this.cablesRepository.save(CableDto.toEntity(dto));
  return await this.findById(cable.CAB_Id)
}

我相信有更简单的解决方案可以实现这一目标,或者至少是一种正确的方法。

任何想法 ?

谢谢你。

标签: typescriptnestjsdtotypeorm

解决方案


您可以使用 Automapper 来完成 Entity 和 DTO 之间的所有转换。

我在许多项目中使用它并取得了很好的效果,尤其是在具有许多 DTO 和实体的项目中。

例如,您可以执行以下操作:

    export class InitializeMapper {
        mapper: AutoMapper;
    
        constructor() {
            this.mapper = new AutoMapper();
    
            this.mapper.createMap(CableDto, CableEntitt)
                .forMember(dst => dst.length, mapFrom(s => s.length))
                .forMember(dst => dst.quantity, mapFrom(s => s.quantity))
                .forMember(dst => dst.connector_A, mapFrom(s => s.connector_A.id))
                .forMember(dst => dst.connector_B, mapFrom(s => s.connector_B.id))
                .forMember(dst => dst.connector_C, mapFrom(s => s.connector_C.id));
            });
        }
        
        map(a: any, b: any) {
            return this.mapper.map(a, b);
        }
    
        mapArray(a: Array<any>, b: any) {
            return this.mapper.mapArray(a, b);
        }
    }

因此,您可以在项目的任何地方重用相同的映射。

问候


推荐阅读