Prisma Schema 中的多态性 - 最佳实践?

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

这更像是一个设计问题而不是编码问题。假设有以下模式:

// schema.prisma
// Solution 1

model Entity {
  id    Int          @id @default(autoincrement())
  attrs EntityAttr[] 
}

model EntityAttr {
  id       Int         @id @default(autoincrement())
  value    Json        // or String, doesnt matter much here
                       // the point is I need to attach info on the
                       // join table of this relation
  attr     Attr        @relation(fields: [attrId], references: [id])
  entity   Entity      @relation(fields: [entityId], references: [id])

  entityId Int
  attrId   Int

  @@unique([entityId, attrId])
}

model Attr {
  id       Int          @id @default(autoincrement())
  entities EntityAttr[]   
}
// Solution 2
model Entity {
  id          Int          @id @default(autoincrement())
  dateAttrs   DateAttr[]
  recordAttrs RecordAttr[]
  // ... this pattern could continue for more Attr-like models
}

model DateAttr {
  id     Int       @id @default(autoincrement())
  name   String
  entity Entity    @relation(fields: [entityId], references: [id])
  value  DateTime  // Stronger typing in generated code
}

model RecordAttr {
  // ... define another Entity @relation(...)
  name   String
  value  String
  // ...
}

// ... and so on

Please note that the schema might not be 100% complete or accurate. It is mainly to get the point across.

解决方案 1 的优点是数据库中的冗余和表数量显着减少(取决于

Attr
的数量)。它的失败在于令人困惑的查询
*
、可能的特定于案例的类型转换以及每个类似
value
的模型的
Attr
字段没有代码完成。

*
令人困惑的是,我的意思是,当使用自定义联接表时,
prisma
中简化的 m-n 查询选项在功能上被禁用(例如
EntityAttr

解决方案 2 有其优点,其中生成的代码会为

value
字段生成更强类型的代码,但是它会减少生成的表的数量(我实际上不知道更多的表是好事还是坏事)事情,我认为如果你有相似的值,它们应该在同一个表中)。

如果你站在我的立场上会做什么?

graphql nexus prisma prisma-graphql nexus-prisma
4个回答
10
投票

我一直在寻找合适的答案,并在here找到了它。 我不确定它是否可以应用于您的问题,但这是关于

prisma
polymorphism
的问题,所以我认为这个代码片段可能对开发人员有用:

model Photo {
  id Int @id @default(autoincrement())

  likes Like[] @relation("PhotoLike")
}

model Video {
  id Int @id @default(autoincrement())

  likes Like[] @relation("VideoLike")
}

enum LikableType {
  Photo
  Video
}

model Like {
  id Int @id @default(autoincrement())

  Photo Photo? @relation("PhotoLike", fields: [likableId], references: [id], map: "photo_likableId")
  Video Video? @relation("VideoLike", fields: [likableId], references: [id], map: "video_likableId")

  likableId   Int
  likableType LikableType
}

在 dbdocs 中解析关系:


2
投票

有时用例不能概括为抽象并具有类型。

如果您控制它们并且具有有限的属性,请确保您可以将每个属性创建为单独的表,每个属性都有自己的架构。

有时需要更多的自由度或者块是动态的。

用例:构建一个像“notion.so”这样的块文档编辑器,并且您想让用户创建自定义块或配置它们。

你可以这样做:

model Document {
    id     String  @id
    blocks Block[]
}

model Block {
    id           String    @id
    value        Json
    index        Int
    customConfig Json?
    document     Document? @relation(fields: [documentID], references: [id])
    documentID   String?
    blockType    BlockType @relation(fields: [blockTypeID], references: [id])
    blockTypeID  String
}

model BlockType {
    id     String  @id
    name   String
    config Json
    blocks Block[]
}

其中配置和自定义配置可以包含 html、自定义 css 类、链接属性颜色或任何内容。

使用类型脚本,您可以创建 block.types.ts 并为配置添加不同的模板。

我希望我对你有用,总而言之,这取决于要求:>)


0
投票

这不是 Prisma 的限制,每个依赖于关系的外键约束的 ORM 都无法以直接的方式处理“变形”。 实现这种关系的最佳方法是使用泛化并将关系链接到对象的一般形式而不是具体类型:

model Obj{
  id      String  @default(cuid())
  objType ObjType
  user  User?
  publisher  Publisher?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  subscribers   Subscription[]
  @@id([id,objType])
}

enum ObjType{
  Publisher
  User
}
model User {
  id       String @id
  objType  ObjType @default(User)
  obj Obj? @relation(fields: [id,objType],references: [id,objType],map:"UserObj")
  email     String   @unique
  phone     String   @unique
  otp   String
  refreshToken  String
  password  String
  // posts     Post[]
  roles      Role[]
  subscriptions Subscription[] @relation("subscriber")

  @@unique([id,objType])
}

model Publisher {
  id       String @id
  objType  ObjType @default(User)
  obj Obj? @relation(fields: [id,objType],references: [id,objType],map:"PublisherObj")
  name     String
  @@unique([id,objType])
}

//subscription
model Subscription {
  subscriber  User @relation("subscriber",fields: [subscriberId], references: [id])
  subscriberId  String
  subscribed Obj @relation(fields: [subscribedId,subscribedType],references: [id,objType])
  subscribedId   String
  subscribedType ObjType
  duration Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  @@id([subscriberId,subscribedId,subscribedType])
}

0
投票

最近基于 Prisma 构建的 v2 alpha 版 ZenStack 为其提供了天然的支持:

model User {
  id Int @id @default(autoincrement())
  contents Content[]
}

model Content {
  id Int @id @default(autoincrement())
  published Boolean @default(false)
  owner User @relation(fields: [ownerId], references: [id])
  ownerId Int
  contentType String

  @@delegate(contentType)
}

model Post extends Content {
  title String
}

model Video extends Content {
  name String
  duration Int
}

查看更多详细信息: https://zenstack.dev/docs/next/guides/polymorphism

如果您对实现细节感兴趣,请查看以下博客文章: https://zenstack.dev/blog/polymorphism

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