JangoCG
JangoCG

Reputation: 986

Many To Many Relation only accecepts Unique Values

Orders can have 0 to n Items in them. One Item can belongs to 0 to n Orders. I have the Relationship set up the following way

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

  @ManyToOne(() => Customer, (customer) => customer.orders, {
    eager: true,
  })
  customer: Customer;

  @JoinTable()
  @ManyToMany(() => Item, { eager: true })
  items: Item[];

}

But I can only add Unique Items to my order. When I try to safe a item twice, it gets ignored?

This is the code for adding Items to a order

  async addItemToOrder(orderId: number, itemId: number) {
    const order = await this.findOne(orderId);
    const item = await this.itemService.findOne(itemId);
    if (!order.items) {
      order.items = [];
      order.items = [...order.items, item];
    } else {
      order.items = [...order.items, item];
    }
    order.totalPrice = this.calcTotalPrice(order.items);
    await this.orderRepository.save(order);
    return order;
  }

This is the item

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

  @Column()
  name: string;

  @Column()
  price: number;
}

Upvotes: 0

Views: 594

Answers (1)

Jan Krüger
Jan Krüger

Reputation: 878

It is working as intended. In the underlying join table a itemId gets connected to a orderId. Typeorm only inserts a new entry if it cannot find a combination of itemId and orderId.

For your usecase it would make sense to define a own join table that includes a amount attribute. So your join table looks like this

itemId orderId amount
1 1 2
1 2 1
2 2 5

You can achive this using typeorm like this:

You create a new Entity that is the join entity between an item and an order and includes a attribute amount

@Entity()
export class OrderItem {
  @Column('int')
  amount: number;

  @ManyToOne(() => Item, item => item.orders, { primary: true })
  item: Item;

  @ManyToOne(() => Order, order => order.items, { primary: true })
  order: Order;
}
@Entity()
export class Order {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => Customer, (customer) => customer.orders, {
    eager: true,
  })
  customer: Customer;

  @OneToMany(() => OrderItem, { eager: true })
  items: OrderItem[];
}
@Entity()
export class Item {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  price: number;

  @OneToMany(() => OrderItem, { eager: true })
  orders: OrderItem[];
}

The reason you do this is normalization. Relational databases rely on normalization to prevent inconsistent data. You can read more about normalization here

Upvotes: 2

Related Questions