简体   繁体   中英

TypeORM : relation with where clause defined in entity

I want to make a SalesOrder entity which have 2 OneToMany relations to the same entity but with a different criteria.

The final goal is to retrieve addresses like that:

const salesOrder = await SalesOrder.findOne(1, {
    relations: ['receiverAddress', 'senderAddress']
});
console.log(salesOrder.receiverAddress)
console.log(salesOrder.senderAddress)

I can't figure out how to filter addresses relations by type in the SalesOrder entity.

I want to do something like that:

// SalesOrder.ts
@Entity()
export class SalesOrder extends BaseEntity {
  @Column()
  @PrimaryGeneratedColumn()
  id: number

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'receiver' // join condition salesOrderAddress.type = 'receiver'
    }
  })
  receiverAddress: SalesOrderAddress

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'sender' // join condition salesOrderAddress.type = 'sender'
    }
  })
  senderAddress: SalesOrderAddress
}

Problem: where clauses are not handled in typeorm decorators.

Is it possible to get something similar (without using query builder)?

I know that I can use 2 OneToOne relations instead, but it implies that the SalesOrder db contains two foreign keys: receiverAddressId and senderAddressId. I prefer to use OneToMany relations since it allows to have just a single foreign key on the SalesOrderAddress (eg salesOrderId).

In your case, I would consider to create custom methods to get the desired information. Check that example:

import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm';

@Entity({ name: 'Address' })
export class Address extends BaseEntity {
  @PrimaryGeneratedColumn({ type: 'int' })
  id: number;

  @Column({ type: 'varchar', length: 20 })
  type: 'receiver' | 'sender';

  @Column({ type: 'nvarchar', length: 1024 })
  value: string;

  // Put the prop that connects this entity to SalesOrder
  @ManyToOne(SalesOrder, ref => ref.addresses)
  salesOrder: SalesOrder;
}

@Entity({ name: 'SalesOrder' })
export class SalesOrder extends BaseEntity {
  @PrimaryGeneratedColumn({ type: 'int' })
  id: number;

  // Put the prop that connects this entity to Address
  @OneToMany(Address, ref => ref.salesOrder)
  addresses: Address[];

  // Create a generic method to filter
  private static getTypeAddresses(id: number, type: 'receiver' | 'sender'): Promise<Address[]> {
    if (typeof id !== 'number') {
      throw new Error('The entity\'s id must be a number');
    }

    return Address
      .createQueryBuilder('Address')
      .select([ 'Address' ])
      .innerJoin(
        'Address.salesOrder',
        'SalesOrder',
        'SalesOrder.id = :id',
        { id: id }
      )
      .where(
        'type = :type',
        { type }
       )
      .getMany();
  }

  // Later, create your methods to obtain easifully your address
  static getReceiverAddresses(id: number): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(id, 'receiver');
  }

  static getSenderAddresses(id: number): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(id, 'sender');
  }

  getReceiverAddresses(): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(this.id, 'receiver');
  }

  getSenderAddresses(): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(this.id, 'sender');
  }
}

Example of usage:

const salesOrder = await SalesOrder.findOne({ id: 1 });
console.log(await salesOrder.getReceiverAddresses());
console.log(await salesOrder.getSenderAddresses());

If you don't want to add methods to your entity's class, consider to create a controller class or another more convenient process. For example:

import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm';

@Entity({ name: 'Address' })
export class Address extends BaseEntity {
  @PrimaryGeneratedColumn({ type: 'int' })
  id: number;

  @Column({ type: 'varchar', length: 20 })
  type: 'receiver' | 'sender';

  @Column({ type: 'nvarchar', length: 1024 })
  value: string;

  // Put the prop that connects this entity to SalesOrder
  @ManyToOne(SalesOrder, ref => ref.addresses)
  salesOrder: SalesOrder;
}

@Entity({ name: 'SalesOrder' })
export class SalesOrder extends BaseEntity {
  @PrimaryGeneratedColumn({ type: 'int' })
  id: number;

  // Put the prop that connects this entity to Address
  @OneToMany(Address, ref => ref.salesOrder)
  addresses: Address[];
}

export class SalesOrderCtrl extends SalesOrder {
  private static getTypeAddresses(id: number, type: 'receiver' | 'sender'): Promise<Address[]> {
    if (typeof id !== 'number') {
      throw new Error('The entity\'s id must be a number');
    }

    return Address
      .createQueryBuilder('Address')
      .select([ 'Address' ])
      .innerJoin(
        'Address.salesOrder',
        'SalesOrder',
        'SalesOrder.id = :id',
        { id: id }
      )
      .where(
        'type = :type',
        { type }
       )
      .getMany();
  }

  static getReceiverAddresses(id: number): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(id, 'receiver');
  }

  static getSenderAddresses(id: number): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(id, 'sender');
  }

  getReceiverAddresses(): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(this.id, 'receiver');
  }

  getSenderAddresses(): Promise<Address[]> {
    return SalesOrder.getTypeAddresses(this.id, 'sender');
  }
}

Example of usage:

const salesOrder = await SalesOrderCtrl.findOne({ id: 1 });
console.log(await salesOrder.getReceiverAddresses());
console.log(await salesOrder.getSenderAddresses());

I want to make a SalesOrder entity which have 2 OneToMany relations to the same entity but with a different criteria.

The final goal is to retrieve addresses like that:

const salesOrder = await SalesOrder.findOne(1, {
    relations: ['receiverAddress', 'senderAddress']
});
console.log(salesOrder.receiverAddress)
console.log(salesOrder.senderAddress)

I can't figure out how to filter addresses relations by type in the SalesOrder entity.

I want to do something like that:

// SalesOrder.ts
@Entity()
export class SalesOrder extends BaseEntity {
  @Column()
  @PrimaryGeneratedColumn()
  id: number

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'receiver' // join condition salesOrderAddress.type = 'receiver'
    }
  })
  receiverAddress: SalesOrderAddress

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'sender' // join condition salesOrderAddress.type = 'sender'
    }
  })
  senderAddress: SalesOrderAddress
}

Problem: where clauses are not handled in typeorm decorators.

Is it possible to get something similar (without using query builder)?

I know that I can use 2 OneToOne relations instead, but it implies that the SalesOrder db contains two foreign keys: receiverAddressId and senderAddressId. I prefer to use OneToMany relations since it allows to have just a single foreign key on the SalesOrderAddress (eg salesOrderId).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM