@mikro-orm/core 7.0.0-rc.2 → 7.0.0
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.d.ts +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
package/entity/EntityAssigner.js
CHANGED
|
@@ -71,7 +71,10 @@ export class EntityAssigner {
|
|
|
71
71
|
value = customType.convertToJSValue(value, options.platform);
|
|
72
72
|
}
|
|
73
73
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop?.kind) && value != null) {
|
|
74
|
-
if (options.updateNestedEntities &&
|
|
74
|
+
if (options.updateNestedEntities &&
|
|
75
|
+
Object.hasOwn(entity, propName) &&
|
|
76
|
+
Utils.isEntity(entity[propName], true) &&
|
|
77
|
+
Utils.isPlainObject(value)) {
|
|
75
78
|
const unwrappedEntity = Reference.unwrapReference(entity[propName]);
|
|
76
79
|
const wrapped = helper(unwrappedEntity);
|
|
77
80
|
if (options.updateByPrimaryKey) {
|
|
@@ -95,12 +98,14 @@ export class EntityAssigner {
|
|
|
95
98
|
}
|
|
96
99
|
if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && (prop.setter || !prop.getter)) {
|
|
97
100
|
validateProperty(prop, value, entity);
|
|
98
|
-
return entity[prop.name] = value;
|
|
101
|
+
return (entity[prop.name] = value);
|
|
99
102
|
}
|
|
100
103
|
if (prop.kind === ReferenceKind.EMBEDDED && EntityAssigner.validateEM(options.em)) {
|
|
101
104
|
return EntityAssigner.assignEmbeddable(entity, value, prop, options.em, options);
|
|
102
105
|
}
|
|
103
|
-
if (options.mergeObjectProperties &&
|
|
106
|
+
if (options.mergeObjectProperties &&
|
|
107
|
+
Utils.isPlainObject(entity[propName]) &&
|
|
108
|
+
Utils.isPlainObject(value)) {
|
|
104
109
|
entity[propName] ??= {};
|
|
105
110
|
entity[propName] = Utils.merge({}, entity[propName], value);
|
|
106
111
|
}
|
|
@@ -141,7 +146,9 @@ export class EntityAssigner {
|
|
|
141
146
|
entity[prop.name] = Reference.wrapReference(value, prop);
|
|
142
147
|
}
|
|
143
148
|
else if (Utils.isPrimaryKey(value, true) && EntityAssigner.validateEM(em)) {
|
|
144
|
-
entity[prop.name] = prop.mapToPk
|
|
149
|
+
entity[prop.name] = prop.mapToPk
|
|
150
|
+
? value
|
|
151
|
+
: Reference.wrapReference(em.getReference(prop.targetMeta.class, value, options), prop);
|
|
145
152
|
}
|
|
146
153
|
else if (Utils.isPlainObject(value) && options.merge && EntityAssigner.validateEM(em)) {
|
|
147
154
|
entity[prop.name] = Reference.wrapReference(em.merge(prop.targetMeta.class, value, options), prop);
|
|
@@ -174,7 +181,10 @@ export class EntityAssigner {
|
|
|
174
181
|
return this.createCollectionItem(item, em, prop, invalid, options);
|
|
175
182
|
}
|
|
176
183
|
/* v8 ignore next */
|
|
177
|
-
if (options.updateNestedEntities &&
|
|
184
|
+
if (options.updateNestedEntities &&
|
|
185
|
+
!options.updateByPrimaryKey &&
|
|
186
|
+
collection[idx] &&
|
|
187
|
+
helper(collection[idx])?.isInitialized()) {
|
|
178
188
|
return EntityAssigner.assign(collection[idx], item, options);
|
|
179
189
|
}
|
|
180
190
|
return this.createCollectionItem(item, em, prop, invalid, options);
|
|
@@ -186,7 +196,8 @@ export class EntityAssigner {
|
|
|
186
196
|
if (Array.isArray(value)) {
|
|
187
197
|
collection.set(items);
|
|
188
198
|
}
|
|
189
|
-
else {
|
|
199
|
+
else {
|
|
200
|
+
// append to the collection in case of assigning a single value instead of array
|
|
190
201
|
collection.add(items);
|
|
191
202
|
}
|
|
192
203
|
}
|
|
@@ -207,11 +218,12 @@ export class EntityAssigner {
|
|
|
207
218
|
entity[propName].push(...Object.values(tmp));
|
|
208
219
|
});
|
|
209
220
|
}
|
|
210
|
-
const create = () => EntityAssigner.validateEM(em) &&
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
const create = () => EntityAssigner.validateEM(em) &&
|
|
222
|
+
em.getEntityFactory().createEmbeddable(prop.targetMeta.class, value, {
|
|
223
|
+
convertCustomTypes: options.convertCustomTypes,
|
|
224
|
+
newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
|
|
225
|
+
});
|
|
226
|
+
entity[propName] = (options.mergeEmbeddedProperties ? entity[propName] || create() : create());
|
|
215
227
|
Object.keys(value).forEach(key => {
|
|
216
228
|
EntityAssigner.assignProperty(entity[propName], key, prop.embeddedProps, value, options);
|
|
217
229
|
});
|
|
@@ -23,14 +23,7 @@ export interface FactoryOptions {
|
|
|
23
23
|
key?: string;
|
|
24
24
|
}
|
|
25
25
|
export declare class EntityFactory {
|
|
26
|
-
private
|
|
27
|
-
private readonly driver;
|
|
28
|
-
private readonly platform;
|
|
29
|
-
private readonly config;
|
|
30
|
-
private readonly metadata;
|
|
31
|
-
private readonly hydrator;
|
|
32
|
-
private readonly eventManager;
|
|
33
|
-
private readonly comparator;
|
|
26
|
+
#private;
|
|
34
27
|
constructor(em: EntityManager);
|
|
35
28
|
create<T extends object, P extends string = string>(entityName: EntityName<T>, data: EntityData<T>, options?: FactoryOptions): New<T, P>;
|
|
36
29
|
mergeData<T extends object>(meta: EntityMetadata<T>, entity: T, data: EntityData<T>, options?: FactoryOptions): void;
|
package/entity/EntityFactory.js
CHANGED
|
@@ -6,23 +6,23 @@ import { helper } from './wrap.js';
|
|
|
6
6
|
import { EntityHelper } from './EntityHelper.js';
|
|
7
7
|
import { JsonType } from '../types/JsonType.js';
|
|
8
8
|
export class EntityFactory {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
#driver;
|
|
10
|
+
#platform;
|
|
11
|
+
#config;
|
|
12
|
+
#metadata;
|
|
13
|
+
#hydrator;
|
|
14
|
+
#eventManager;
|
|
15
|
+
#comparator;
|
|
16
|
+
#em;
|
|
17
17
|
constructor(em) {
|
|
18
|
-
this
|
|
19
|
-
this
|
|
20
|
-
this
|
|
21
|
-
this
|
|
22
|
-
this
|
|
23
|
-
this
|
|
24
|
-
this
|
|
25
|
-
this
|
|
18
|
+
this.#em = em;
|
|
19
|
+
this.#driver = this.#em.getDriver();
|
|
20
|
+
this.#platform = this.#driver.getPlatform();
|
|
21
|
+
this.#config = this.#em.config;
|
|
22
|
+
this.#metadata = this.#em.getMetadata();
|
|
23
|
+
this.#hydrator = this.#config.getHydrator(this.#metadata);
|
|
24
|
+
this.#eventManager = this.#em.getEventManager();
|
|
25
|
+
this.#comparator = this.#em.getComparator();
|
|
26
26
|
}
|
|
27
27
|
create(entityName, data, options = {}) {
|
|
28
28
|
data = Reference.unwrapReference(data);
|
|
@@ -30,7 +30,7 @@ export class EntityFactory {
|
|
|
30
30
|
if (data.__entity) {
|
|
31
31
|
return data;
|
|
32
32
|
}
|
|
33
|
-
const meta = this
|
|
33
|
+
const meta = this.#metadata.get(entityName);
|
|
34
34
|
if (meta.virtual) {
|
|
35
35
|
data = { ...data };
|
|
36
36
|
const entity = this.createEntity(data, meta, options);
|
|
@@ -70,11 +70,15 @@ export class EntityFactory {
|
|
|
70
70
|
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
71
71
|
continue;
|
|
72
72
|
}
|
|
73
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
73
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
74
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
74
75
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
75
76
|
}
|
|
76
|
-
if (prop.customType instanceof JsonType && this
|
|
77
|
-
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this
|
|
77
|
+
if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
|
|
78
|
+
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
|
|
79
|
+
key: prop.name,
|
|
80
|
+
mode: 'hydration',
|
|
81
|
+
});
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
}
|
|
@@ -95,11 +99,11 @@ export class EntityFactory {
|
|
|
95
99
|
loaded: options.initialized,
|
|
96
100
|
});
|
|
97
101
|
if (options.recomputeSnapshot) {
|
|
98
|
-
wrapped.__originalEntityData = this
|
|
102
|
+
wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
|
-
if (this
|
|
102
|
-
this
|
|
105
|
+
if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
|
|
106
|
+
this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
|
|
103
107
|
}
|
|
104
108
|
wrapped.__processing = false;
|
|
105
109
|
return entity;
|
|
@@ -107,49 +111,60 @@ export class EntityFactory {
|
|
|
107
111
|
mergeData(meta, entity, data, options = {}) {
|
|
108
112
|
// merge unchanged properties automatically
|
|
109
113
|
data = QueryHelper.processParams(data);
|
|
110
|
-
const existsData = this
|
|
114
|
+
const existsData = this.#comparator.prepareEntity(entity);
|
|
111
115
|
const originalEntityData = helper(entity).__originalEntityData ?? {};
|
|
112
|
-
const diff = this
|
|
116
|
+
const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
|
|
113
117
|
// version properties are not part of entity snapshots
|
|
114
|
-
if (meta.versionProperty &&
|
|
118
|
+
if (meta.versionProperty &&
|
|
119
|
+
data[meta.versionProperty] &&
|
|
120
|
+
data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
|
|
115
121
|
diff[meta.versionProperty] = data[meta.versionProperty];
|
|
116
122
|
}
|
|
117
|
-
const diff2 = this
|
|
123
|
+
const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
|
|
118
124
|
// do not override values changed by user
|
|
119
125
|
Utils.keys(diff).forEach(key => delete diff2[key]);
|
|
120
|
-
Utils.keys(diff2)
|
|
126
|
+
Utils.keys(diff2)
|
|
127
|
+
.filter(key => {
|
|
121
128
|
// ignore null values if there is already present non-null value
|
|
122
129
|
if (existsData[key] != null) {
|
|
123
130
|
return diff2[key] == null;
|
|
124
131
|
}
|
|
125
132
|
return diff2[key] === undefined;
|
|
126
|
-
})
|
|
133
|
+
})
|
|
134
|
+
.forEach(key => delete diff2[key]);
|
|
127
135
|
// but always add collection properties and formulas if they are part of the `data`
|
|
128
136
|
Utils.keys(data)
|
|
129
|
-
.filter(key => meta.properties[key]?.formula ||
|
|
130
|
-
.
|
|
137
|
+
.filter(key => meta.properties[key]?.formula ||
|
|
138
|
+
[ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
|
|
139
|
+
.forEach(key => (diff2[key] = data[key]));
|
|
131
140
|
// rehydrated with the new values, skip those changed by user
|
|
132
141
|
// use full hydration if the entity is already initialized, even if the caller used `initialized: false`
|
|
133
142
|
// (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
|
|
134
143
|
const initialized = options.initialized || helper(entity).__initialized;
|
|
135
144
|
this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
|
|
136
145
|
// we need to update the entity data only with keys that were not present before
|
|
137
|
-
const nullVal = this
|
|
146
|
+
const nullVal = this.#config.get('forceUndefined') ? undefined : null;
|
|
138
147
|
Utils.keys(diff2).forEach(key => {
|
|
139
148
|
const prop = meta.properties[key];
|
|
140
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
149
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
150
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
151
|
+
// oxfmt-ignore
|
|
141
152
|
diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
|
|
142
153
|
}
|
|
143
|
-
if (!options.convertCustomTypes &&
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
if (!options.convertCustomTypes &&
|
|
155
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
|
|
156
|
+
prop.customType?.ensureComparable(meta, prop) &&
|
|
157
|
+
diff2[key] != null) {
|
|
158
|
+
const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
|
|
159
|
+
diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
|
|
146
160
|
}
|
|
147
161
|
originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
|
|
148
162
|
helper(entity).__loadedProperties.add(key);
|
|
149
163
|
});
|
|
150
164
|
// in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
|
|
151
165
|
meta.relations.forEach(prop => {
|
|
152
|
-
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
166
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
167
|
+
Array.isArray(data[prop.name])) {
|
|
153
168
|
// instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
|
|
154
169
|
// we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
|
|
155
170
|
data[prop.name]
|
|
@@ -157,7 +172,10 @@ export class EntityFactory {
|
|
|
157
172
|
.forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
|
|
158
173
|
return;
|
|
159
174
|
}
|
|
160
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
175
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
176
|
+
Utils.isPlainObject(data[prop.name]) &&
|
|
177
|
+
entity[prop.name] &&
|
|
178
|
+
helper(entity[prop.name]).__initialized) {
|
|
161
179
|
this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
162
180
|
}
|
|
163
181
|
});
|
|
@@ -165,8 +183,8 @@ export class EntityFactory {
|
|
|
165
183
|
}
|
|
166
184
|
createReference(entityName, id, options = {}) {
|
|
167
185
|
options.convertCustomTypes ??= true;
|
|
168
|
-
const meta = this
|
|
169
|
-
const schema = this
|
|
186
|
+
const meta = this.#metadata.get(entityName);
|
|
187
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
170
188
|
// Handle alternate key lookup
|
|
171
189
|
if (options.key) {
|
|
172
190
|
const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
|
|
@@ -190,7 +208,7 @@ export class EntityFactory {
|
|
|
190
208
|
if (Array.isArray(id)) {
|
|
191
209
|
id = Utils.getPrimaryKeyCondFromArray(id, meta);
|
|
192
210
|
}
|
|
193
|
-
const pks = Utils.getOrderedPrimaryKeys(id, meta, this
|
|
211
|
+
const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
|
|
194
212
|
const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
|
|
195
213
|
if (exists) {
|
|
196
214
|
return exists;
|
|
@@ -202,15 +220,15 @@ export class EntityFactory {
|
|
|
202
220
|
}
|
|
203
221
|
createEmbeddable(entityName, data, options = {}) {
|
|
204
222
|
data = { ...data };
|
|
205
|
-
const meta = this
|
|
223
|
+
const meta = this.#metadata.get(entityName);
|
|
206
224
|
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
207
225
|
return this.createEntity(data, meta2, options);
|
|
208
226
|
}
|
|
209
227
|
getComparator() {
|
|
210
|
-
return this
|
|
228
|
+
return this.#comparator;
|
|
211
229
|
}
|
|
212
230
|
createEntity(data, meta, options) {
|
|
213
|
-
const schema = this
|
|
231
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
214
232
|
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
215
233
|
if (meta.polymorphs) {
|
|
216
234
|
throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
|
|
@@ -221,7 +239,7 @@ export class EntityFactory {
|
|
|
221
239
|
const entity = new Entity(...params);
|
|
222
240
|
// creating managed entity instance when `forceEntityConstructor` is enabled,
|
|
223
241
|
// we need to wipe all the values as they would cause update queries on next flush
|
|
224
|
-
if (!options.newEntity && (meta.forceConstructor || this
|
|
242
|
+
if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
|
|
225
243
|
meta.props
|
|
226
244
|
.filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
|
|
227
245
|
.forEach(prop => delete entity[prop.name]);
|
|
@@ -241,7 +259,7 @@ export class EntityFactory {
|
|
|
241
259
|
helper(entity).__processing = !meta.embeddable && !meta.virtual;
|
|
242
260
|
helper(entity).__schema = schema;
|
|
243
261
|
if (options.merge && !options.newEntity) {
|
|
244
|
-
this
|
|
262
|
+
this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, options.parentSchema);
|
|
245
263
|
this.unitOfWork.register(entity);
|
|
246
264
|
}
|
|
247
265
|
if (options.initialized) {
|
|
@@ -252,35 +270,35 @@ export class EntityFactory {
|
|
|
252
270
|
assignDefaultValues(entity, meta) {
|
|
253
271
|
for (const prop of meta.props) {
|
|
254
272
|
if (prop.onCreate) {
|
|
255
|
-
entity[prop.name] ??= prop.onCreate(entity, this
|
|
273
|
+
entity[prop.name] ??= prop.onCreate(entity, this.#em);
|
|
256
274
|
}
|
|
257
275
|
}
|
|
258
276
|
}
|
|
259
277
|
hydrate(entity, meta, data, options) {
|
|
260
278
|
if (options.initialized) {
|
|
261
|
-
this
|
|
279
|
+
this.#hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
262
280
|
}
|
|
263
281
|
else {
|
|
264
|
-
this
|
|
282
|
+
this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
265
283
|
}
|
|
266
284
|
Utils.keys(data).forEach(key => {
|
|
267
285
|
helper(entity)?.__loadedProperties.add(key);
|
|
268
286
|
helper(entity)?.__serializationContext.fields?.add(key);
|
|
269
287
|
});
|
|
270
|
-
const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this
|
|
288
|
+
const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
|
|
271
289
|
if (options.newEntity && processOnCreateHooksEarly) {
|
|
272
290
|
this.assignDefaultValues(entity, meta);
|
|
273
291
|
}
|
|
274
292
|
}
|
|
275
293
|
findEntity(data, meta, options) {
|
|
276
|
-
const schema = this
|
|
294
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
277
295
|
if (meta.simplePK) {
|
|
278
296
|
return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
|
|
279
297
|
}
|
|
280
298
|
if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
|
|
281
299
|
return undefined;
|
|
282
300
|
}
|
|
283
|
-
const pks = Utils.getOrderedPrimaryKeys(data, meta, this
|
|
301
|
+
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
|
|
284
302
|
return this.unitOfWork.getById(meta.class, pks, schema);
|
|
285
303
|
}
|
|
286
304
|
processDiscriminatorColumn(meta, data) {
|
|
@@ -289,7 +307,7 @@ export class EntityFactory {
|
|
|
289
307
|
const prop = meta.properties[meta.root.discriminatorColumn];
|
|
290
308
|
const value = data[prop.name];
|
|
291
309
|
const type = meta.root.discriminatorMap[value];
|
|
292
|
-
meta = type ? this
|
|
310
|
+
meta = type ? this.#metadata.get(type) : meta;
|
|
293
311
|
return meta;
|
|
294
312
|
}
|
|
295
313
|
// Handle TPT discriminator (computed at query time)
|
|
@@ -297,7 +315,7 @@ export class EntityFactory {
|
|
|
297
315
|
const value = data[meta.root.tptDiscriminatorColumn];
|
|
298
316
|
if (value) {
|
|
299
317
|
const type = meta.root.discriminatorMap[value];
|
|
300
|
-
meta = type ? this
|
|
318
|
+
meta = type ? this.#metadata.get(type) : meta;
|
|
301
319
|
}
|
|
302
320
|
}
|
|
303
321
|
return meta;
|
|
@@ -312,7 +330,7 @@ export class EntityFactory {
|
|
|
312
330
|
return;
|
|
313
331
|
}
|
|
314
332
|
if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
|
|
315
|
-
data[pk.name] = this
|
|
333
|
+
data[pk.name] = this.#platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
|
|
316
334
|
delete data[spk.name];
|
|
317
335
|
}
|
|
318
336
|
}
|
|
@@ -356,22 +374,24 @@ export class EntityFactory {
|
|
|
356
374
|
if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
|
|
357
375
|
continue;
|
|
358
376
|
}
|
|
359
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
377
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
378
|
+
Utils.isPlainObject(tmp[prop.name]) &&
|
|
379
|
+
!Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
|
|
360
380
|
tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
|
|
361
381
|
}
|
|
362
382
|
else if (prop.kind === ReferenceKind.SCALAR) {
|
|
363
|
-
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this
|
|
383
|
+
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
|
|
364
384
|
}
|
|
365
385
|
}
|
|
366
386
|
return tmp;
|
|
367
387
|
}
|
|
368
388
|
if (options.convertCustomTypes && prop.customType && value != null) {
|
|
369
|
-
return prop.customType.convertToJSValue(value, this
|
|
389
|
+
return prop.customType.convertToJSValue(value, this.#platform);
|
|
370
390
|
}
|
|
371
391
|
return value;
|
|
372
392
|
});
|
|
373
393
|
}
|
|
374
394
|
get unitOfWork() {
|
|
375
|
-
return this
|
|
395
|
+
return this.#em.getUnitOfWork(false);
|
|
376
396
|
}
|
|
377
397
|
}
|
package/entity/EntityHelper.js
CHANGED
|
@@ -31,15 +31,21 @@ export class EntityHelper {
|
|
|
31
31
|
EntityHelper.defineProperties(meta, fork);
|
|
32
32
|
}
|
|
33
33
|
const prototype = meta.prototype;
|
|
34
|
-
if (!prototype.toJSON) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
if (!prototype.toJSON) {
|
|
35
|
+
// toJSON can be overridden
|
|
36
|
+
Object.defineProperty(prototype, 'toJSON', {
|
|
37
|
+
value: function (...args) {
|
|
38
|
+
// Guard against being called on the prototype itself (e.g. by serializers
|
|
39
|
+
// walking the object graph and calling toJSON on prototype objects)
|
|
40
|
+
if (this === prototype) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
return EntityTransformer.toObject(this, ...args);
|
|
44
|
+
},
|
|
45
|
+
writable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
enumerable: false,
|
|
48
|
+
});
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
@@ -49,6 +55,7 @@ export class EntityHelper {
|
|
|
49
55
|
* property on the entity instance, so shadowing the prototype setter.
|
|
50
56
|
*/
|
|
51
57
|
static defineBaseProperties(meta, prototype, em) {
|
|
58
|
+
// oxfmt-ignore
|
|
52
59
|
const helperParams = meta.embeddable || meta.virtual ? [] : [em.getComparator().getPkGetter(meta), em.getComparator().getPkSerializer(meta), em.getComparator().getPkGetterConverted(meta)];
|
|
53
60
|
Object.defineProperties(prototype, {
|
|
54
61
|
__entity: { value: !meta.embeddable, configurable: true },
|
|
@@ -78,10 +85,9 @@ export class EntityHelper {
|
|
|
78
85
|
* than on its prototype. Thanks to this we still have those properties enumerable (e.g. part of `Object.keys(entity)`).
|
|
79
86
|
*/
|
|
80
87
|
static defineProperties(meta, em) {
|
|
81
|
-
Object
|
|
82
|
-
.values(meta.properties)
|
|
83
|
-
.forEach(prop => {
|
|
88
|
+
Object.values(meta.properties).forEach(prop => {
|
|
84
89
|
const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
|
|
90
|
+
// oxfmt-ignore
|
|
85
91
|
const isReference = [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && (prop.inversedBy || prop.mappedBy) && !prop.mapToPk;
|
|
86
92
|
if (isReference) {
|
|
87
93
|
Object.defineProperty(meta.prototype, prop.name, {
|
|
@@ -131,9 +137,7 @@ export class EntityHelper {
|
|
|
131
137
|
}
|
|
132
138
|
// ensure we dont have internal symbols in the POJO
|
|
133
139
|
[OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps, EntityName].forEach(sym => delete object[sym]);
|
|
134
|
-
meta.props
|
|
135
|
-
.filter(prop => object[prop.name] === undefined)
|
|
136
|
-
.forEach(prop => delete object[prop.name]);
|
|
140
|
+
meta.props.filter(prop => object[prop.name] === undefined).forEach(prop => delete object[prop.name]);
|
|
137
141
|
const ret = inspect(object, { depth });
|
|
138
142
|
let name = this.constructor.name;
|
|
139
143
|
const showEM = ['true', 't', '1'].includes(getEnv('MIKRO_ORM_LOG_EM_ID')?.toLowerCase() ?? '');
|
|
@@ -161,6 +165,7 @@ export class EntityHelper {
|
|
|
161
165
|
set(val) {
|
|
162
166
|
const entity = Reference.unwrapReference(val ?? wrapped.__data[prop.name]);
|
|
163
167
|
const old = Reference.unwrapReference(wrapped.__data[prop.name]);
|
|
168
|
+
// oxfmt-ignore
|
|
164
169
|
if (old && old !== entity && prop.kind === ReferenceKind.MANY_TO_ONE && prop.inversedBy && old[prop.inversedBy]) {
|
|
165
170
|
old[prop.inversedBy].removeWithoutPropagation(this);
|
|
166
171
|
}
|
|
@@ -192,6 +197,7 @@ export class EntityHelper {
|
|
|
192
197
|
if ((prop2.inversedBy || prop2.mappedBy) !== prop.name) {
|
|
193
198
|
continue;
|
|
194
199
|
}
|
|
200
|
+
// oxfmt-ignore
|
|
195
201
|
if (prop2.targetMeta.abstract ? prop2.targetMeta.root.class !== meta.root.class : prop2.targetMeta.class !== meta.class) {
|
|
196
202
|
continue;
|
|
197
203
|
}
|
|
@@ -223,7 +229,10 @@ export class EntityHelper {
|
|
|
223
229
|
static propagateOneToOne(entity, owner, prop, prop2, value, old) {
|
|
224
230
|
helper(entity).__pk = helper(entity).getPrimaryKey();
|
|
225
231
|
// the inverse side will be changed on the `value` too, so we need to clean-up and schedule orphan removal there too
|
|
226
|
-
if (!prop.primary &&
|
|
232
|
+
if (!prop.primary &&
|
|
233
|
+
!prop2.mapToPk &&
|
|
234
|
+
value?.[prop2.name] != null &&
|
|
235
|
+
Reference.unwrapReference(value[prop2.name]) !== entity) {
|
|
227
236
|
const other = Reference.unwrapReference(value[prop2.name]);
|
|
228
237
|
delete helper(other).__data[prop.name];
|
|
229
238
|
if (prop2.orphanRemoval) {
|
package/entity/EntityLoader.d.ts
CHANGED
|
@@ -22,9 +22,7 @@ export interface EntityLoaderOptions<Entity, Fields extends string = PopulatePat
|
|
|
22
22
|
logging?: LoggingOptions;
|
|
23
23
|
}
|
|
24
24
|
export declare class EntityLoader {
|
|
25
|
-
private
|
|
26
|
-
private readonly metadata;
|
|
27
|
-
private readonly driver;
|
|
25
|
+
#private;
|
|
28
26
|
constructor(em: EntityManager);
|
|
29
27
|
/**
|
|
30
28
|
* Loads specified relations in batch.
|