NestJS TypeORM 如何拥有类型为条件的 ManyToOne 关系成员?

问题描述 投票:0回答:2

我有一个实体

Message
,它有一个
sender
(
User
) 和一个
receiver
,但
receiver
可以是
User
Channel
(两个实体都有
messagesIn
)成员),我想使用 TypeORM 关系来实现这一点。

是否可以使用 TypeORM (Postgres) / NestJS 做这样的事情(或者是否有任何传统的其他方法来实现相同的目标)?

// message.entity.ts

@Entity()
export class Message {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  content: string;

  @ManyToOne(() => User, (snd) => snd.messagesOut)
  sender: User;

  // That line doesn't compile
  @ManyToOne(() => User | Channel, (rcv) => rcv.messagesIn)
  receiver: User | Channel;
};
typescript postgresql nestjs relationship typeorm
2个回答
3
投票

你想要做的事情是不可能的,因为 TypeORM 需要确切地知道你想要填充什么实体,这样它就可以在正确的位置进行查询。

TypeORM 有一些机制允许您创建一个表,来自用户和通道的所有记录都将插入其中,请参阅此处

但是,如果您想为每个实体有两个专用表(又名每个类型继承表),我建议创建另一个名为

Receiver
的实体,其中包含与消息传递机制相关的公共属性,位于
User
Channel
,加上一个名为
type
的属性,它将定义接收者是
User
还是
Channel
;然后在两个表中分别引用
Receiver
实体。

export enum ReceiverType {
  USER = 'USER',
  CHANNEL = 'CHANNEL',
}

@Entity()
export class Receiver {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  // all common properties that you want related to messaging goes here

  @OneToMany(() => Message, (msg) => msg.receiver)
  messagesIn: Message[];

  @Column({
    type: "enum",
    enum: ReceiverType,
  })
  type: ReceiverType
}
@Entity()
export class User {
  // user properties...

  @OneToOne(() => Receiver)
  @JoinColumn()
  receiver: Receiver
}
@Entity()
export class Channel {
  // channel properties...

  @OneToOne(() => Receiver)
  @JoinColumn()
  receiver: Receiver
}

然后,在

Message
实体中,您将能够引用
Receiver

@Entity()
export class Message {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  content: string;

  @ManyToOne(() => User, (snd) => snd.messagesOut)
  sender: User;

  @ManyToOne(() => Receiver, (rcv) => rcv.messagesIn)
  receiver: Receiver;
};

因此,当您通过存储库获取 Message 实体时,您将收到类似以下内容:

{
  id: string;
  content: string;
  sender: { ... };
  receiver: {
    // common properties you defined...
    messagesIn: [ ... ],
    type: ReceiverType,
  }
}

有了这些信息,您将能够访问您想要的并且是消息发送流程所必需的数据,并且,如果您需要一些仅存在于

User
Channel
实体中的特定信息,您可以从
type
获取
Receiver
并通过接收者 ID 检索相应的实体,例如:

if (message.receiver.type === ReceiverType.USER) {
  const user = await userRepository.findOne({
    where: {
      receiver: { id: message.receiver.id },
    },
  });

  ...
}

if (message.receiver.type === ReceiverType.CHANNEL) {
  const user = await channelRepository.findOne({
    where: {
      receiver: { id: message.receiver.id },
    },
  });

  ...
}

0
投票

不能有一列是多个表中任意一个的外键; FK 必须始终引用单个表中的特定列/列集。您可以通过定义引用每个表的可为空列,然后放置一个要求其中一个为空的约束来处理这种情况。不幸的是,我对您的模糊化管理器(TypeORM)不够熟悉,所以我还将提供原始的 Postgres 表定义。

Create table Message( id          uuid default gen_random_uuid()
                                  primary key
                    , content     text 
                    , sender_user uuid
                                  references users(id) 
                    , rcv_user    uuid 
                                  references users(id) 
                    , rcv_channel uuid 
                                  references channels(id)                   
                    , constraint  only_user_or_channel_ck 
                                  check ( num_nulls(rcv_user,rcv_channel) = 1 )
                    ); 

当然,这会对您的表、它们的键和列类型做出假设。

© www.soinside.com 2019 - 2024. All rights reserved.