这更像是一个设计问题而不是编码问题。假设有以下模式:
// 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
字段没有代码完成。
令人困惑的是,我的意思是,当使用自定义联接表时,*
中简化的 m-n 查询选项在功能上被禁用(例如prisma
)EntityAttr
解决方案 2 有其优点,其中生成的代码会为
value
字段生成更强类型的代码,但是它会减少生成的表的数量(我实际上不知道更多的表是好事还是坏事)事情,我认为如果你有相似的值,它们应该在同一个表中)。
如果你站在我的立场上会做什么?
我一直在寻找合适的答案,并在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
}
有时用例不能概括为抽象并具有类型。
如果您控制它们并且具有有限的属性,请确保您可以将每个属性创建为单独的表,每个属性都有自己的架构。
有时需要更多的自由度或者块是动态的。
用例:构建一个像“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 并为配置添加不同的模板。
我希望我对你有用,总而言之,这取决于要求:>)
这不是 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])
}
最近基于 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