feat: Enhance BaseModel field handling with error logging and getter fixes; update Field decorator for private key access
This commit is contained in:
parent
383419beec
commit
3a22a655b2
@ -33,7 +33,14 @@ export abstract class BaseModel {
|
|||||||
Object.keys(data).forEach((key) => {
|
Object.keys(data).forEach((key) => {
|
||||||
if (key !== '_loadedRelations' && key !== '_isDirty' && key !== '_isNew' && data[key] !== undefined) {
|
if (key !== '_loadedRelations' && key !== '_isDirty' && key !== '_isNew' && data[key] !== undefined) {
|
||||||
// Always set directly - the Field decorator's setter will handle validation and transformation
|
// Always set directly - the Field decorator's setter will handle validation and transformation
|
||||||
|
try {
|
||||||
(this as any)[key] = data[key];
|
(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;
|
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
|
// Core CRUD operations
|
||||||
@ -312,9 +348,13 @@ export abstract class BaseModel {
|
|||||||
const result: any = {};
|
const result: any = {};
|
||||||
const modelClass = this.constructor as typeof BaseModel;
|
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) {
|
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
|
// Include basic properties
|
||||||
@ -353,9 +393,22 @@ export abstract class BaseModel {
|
|||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const modelClass = this.constructor as typeof BaseModel;
|
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
|
// Validate each field
|
||||||
for (const [fieldName, fieldConfig] of modelClass.fields) {
|
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);
|
const fieldErrors = this.validateField(fieldName, value, fieldConfig);
|
||||||
errors.push(...fieldErrors);
|
errors.push(...fieldErrors);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ export function Field(config: FieldConfig) {
|
|||||||
|
|
||||||
Object.defineProperty(target, propertyKey, {
|
Object.defineProperty(target, propertyKey, {
|
||||||
get() {
|
get() {
|
||||||
return this[privateKey];
|
// Explicitly construct the private key to avoid closure issues
|
||||||
|
const key = `_${propertyKey}`;
|
||||||
|
return this[key];
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
// Apply transformation first
|
// Apply transformation first
|
||||||
|
Reference in New Issue
Block a user