Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions docs/orm/polymorphism.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description: Polymorphic models

import ZenStackVsPrisma from '../_components/ZenStackVsPrisma';
import StackBlitzGithub from '@site/src/components/StackBlitzGithub';
import AvailableSince from '../_components/AvailableSince';

# Polymorphic Models

Expand All @@ -26,6 +27,93 @@ The ORM query API hides all the complexity of managing polymorphic models for yo
- When querying a base entity, the ORM fetches the associated concrete entity and merges the results.
- When deleting a base or concrete entity, the ORM automatically deletes the counterpart entity.

## Customizing the discriminator value

<AvailableSince version="v3.7.1" />

By default, when the ORM creates a concrete entity, it stores the concrete model's **name** in the base model's discriminator field. For instance, given the schema below, creating a `Video` writes `"Video"` to `Content.type`, and creating an `Image` writes `"Image"`.

```zmodel
model Content {
id Int @id
type String
@@delegate(type)
}

model Video extends Content { url String }
model Image extends Content { data Bytes }
```

Comment thread
coderabbitai[bot] marked this conversation as resolved.
If the model names don't match the values you want to store, you can override the value with the `@@delegateMap` attribute on each concrete model.

### String discriminator

When the discriminator field is a `String`, pass a string literal to `@@delegateMap`:

```zmodel
model Content {
id Int @id
type String
@@delegate(type)
}

model Video extends Content {
url String
// highlight-next-line
@@delegateMap("video")
}

model Image extends Content {
data Bytes
// highlight-next-line
@@delegateMap("image")
}
```

### Enum discriminator

When the discriminator field is an enum, pass an enum member to `@@delegateMap`. The member must come from the same enum as the discriminator field:

```zmodel
enum AssetKind {
ASSET_KIND_VIDEO
ASSET_KIND_IMAGE
}

model Asset {
id Int @id
type AssetKind
@@delegate(type)
}

model Video extends Asset {
url String
// highlight-next-line
@@delegateMap(ASSET_KIND_VIDEO)
}

model Image extends Asset {
data Bytes
// highlight-next-line
@@delegateMap(ASSET_KIND_IMAGE)
}
```

### TypeScript narrowing

The discriminator value flows into the result type as a string literal (or enum member), so a check on the base field narrows the result to the matching concrete model:

```ts
const content = await client.content.findUniqueOrThrow({ where: { id: 1 } });
if (content.type === 'video') {
// narrowed to Video — `url` is available
console.log(content.url);
} else if (content.type === 'image') {
// narrowed to Image — `data` is available
console.log(content.data);
}
```

## Samples

The schema used in the sample involves a base model and three concrete models:
Expand Down
14 changes: 14 additions & 0 deletions docs/reference/zmodel/attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,20 @@ _Params_:
| ------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| discriminator | A `String` or `enum` field in the same model used to store the name of the concrete model that inherit from this base model. |

### @@delegateMap

```zmodel
attribute @@delegateMap(_ value: Any)
```

Maps a delegate sub-model to a specific discriminator value. Applied to a concrete model that extends a delegate base. If omitted, the sub-model's name is used as the discriminator value by default. See [Customizing the discriminator value](../../orm/polymorphism.md#customizing-the-discriminator-value).

_Params_:

| Name | Description |
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| value | A string literal (when the discriminator is `String`) or an enum member (when the discriminator is an enum, and must belong to the same enum type). |

### @@meta

```zmodel
Expand Down