@mikro-orm/core 6.5.10-dev.25 → 6.5.10-dev.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.js +1 -0
- package/decorators/Property.d.ts +50 -3
- package/entity/EntityFactory.d.ts +1 -0
- package/entity/EntityFactory.js +6 -3
- package/entity/EntityHelper.js +13 -2
- package/entity/defineEntity.d.ts +1 -2
- package/entity/defineEntity.js +3 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +25 -22
- package/metadata/MetadataDiscovery.d.ts +1 -0
- package/metadata/MetadataDiscovery.js +33 -3
- package/package.json +2 -2
- package/serialization/EntitySerializer.js +1 -1
- package/serialization/EntityTransformer.js +1 -1
- package/typings.d.ts +3 -2
package/EntityManager.js
CHANGED
|
@@ -1227,6 +1227,7 @@ class EntityManager {
|
|
|
1227
1227
|
...options,
|
|
1228
1228
|
newEntity: !options.managed,
|
|
1229
1229
|
merge: options.managed,
|
|
1230
|
+
normalizeAccessors: true,
|
|
1230
1231
|
});
|
|
1231
1232
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1232
1233
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
package/decorators/Property.d.ts
CHANGED
|
@@ -162,7 +162,7 @@ export interface PropertyOptions<Owner> {
|
|
|
162
162
|
* Set true to define the properties as setter. (virtual)
|
|
163
163
|
*
|
|
164
164
|
* @example
|
|
165
|
-
* ```
|
|
165
|
+
* ```ts
|
|
166
166
|
* @Property({ setter: true })
|
|
167
167
|
* set address(value: string) {
|
|
168
168
|
* this._address = value.toLocaleLowerCase();
|
|
@@ -174,7 +174,7 @@ export interface PropertyOptions<Owner> {
|
|
|
174
174
|
* Set true to define the properties as getter. (virtual)
|
|
175
175
|
*
|
|
176
176
|
* @example
|
|
177
|
-
* ```
|
|
177
|
+
* ```ts
|
|
178
178
|
* @Property({ getter: true })
|
|
179
179
|
* get fullName() {
|
|
180
180
|
* return this.firstName + this.lastName;
|
|
@@ -187,7 +187,7 @@ export interface PropertyOptions<Owner> {
|
|
|
187
187
|
* to the method name.
|
|
188
188
|
*
|
|
189
189
|
* @example
|
|
190
|
-
* ```
|
|
190
|
+
* ```ts
|
|
191
191
|
* @Property({ getter: true })
|
|
192
192
|
* getFullName() {
|
|
193
193
|
* return this.firstName + this.lastName;
|
|
@@ -195,6 +195,53 @@ export interface PropertyOptions<Owner> {
|
|
|
195
195
|
* ```
|
|
196
196
|
*/
|
|
197
197
|
getterName?: keyof Owner;
|
|
198
|
+
/**
|
|
199
|
+
* When using a private property backed by a public get/set pair, use the `accessor` option to point to the other side.
|
|
200
|
+
*
|
|
201
|
+
* > The `fieldName` will be inferred based on the accessor name unless specified explicitly.
|
|
202
|
+
*
|
|
203
|
+
* If the `accessor` option points to something, the ORM will use the backing property directly.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* @Entity()
|
|
208
|
+
* export class User {
|
|
209
|
+
* // the ORM will use the backing field directly
|
|
210
|
+
* @Property({ accessor: 'email' })
|
|
211
|
+
* private _email: string;
|
|
212
|
+
*
|
|
213
|
+
* get email() {
|
|
214
|
+
* return this._email;
|
|
215
|
+
* }
|
|
216
|
+
*
|
|
217
|
+
* set email() {
|
|
218
|
+
* return this._email;
|
|
219
|
+
* }
|
|
220
|
+
* }
|
|
221
|
+
*```
|
|
222
|
+
*
|
|
223
|
+
* If you want to the ORM to use your accessor internally too, use `accessor: true` on the get/set property instead.
|
|
224
|
+
* This is handy if you want to use a native private property for the backing field.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* @Entity({ forceConstructor: true })
|
|
229
|
+
* export class User {
|
|
230
|
+
* #email: string;
|
|
231
|
+
*
|
|
232
|
+
* // the ORM will use the accessor internally
|
|
233
|
+
* @Property({ accessor: true })
|
|
234
|
+
* get email() {
|
|
235
|
+
* return this.#email;
|
|
236
|
+
* }
|
|
237
|
+
*
|
|
238
|
+
* set email() {
|
|
239
|
+
* return this.#email;
|
|
240
|
+
* }
|
|
241
|
+
* }
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
accessor?: keyof Owner | boolean;
|
|
198
245
|
/**
|
|
199
246
|
* Set to define serialized primary key for MongoDB. (virtual)
|
|
200
247
|
* Alias for `@SerializedPrimaryKey()` decorator.
|
package/entity/EntityFactory.js
CHANGED
|
@@ -7,6 +7,7 @@ const enums_1 = require("../enums");
|
|
|
7
7
|
const Reference_1 = require("./Reference");
|
|
8
8
|
const wrap_1 = require("./wrap");
|
|
9
9
|
const EntityHelper_1 = require("./EntityHelper");
|
|
10
|
+
const JsonType_1 = require("../types/JsonType");
|
|
10
11
|
class EntityFactory {
|
|
11
12
|
em;
|
|
12
13
|
driver;
|
|
@@ -76,7 +77,9 @@ class EntityFactory {
|
|
|
76
77
|
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
77
78
|
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
if (prop.customType instanceof JsonType_1.JsonType && this.platform.convertsJsonAutomatically()) {
|
|
81
|
+
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
|
|
82
|
+
}
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
}
|
|
@@ -248,10 +251,10 @@ class EntityFactory {
|
|
|
248
251
|
}
|
|
249
252
|
hydrate(entity, meta, data, options) {
|
|
250
253
|
if (options.initialized) {
|
|
251
|
-
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
254
|
+
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
252
255
|
}
|
|
253
256
|
else {
|
|
254
|
-
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
257
|
+
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
255
258
|
}
|
|
256
259
|
Utils_1.Utils.keys(data).forEach(key => {
|
|
257
260
|
(0, wrap_1.helper)(entity)?.__loadedProperties.add(key);
|
package/entity/EntityHelper.js
CHANGED
|
@@ -90,7 +90,7 @@ class EntityHelper {
|
|
|
90
90
|
});
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
|
-
if (prop.inherited || prop.primary || prop.persist === false || prop.trackChanges === false || prop.embedded || isCollection) {
|
|
93
|
+
if (prop.inherited || prop.primary || prop.accessor || prop.persist === false || prop.trackChanges === false || prop.embedded || isCollection) {
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
Object.defineProperty(meta.prototype, prop.name, {
|
|
@@ -116,7 +116,18 @@ class EntityHelper {
|
|
|
116
116
|
static defineCustomInspect(meta) {
|
|
117
117
|
// @ts-ignore
|
|
118
118
|
meta.prototype[node_util_1.inspect.custom] ??= function (depth = 2) {
|
|
119
|
-
const object = {
|
|
119
|
+
const object = {};
|
|
120
|
+
const keys = new Set(Utils_1.Utils.keys(this)); // .sort((a, b) => (meta.propertyOrder.get(a) ?? 0) - (meta.propertyOrder.get(b) ?? 0));
|
|
121
|
+
for (const prop of meta.props) {
|
|
122
|
+
if (keys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
|
|
123
|
+
object[prop.name] = this[prop.name];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const key of keys) {
|
|
127
|
+
if (!meta.properties[key]) {
|
|
128
|
+
object[key] = this[key];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
120
131
|
// ensure we dont have internal symbols in the POJO
|
|
121
132
|
[typings_1.OptionalProps, typings_1.EntityRepositoryType, typings_1.PrimaryKeyProp, typings_1.EagerProps, typings_1.HiddenProps].forEach(sym => delete object[sym]);
|
|
122
133
|
meta.props
|
package/entity/defineEntity.d.ts
CHANGED
|
@@ -111,14 +111,12 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
111
111
|
returning(returning?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
112
112
|
/**
|
|
113
113
|
* Automatically set the property value when entity gets created, executed during flush operation.
|
|
114
|
-
* @param entity
|
|
115
114
|
*/
|
|
116
115
|
onCreate(onCreate: (entity: any, em: EntityManager) => Value): Pick<UniversalPropertyOptionsBuilder<Value, Options & {
|
|
117
116
|
onCreate: (...args: any[]) => any;
|
|
118
117
|
}, IncludeKeys>, IncludeKeys>;
|
|
119
118
|
/**
|
|
120
119
|
* Automatically update the property value every time entity gets updated, executed during flush operation.
|
|
121
|
-
* @param entity
|
|
122
120
|
*/
|
|
123
121
|
onUpdate(onUpdate: (entity: any, em: EntityManager) => Value): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
124
122
|
/**
|
|
@@ -366,6 +364,7 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
366
364
|
foreignKeyName(foreignKeyName: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
367
365
|
/** Remove the entity when it gets disconnected from the relationship (see {@doclink cascading | Cascading}). */
|
|
368
366
|
orphanRemoval(orphanRemoval?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
367
|
+
accessor(accessor?: string | boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
369
368
|
}
|
|
370
369
|
export interface EmptyOptions extends Partial<Record<UniversalPropertyKeys, unknown>> {
|
|
371
370
|
}
|
package/entity/defineEntity.js
CHANGED
|
@@ -100,14 +100,12 @@ class UniversalPropertyOptionsBuilder {
|
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
102
|
* Automatically set the property value when entity gets created, executed during flush operation.
|
|
103
|
-
* @param entity
|
|
104
103
|
*/
|
|
105
104
|
onCreate(onCreate) {
|
|
106
105
|
return this.assignOptions({ onCreate });
|
|
107
106
|
}
|
|
108
107
|
/**
|
|
109
108
|
* Automatically update the property value every time entity gets updated, executed during flush operation.
|
|
110
|
-
* @param entity
|
|
111
109
|
*/
|
|
112
110
|
onUpdate(onUpdate) {
|
|
113
111
|
return this.assignOptions({ onUpdate });
|
|
@@ -455,6 +453,9 @@ class UniversalPropertyOptionsBuilder {
|
|
|
455
453
|
orphanRemoval(orphanRemoval = true) {
|
|
456
454
|
return this.assignOptions({ orphanRemoval });
|
|
457
455
|
}
|
|
456
|
+
accessor(accessor = true) {
|
|
457
|
+
return this.assignOptions({ accessor });
|
|
458
|
+
}
|
|
458
459
|
}
|
|
459
460
|
exports.UniversalPropertyOptionsBuilder = UniversalPropertyOptionsBuilder;
|
|
460
461
|
/** @internal */
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { EntityData, EntityMetadata } from '../typings';
|
|
2
2
|
import { Hydrator } from './Hydrator';
|
|
3
3
|
import type { EntityFactory } from '../entity/EntityFactory';
|
|
4
|
-
type EntityHydrator<T extends object> = (entity: T, data: EntityData<T>, factory: EntityFactory, newEntity: boolean, convertCustomTypes: boolean, schema?: string, parentSchema?: string) => void;
|
|
4
|
+
type EntityHydrator<T extends object> = (entity: T, data: EntityData<T>, factory: EntityFactory, newEntity: boolean, convertCustomTypes: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean) => void;
|
|
5
5
|
export declare class ObjectHydrator extends Hydrator {
|
|
6
6
|
private readonly hydrators;
|
|
7
7
|
private tmpIndex;
|
|
8
8
|
/**
|
|
9
9
|
* @inheritDoc
|
|
10
10
|
*/
|
|
11
|
-
hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
|
|
11
|
+
hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
|
|
12
12
|
/**
|
|
13
13
|
* @inheritDoc
|
|
14
14
|
*/
|
|
15
|
-
hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
|
|
15
|
+
hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
|
|
16
16
|
/**
|
|
17
17
|
* @internal Highly performance-sensitive method.
|
|
18
18
|
*/
|
|
19
|
-
getEntityHydrator<T extends object>(meta: EntityMetadata<T>, type: 'full' | 'reference'): EntityHydrator<T>;
|
|
19
|
+
getEntityHydrator<T extends object>(meta: EntityMetadata<T>, type: 'full' | 'reference', normalizeAccessors?: boolean): EntityHydrator<T>;
|
|
20
20
|
private createCollectionItemMapper;
|
|
21
21
|
private wrap;
|
|
22
22
|
private safeKey;
|
|
@@ -9,37 +9,40 @@ const enums_1 = require("../enums");
|
|
|
9
9
|
const RawQueryFragment_1 = require("../utils/RawQueryFragment");
|
|
10
10
|
class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
11
11
|
hydrators = {
|
|
12
|
-
full: new Map(),
|
|
13
|
-
|
|
12
|
+
'full~true': new Map(),
|
|
13
|
+
'full~false': new Map(),
|
|
14
|
+
'reference~true': new Map(),
|
|
15
|
+
'reference~false': new Map(),
|
|
14
16
|
};
|
|
15
17
|
tmpIndex = 0;
|
|
16
18
|
/**
|
|
17
19
|
* @inheritDoc
|
|
18
20
|
*/
|
|
19
|
-
hydrate(entity, meta, data, factory, type, newEntity = false, convertCustomTypes = false, schema, parentSchema) {
|
|
20
|
-
const hydrate = this.getEntityHydrator(meta, type);
|
|
21
|
+
hydrate(entity, meta, data, factory, type, newEntity = false, convertCustomTypes = false, schema, parentSchema, normalizeAccessors) {
|
|
22
|
+
const hydrate = this.getEntityHydrator(meta, type, normalizeAccessors);
|
|
21
23
|
const running = this.running;
|
|
22
24
|
// the running state is used to consider propagation as hydration, saving the values directly to the entity data,
|
|
23
25
|
// but we don't want that for new entities, their propagation should result in entity updates when flushing
|
|
24
26
|
this.running = !newEntity;
|
|
25
|
-
Utils_1.Utils.callCompiledFunction(hydrate, entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema);
|
|
27
|
+
Utils_1.Utils.callCompiledFunction(hydrate, entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema, normalizeAccessors);
|
|
26
28
|
this.running = running;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* @inheritDoc
|
|
30
32
|
*/
|
|
31
|
-
hydrateReference(entity, meta, data, factory, convertCustomTypes = false, schema, parentSchema) {
|
|
32
|
-
const hydrate = this.getEntityHydrator(meta, 'reference');
|
|
33
|
+
hydrateReference(entity, meta, data, factory, convertCustomTypes = false, schema, parentSchema, normalizeAccessors) {
|
|
34
|
+
const hydrate = this.getEntityHydrator(meta, 'reference', normalizeAccessors);
|
|
33
35
|
const running = this.running;
|
|
34
36
|
this.running = true;
|
|
35
|
-
Utils_1.Utils.callCompiledFunction(hydrate, entity, data, factory, false, convertCustomTypes, schema, parentSchema);
|
|
37
|
+
Utils_1.Utils.callCompiledFunction(hydrate, entity, data, factory, false, convertCustomTypes, schema, parentSchema, normalizeAccessors);
|
|
36
38
|
this.running = running;
|
|
37
39
|
}
|
|
38
40
|
/**
|
|
39
41
|
* @internal Highly performance-sensitive method.
|
|
40
42
|
*/
|
|
41
|
-
getEntityHydrator(meta, type) {
|
|
42
|
-
const
|
|
43
|
+
getEntityHydrator(meta, type, normalizeAccessors = false) {
|
|
44
|
+
const key = `${type}~${normalizeAccessors}`;
|
|
45
|
+
const exists = this.hydrators[key].get(meta.className);
|
|
43
46
|
if (exists) {
|
|
44
47
|
return exists;
|
|
45
48
|
}
|
|
@@ -135,17 +138,17 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
135
138
|
ret.push(` } else if (typeof data${dataKey} !== 'undefined') {`);
|
|
136
139
|
ret.push(` if (isPrimaryKey(data${dataKey}, true)) {`);
|
|
137
140
|
if (prop.ref) {
|
|
138
|
-
ret.push(` entity${entityKey} = Reference.create(factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema }));`);
|
|
141
|
+
ret.push(` entity${entityKey} = Reference.create(factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, normalizeAccessors, schema }));`);
|
|
139
142
|
}
|
|
140
143
|
else {
|
|
141
|
-
ret.push(` entity${entityKey} = factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema });`);
|
|
144
|
+
ret.push(` entity${entityKey} = factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, normalizeAccessors, schema });`);
|
|
142
145
|
}
|
|
143
146
|
ret.push(` } else if (data${dataKey} && typeof data${dataKey} === 'object') {`);
|
|
144
147
|
if (prop.ref) {
|
|
145
|
-
ret.push(` entity${entityKey} = Reference.create(factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema }));`);
|
|
148
|
+
ret.push(` entity${entityKey} = Reference.create(factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, normalizeAccessors, schema }));`);
|
|
146
149
|
}
|
|
147
150
|
else {
|
|
148
|
-
ret.push(` entity${entityKey} = factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema });`);
|
|
151
|
+
ret.push(` entity${entityKey} = factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, normalizeAccessors, schema });`);
|
|
149
152
|
}
|
|
150
153
|
ret.push(` }`);
|
|
151
154
|
ret.push(` }`);
|
|
@@ -254,7 +257,7 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
254
257
|
// weak comparison as we can have numbers that might have been converted to strings due to being object keys
|
|
255
258
|
ret.push(` if (data${childDataKey} == '${childMeta.discriminatorValue}') {`);
|
|
256
259
|
ret.push(` if (entity${entityKey} == null) {`);
|
|
257
|
-
ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
|
|
260
|
+
ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes, normalizeAccessors });`);
|
|
258
261
|
ret.push(` }`);
|
|
259
262
|
meta.props
|
|
260
263
|
.filter(p => p.embedded?.[0] === prop.name)
|
|
@@ -275,7 +278,7 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
275
278
|
}
|
|
276
279
|
else {
|
|
277
280
|
ret.push(` if (entity${entityKey} == null) {`);
|
|
278
|
-
ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
|
|
281
|
+
ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes, normalizeAccessors });`);
|
|
279
282
|
ret.push(` }`);
|
|
280
283
|
meta.props
|
|
281
284
|
.filter(p => p.embedded?.[0] === prop.name)
|
|
@@ -313,7 +316,7 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
313
316
|
};
|
|
314
317
|
const hydrateProperty = (prop, object = prop.object, path = [prop.name], dataKey) => {
|
|
315
318
|
const entityKey = path.map(k => this.wrap(k)).join('');
|
|
316
|
-
dataKey = dataKey ?? (object ? entityKey : this.wrap(prop.name));
|
|
319
|
+
dataKey = dataKey ?? (object ? entityKey : this.wrap(normalizeAccessors ? (prop.accessor ?? prop.name) : prop.name));
|
|
317
320
|
const ret = [];
|
|
318
321
|
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.mapToPk) {
|
|
319
322
|
ret.push(...hydrateToOne(prop, dataKey, entityKey));
|
|
@@ -343,11 +346,11 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
343
346
|
for (const prop of props) {
|
|
344
347
|
lines.push(...hydrateProperty(prop));
|
|
345
348
|
}
|
|
346
|
-
const code = `// compiled hydrator for entity ${meta.className} (${type})\n`
|
|
347
|
-
+ `return function(entity, data, factory, newEntity, convertCustomTypes, schema) {\n`
|
|
349
|
+
const code = `// compiled hydrator for entity ${meta.className} (${type + normalizeAccessors ? ' normalized' : ''})\n`
|
|
350
|
+
+ `return function(entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema, normalizeAccessors) {\n`
|
|
348
351
|
+ `${lines.join('\n')}\n}`;
|
|
349
352
|
const hydrator = Utils_1.Utils.createFunction(context, code);
|
|
350
|
-
this.hydrators[
|
|
353
|
+
this.hydrators[key].set(meta.className, hydrator);
|
|
351
354
|
return hydrator;
|
|
352
355
|
}
|
|
353
356
|
createCollectionItemMapper(prop) {
|
|
@@ -360,9 +363,9 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
360
363
|
lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
|
|
361
364
|
lines.push(` }`);
|
|
362
365
|
}
|
|
363
|
-
lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, merge: true });`);
|
|
366
|
+
lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, normalizeAccessors, merge: true });`);
|
|
364
367
|
lines.push(` if (value && value.__entity) return value;`);
|
|
365
|
-
lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, merge: true });`);
|
|
368
|
+
lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, normalizeAccessors, merge: true });`);
|
|
366
369
|
lines.push(` }`);
|
|
367
370
|
return lines;
|
|
368
371
|
}
|
|
@@ -18,6 +18,7 @@ export declare class MetadataDiscovery {
|
|
|
18
18
|
discover(preferTsNode?: boolean): Promise<MetadataStorage>;
|
|
19
19
|
discoverSync(preferTsNode?: boolean): MetadataStorage;
|
|
20
20
|
private mapDiscoveredEntities;
|
|
21
|
+
private initAccessors;
|
|
21
22
|
processDiscoveredEntities(discovered: EntityMetadata[]): EntityMetadata[];
|
|
22
23
|
private findEntities;
|
|
23
24
|
private discoverMissingTargets;
|
|
@@ -76,11 +76,42 @@ class MetadataDiscovery {
|
|
|
76
76
|
});
|
|
77
77
|
return discovered;
|
|
78
78
|
}
|
|
79
|
+
initAccessors(meta) {
|
|
80
|
+
for (const prop of Object.values(meta.properties)) {
|
|
81
|
+
if (!prop.accessor || meta.properties[prop.accessor]) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
85
|
+
if (desc?.get || desc?.set) {
|
|
86
|
+
this.initFieldName(prop);
|
|
87
|
+
const accessor = prop.name;
|
|
88
|
+
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
89
|
+
if (prop.accessor === true) {
|
|
90
|
+
prop.getter = prop.setter = true;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
prop.getter = prop.setter = false;
|
|
94
|
+
}
|
|
95
|
+
prop.accessor = accessor;
|
|
96
|
+
prop.serializedName ??= accessor;
|
|
97
|
+
Utils_1.Utils.renameKey(meta.properties, accessor, prop.name);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const name = prop.name;
|
|
101
|
+
prop.name = prop.accessor;
|
|
102
|
+
this.initFieldName(prop);
|
|
103
|
+
prop.serializedName ??= prop.accessor;
|
|
104
|
+
prop.name = name;
|
|
105
|
+
prop.trackChanges = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
79
109
|
processDiscoveredEntities(discovered) {
|
|
80
110
|
for (const meta of discovered) {
|
|
81
111
|
let i = 1;
|
|
82
112
|
Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
|
|
83
113
|
Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
|
|
114
|
+
this.initAccessors(meta);
|
|
84
115
|
}
|
|
85
116
|
// ignore base entities (not annotated with @Entity)
|
|
86
117
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
@@ -1026,8 +1057,7 @@ class MetadataDiscovery {
|
|
|
1026
1057
|
return '1';
|
|
1027
1058
|
}
|
|
1028
1059
|
inferDefaultValue(meta, prop) {
|
|
1029
|
-
|
|
1030
|
-
if (!meta.class) {
|
|
1060
|
+
if (!meta.class || !this.config.get('discovery').inferDefaultValues) {
|
|
1031
1061
|
return;
|
|
1032
1062
|
}
|
|
1033
1063
|
try {
|
|
@@ -1036,7 +1066,7 @@ class MetadataDiscovery {
|
|
|
1036
1066
|
const entity1 = new meta.class();
|
|
1037
1067
|
const entity2 = new meta.class();
|
|
1038
1068
|
// we compare the two values by reference, this will discard things like `new Date()` or `Date.now()`
|
|
1039
|
-
if (
|
|
1069
|
+
if (prop.default === undefined && entity1[prop.name] != null && entity1[prop.name] === entity2[prop.name] && entity1[prop.name] !== now) {
|
|
1040
1070
|
prop.default ??= entity1[prop.name];
|
|
1041
1071
|
}
|
|
1042
1072
|
// if the default value is null, infer nullability
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "6.5.10-dev.
|
|
3
|
+
"version": "6.5.10-dev.27",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"esprima": "4.0.1",
|
|
65
65
|
"fs-extra": "11.3.2",
|
|
66
66
|
"globby": "11.1.0",
|
|
67
|
-
"mikro-orm": "6.5.10-dev.
|
|
67
|
+
"mikro-orm": "6.5.10-dev.27",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -19,7 +19,7 @@ function isVisible(meta, propName, options) {
|
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
21
21
|
const visible = prop && !(prop.hidden && !options.includeHidden);
|
|
22
|
-
const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
22
|
+
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
23
23
|
return visible && !prefixed;
|
|
24
24
|
}
|
|
25
25
|
function isPopulated(propName, options) {
|
|
@@ -8,7 +8,7 @@ const SerializationContext_1 = require("./SerializationContext");
|
|
|
8
8
|
function isVisible(meta, propName, ignoreFields = []) {
|
|
9
9
|
const prop = meta.properties[propName];
|
|
10
10
|
const visible = prop && !prop.hidden;
|
|
11
|
-
const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
11
|
+
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
|
|
12
12
|
return visible && !prefixed && !ignoreFields.includes(propName);
|
|
13
13
|
}
|
|
14
14
|
class EntityTransformer {
|
package/typings.d.ts
CHANGED
|
@@ -379,6 +379,7 @@ export interface EntityProperty<Owner = any, Target = any> {
|
|
|
379
379
|
setter?: boolean;
|
|
380
380
|
getter?: boolean;
|
|
381
381
|
getterName?: keyof Owner;
|
|
382
|
+
accessor?: EntityKey<Owner>;
|
|
382
383
|
cascade: Cascade[];
|
|
383
384
|
orphanRemoval?: boolean;
|
|
384
385
|
onCreate?: (entity: Owner, em: EntityManager) => any;
|
|
@@ -819,11 +820,11 @@ export interface IHydrator {
|
|
|
819
820
|
* Hydrates the whole entity. This process handles custom type conversions, creating missing Collection instances,
|
|
820
821
|
* mapping FKs to entity instances, as well as merging those entities.
|
|
821
822
|
*/
|
|
822
|
-
hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
|
|
823
|
+
hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
|
|
823
824
|
/**
|
|
824
825
|
* Hydrates primary keys only
|
|
825
826
|
*/
|
|
826
|
-
hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
|
|
827
|
+
hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
|
|
827
828
|
isRunning(): boolean;
|
|
828
829
|
}
|
|
829
830
|
export interface HydratorConstructor {
|