-
-
Notifications
You must be signed in to change notification settings - Fork 43
Description
Feature Request: Support for Recursive Discriminated Models in Pinia ORM
Problem Statement
Currently, Pinia ORM supports discriminated models, but only for one level of inheritance. When working with deeper inheritance hierarchies, such as Document > File > Video, the model isn't correctly hydrated all the way to the most specific type.
Use Case
I'm working with a content management system that has a complex document hierarchy:
Document
├── BlockDocument
└── File
├── Video
└── Audio
Example Models
Model Definitions
// Base Document model
class Document extends Model {
static entity = 'documents'
static typeKey = 'documentType'
static fields() {
return {
id: this.uid(),
name: this.string(''),
documentType: this.string('document')
}
}
static types() {
return {
'document': Document,
'file': File,
'block': BlockDocument
}
}
}
class BlockDocument extends Document {
static fields() {
return {
...super.fields(),
blocks: this.attr([])
}
}
}
class File extends Document {
static typeKey = 'fileType'
static fields() {
return {
...super.fields(),
fileType: this.string(''),
size: this.number(0)
}
}
static types() {
return {
'video': Video,
'audio': Audio
}
}
}
class Video extends File {
static fields() {
return {
...super.fields(),
duration: this.number(0),
resolution: this.attr({})
}
}
}
class Audio extends File {
static fields() {
return {
...super.fields(),
duration: this.number(0),
bitrate: this.number(0)
}
}
}Current Behavior
const documentData = {
id: '123',
name: 'My Video',
documentType: 'file',
fileType: 'video',
size: 1024000,
duration: 120,
resolution: { width: 1920, height: 1080 }
}
// This currently only hydrates as a File, not as a Video
const document = useRepo(Document).make(documentData)
console.log(document instanceof Video) // false - should be true
console.log(document instanceof File) // trueDesired Behavior
When I retrieve a document record with both documentType: "document" and fileType: "video", I expect it to be fully hydrated as a Video instance. However, currently it only hydrates to the first level (File), and doesn't continue to the most specific type.
The hydration should recursively check for discriminated types at each level of the inheritance hierarchy, and create an instance of the most specific type applicable.
Solution Proposal
I've implemented a solution by modifying the getHydratedModel function to recursively check for discriminated types. Here's my working implementation:
getHydratedModel(record, options) {
const id = this.model.$entity() + this.model.$getKey(record, true);
const operationId = (options?.operation || "") + id;
// Check cache first
if (options?.action !== "update") {
const savedHydratedModel = this.hydratedDataCache.get(operationId);
if (!this.getNewHydrated && savedHydratedModel) {
return savedHydratedModel;
}
} else {
this.hydratedDataCache.delete("get" + id);
}
// Prepare options only once
const instanceOptions = { relations: false, ...options || {} };
// Recursive re-instantiation
let baseModel = this.model;
let hydratedModel = baseModel.$newInstance(record, instanceOptions);
const typeKey = baseModel.$typeKey();
let discriminatedModel = baseModel.$types()[record[typeKey]];
while (discriminatedModel) {
const subModelName = discriminatedModel.name;
hydratedModel = discriminatedModel.newRawInstance().$newInstance(record, instanceOptions);
baseModel = discriminatedModel;
// Get the next level of discrimination, if any
const nextDiscriminatedType = record[hydratedModel.$typeKey()];
discriminatedModel = nextDiscriminatedType ? hydratedModel.$types()[nextDiscriminatedType] : null;
}
// Cache the result if needed
if (isEmpty(this.eagerLoad) && options?.operation !== "set") {
this.hydratedDataCache.set(operationId, hydratedModel);
}
return hydratedModel;
}This solution properly walks through the inheritance chain until it finds the most specific applicable type.
Benefits
- Proper OOP inheritance model support
- Better type safety with specific model instances
- Access to specific methods and properties without type casting
- More intuitive API that matches the domain model hierarchy
Would appreciate if this could be considered for inclusion in the core library!
Additional information
- Would you be willing to help implement this feature?
Final checks
- Check existing discussions and issues.