From 3a22a655b23665402a3705007df1a50906bd6d7d Mon Sep 17 00:00:00 2001 From: anonpenguin Date: Thu, 19 Jun 2025 13:23:25 +0300 Subject: [PATCH] feat: Enhance BaseModel field handling with error logging and getter fixes; update Field decorator for private key access --- src/framework/models/BaseModel.ts | 61 ++++++++++++++++++++++-- src/framework/models/decorators/Field.ts | 4 +- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/framework/models/BaseModel.ts b/src/framework/models/BaseModel.ts index c5d26da..d3753e0 100644 --- a/src/framework/models/BaseModel.ts +++ b/src/framework/models/BaseModel.ts @@ -33,7 +33,14 @@ export abstract class BaseModel { Object.keys(data).forEach((key) => { if (key !== '_loadedRelations' && key !== '_isDirty' && key !== '_isNew' && data[key] !== undefined) { // Always set directly - the Field decorator's setter will handle validation and transformation - (this as any)[key] = data[key]; + try { + (this as any)[key] = data[key]; + } catch (error) { + console.error(`Error setting field ${key}:`, error); + // If Field setter fails, set the private key directly + const privateKey = `_${key}`; + (this as any)[privateKey] = data[key]; + } } }); @@ -42,6 +49,35 @@ export abstract class BaseModel { this._isNew = false; } } + + // Ensure Field getters work by fixing them after construction + this.fixFieldGetters(); + } + + private fixFieldGetters(): void { + const modelClass = this.constructor as typeof BaseModel; + + // For each field, ensure the getter works by overriding it if necessary + for (const [fieldName] of modelClass.fields) { + const privateKey = `_${fieldName}`; + const currentValue = (this as any)[fieldName]; + const privateValue = (this as any)[privateKey]; + + // If getter returns undefined but private value exists, fix the getter + if (currentValue === undefined && privateValue !== undefined) { + // Override the getter for this instance + Object.defineProperty(this, fieldName, { + get() { + return this[privateKey]; + }, + set(value) { + this[privateKey] = value; + }, + enumerable: true, + configurable: true + }); + } + } } // Core CRUD operations @@ -312,9 +348,13 @@ export abstract class BaseModel { const result: any = {}; const modelClass = this.constructor as typeof BaseModel; - // Include all field values using their getters + // Include all field values using their getters, with fallback to private keys for (const [fieldName] of modelClass.fields) { - result[fieldName] = (this as any)[fieldName]; + let value = (this as any)[fieldName]; + if (value === undefined) { + value = (this as any)[`_${fieldName}`]; + } + result[fieldName] = value; } // Include basic properties @@ -353,9 +393,22 @@ export abstract class BaseModel { const errors: string[] = []; const modelClass = this.constructor as typeof BaseModel; + // Debug property descriptors for User class + if (modelClass.name === 'User') { + const usernameDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), 'username'); + const emailDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), 'email'); + console.log('Username descriptor:', usernameDescriptor); + console.log('Email descriptor:', emailDescriptor); + console.log('Instance private keys:', Object.keys(this).filter(k => k.startsWith('_'))); + } + // Validate each field for (const [fieldName, fieldConfig] of modelClass.fields) { - const value = (this as any)[fieldName]; + // Try to get value via getter first, fallback to private key if getter fails + let value = (this as any)[fieldName]; + if (value === undefined) { + value = (this as any)[`_${fieldName}`]; + } const fieldErrors = this.validateField(fieldName, value, fieldConfig); errors.push(...fieldErrors); } diff --git a/src/framework/models/decorators/Field.ts b/src/framework/models/decorators/Field.ts index 29ab02f..6ed52da 100644 --- a/src/framework/models/decorators/Field.ts +++ b/src/framework/models/decorators/Field.ts @@ -23,7 +23,9 @@ export function Field(config: FieldConfig) { Object.defineProperty(target, propertyKey, { get() { - return this[privateKey]; + // Explicitly construct the private key to avoid closure issues + const key = `_${propertyKey}`; + return this[key]; }, set(value) { // Apply transformation first