@mikro-orm/core 7.2.6-dev.0 → 7.2.6-dev.10
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 +3 -1
- package/drivers/DatabaseDriver.d.ts +2 -0
- package/drivers/DatabaseDriver.js +16 -3
- package/entity/EntityAssigner.js +15 -3
- package/entity/validators.js +4 -1
- package/hydration/ObjectHydrator.js +4 -0
- package/metadata/MetadataDiscovery.js +34 -5
- package/package.json +1 -1
- package/unit-of-work/ChangeSetComputer.js +2 -1
- package/utils/EntityComparator.js +16 -4
- package/utils/Utils.d.ts +2 -0
- package/utils/Utils.js +6 -1
package/EntityManager.js
CHANGED
|
@@ -789,7 +789,9 @@ export class EntityManager {
|
|
|
789
789
|
})) => {
|
|
790
790
|
for (const prop of helper(e).__meta.props) {
|
|
791
791
|
if (prop.kind === ReferenceKind.SCALAR && !prop.ref) {
|
|
792
|
-
|
|
792
|
+
if (helper(e).__initialized || prop.primary) {
|
|
793
|
+
data[prop.name] = e[prop.name];
|
|
794
|
+
}
|
|
793
795
|
continue;
|
|
794
796
|
}
|
|
795
797
|
if (prop.serializedName && prop.serializedName in data) {
|
|
@@ -81,6 +81,8 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
|
|
|
81
81
|
*/
|
|
82
82
|
private mapCursorOffset;
|
|
83
83
|
protected createCursorCondition<T extends object>(definition: (readonly [keyof T & string, QueryOrder])[], offsets: Dictionary[], inverse: boolean, meta: EntityMetadata<T>, fromJson?: boolean): FilterQuery<T>;
|
|
84
|
+
/** @internal Polymorphic embeddable variants can declare same-named properties with different field names. */
|
|
85
|
+
protected variantProps(prop: EntityProperty, data?: Dictionary): Dictionary<EntityProperty>;
|
|
84
86
|
/** @internal */
|
|
85
87
|
mapDataToFieldNames(data: Dictionary, stringifyJsonArrays: boolean, properties?: Record<string, EntityProperty>, convertCustomTypes?: boolean, object?: boolean): Dictionary;
|
|
86
88
|
protected inlineEmbeddables<T extends object>(meta: EntityMetadata<T>, data: T, where?: boolean): void;
|
|
@@ -403,6 +403,19 @@ export class DatabaseDriver {
|
|
|
403
403
|
}
|
|
404
404
|
return { ...atOrPast, ...past };
|
|
405
405
|
}
|
|
406
|
+
/** @internal Polymorphic embeddable variants can declare same-named properties with different field names. */
|
|
407
|
+
variantProps(prop, data) {
|
|
408
|
+
const meta = prop.targetMeta;
|
|
409
|
+
// eslint-disable-next-line eqeqeq
|
|
410
|
+
const variant = meta?.polymorphs?.find(m => m.discriminatorValue == data?.[meta.discriminatorColumn]);
|
|
411
|
+
const props = variant ? { ...prop.embeddedProps } : prop.embeddedProps;
|
|
412
|
+
for (const p of variant ? Object.values(props) : []) {
|
|
413
|
+
if (variant.properties[p.embedded[1]] && Utils.isSameKind(variant.properties[p.embedded[1]], p)) {
|
|
414
|
+
props[p.embedded[1]] = p;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return props;
|
|
418
|
+
}
|
|
406
419
|
/** @internal */
|
|
407
420
|
mapDataToFieldNames(data, stringifyJsonArrays, properties, convertCustomTypes, object) {
|
|
408
421
|
if (!properties || data == null) {
|
|
@@ -417,17 +430,17 @@ export class DatabaseDriver {
|
|
|
417
430
|
if (prop.embeddedProps && !prop.object && !object) {
|
|
418
431
|
const copy = data[k];
|
|
419
432
|
delete data[k];
|
|
420
|
-
Object.assign(data, this.mapDataToFieldNames(copy, stringifyJsonArrays, prop
|
|
433
|
+
Object.assign(data, this.mapDataToFieldNames(copy, stringifyJsonArrays, this.variantProps(prop, copy), convertCustomTypes));
|
|
421
434
|
return;
|
|
422
435
|
}
|
|
423
436
|
if (prop.embeddedProps && (object || prop.object)) {
|
|
424
437
|
const copy = data[k];
|
|
425
438
|
delete data[k];
|
|
426
439
|
if (prop.array) {
|
|
427
|
-
data[prop.fieldNames[0]] = copy?.map((item) => this.mapDataToFieldNames(item, stringifyJsonArrays, prop
|
|
440
|
+
data[prop.fieldNames[0]] = copy?.map((item) => this.mapDataToFieldNames(item, stringifyJsonArrays, this.variantProps(prop, item), convertCustomTypes, true));
|
|
428
441
|
}
|
|
429
442
|
else {
|
|
430
|
-
data[prop.fieldNames[0]] = this.mapDataToFieldNames(copy, stringifyJsonArrays, prop
|
|
443
|
+
data[prop.fieldNames[0]] = this.mapDataToFieldNames(copy, stringifyJsonArrays, this.variantProps(prop, copy), convertCustomTypes, true);
|
|
431
444
|
}
|
|
432
445
|
if (stringifyJsonArrays && prop.array && !object) {
|
|
433
446
|
data[prop.fieldNames[0]] = this.platform.convertJsonToDatabaseValue(data[prop.fieldNames[0]]);
|
package/entity/EntityAssigner.js
CHANGED
|
@@ -234,14 +234,26 @@ export class EntityAssigner {
|
|
|
234
234
|
entity[propName].push(...Object.values(tmp));
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
|
+
const polymorphs = prop.targetMeta?.polymorphs;
|
|
238
|
+
// eslint-disable-next-line eqeqeq
|
|
239
|
+
const variant = polymorphs?.find(m => m.discriminatorValue == value[prop.targetMeta.discriminatorColumn]);
|
|
237
240
|
const create = () => EntityAssigner.validateEM(em) &&
|
|
238
|
-
em.getEntityFactory().createEmbeddable(prop.targetMeta.class, value, {
|
|
241
|
+
em.getEntityFactory().createEmbeddable((variant ?? prop.targetMeta).class, value, {
|
|
239
242
|
convertCustomTypes: options.convertCustomTypes,
|
|
240
243
|
newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
|
|
241
244
|
});
|
|
242
|
-
|
|
245
|
+
// a different polymorphic variant cannot be merged into the current one
|
|
246
|
+
const merge = options.mergeEmbeddedProperties && (!variant || entity[propName]?.constructor === variant.class);
|
|
247
|
+
entity[propName] = (merge ? entity[propName] || create() : create());
|
|
248
|
+
const props = polymorphs ? { ...prop.embeddedProps } : prop.embeddedProps;
|
|
249
|
+
// polymorphic variants can declare same-named properties of different kinds or relation targets
|
|
250
|
+
for (const p of polymorphs ? entity[propName].__meta.props : []) {
|
|
251
|
+
if (props[p.name] && props[p.name].targetMeta?.class !== p.targetMeta?.class) {
|
|
252
|
+
props[p.name] = { ...p, nullable: prop.nullable || p.nullable };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
243
255
|
Object.keys(value).forEach(key => {
|
|
244
|
-
EntityAssigner.assignProperty(entity[propName], key,
|
|
256
|
+
EntityAssigner.assignProperty(entity[propName], key, props, value, options);
|
|
245
257
|
});
|
|
246
258
|
}
|
|
247
259
|
static createCollectionItem(item, em, prop, invalid, options) {
|
package/entity/validators.js
CHANGED
|
@@ -24,7 +24,10 @@ export function validateProperty(prop, givenValue, entity) {
|
|
|
24
24
|
}
|
|
25
25
|
function getValue(o, prop) {
|
|
26
26
|
if (prop.embedded && prop.embedded[0] in o) {
|
|
27
|
-
|
|
27
|
+
const embeddable = o[prop.embedded[0]];
|
|
28
|
+
const variantProp = embeddable?.__meta?.properties[prop.embedded[1]];
|
|
29
|
+
// a same-named property of a different kind in another polymorphic variant
|
|
30
|
+
return variantProp && !Utils.isSameKind(variantProp, prop) ? undefined : embeddable?.[prop.embedded[1]];
|
|
28
31
|
}
|
|
29
32
|
/* v8 ignore next */
|
|
30
33
|
if (prop.ref) {
|
|
@@ -313,6 +313,10 @@ export class ObjectHydrator extends Hydrator {
|
|
|
313
313
|
? dataKey + this.wrap(childProp.embedded[1])
|
|
314
314
|
: this.wrap(childProp.name);
|
|
315
315
|
const prop2 = childMeta.properties[childProp.embedded[1]];
|
|
316
|
+
// a same-named property of a different kind in another variant
|
|
317
|
+
if (prop2 && !Utils.isSameKind(prop2, childProp)) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
316
320
|
const prop3 = {
|
|
317
321
|
...prop2,
|
|
318
322
|
name: childProp.name,
|
|
@@ -463,7 +463,8 @@ export class MetadataDiscovery {
|
|
|
463
463
|
}
|
|
464
464
|
}
|
|
465
465
|
initManyToOneFieldName(prop, name) {
|
|
466
|
-
|
|
466
|
+
// STI property renaming runs before `initRelation` populates `targetMeta`
|
|
467
|
+
const meta2 = prop.targetMeta ?? this.#metadata.getByClassName(prop.type);
|
|
467
468
|
const ret = [];
|
|
468
469
|
// with `targetKey` on a composite PK target, derive the FK field name from that property
|
|
469
470
|
// instead of the PKs (simple PK targets keep the PK based naming for backwards compatibility)
|
|
@@ -1280,16 +1281,37 @@ export class MetadataDiscovery {
|
|
|
1280
1281
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
1281
1282
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
1282
1283
|
const glue = object ? '~' : '_';
|
|
1283
|
-
|
|
1284
|
-
|
|
1284
|
+
const props = Object.values(embeddable.properties);
|
|
1285
|
+
// variants can declare a same-named property of a different kind, the virtual entity lacks it when the variants are shared with another union
|
|
1286
|
+
for (const prop of embeddable.polymorphs?.flatMap(m => Object.values(m.properties)) ?? []) {
|
|
1287
|
+
const same = props.find(p => p.name === prop.name && Utils.isSameKind(p, prop));
|
|
1288
|
+
if (!same && !prop.embedded) {
|
|
1289
|
+
props.push({ ...prop }); // a copy, the variant's metadata is shared with its other owners
|
|
1290
|
+
}
|
|
1291
|
+
else if (same && same.kind !== ReferenceKind.SCALAR && same.type !== prop.type) {
|
|
1292
|
+
// a column shared by relations to different targets cannot have a FK constraint
|
|
1293
|
+
same.createForeignKeyConstraint = false;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
for (const prop of props) {
|
|
1297
|
+
const sameName = props.filter(p => p.name === prop.name);
|
|
1298
|
+
// same-kind variants of a conflicting property share one owner property, as they share the column
|
|
1299
|
+
const first = sameName.find(p => Utils.isSameKind(p, prop));
|
|
1300
|
+
const conflict = first !== sameName[0];
|
|
1301
|
+
const name = (embeddedProp.embeddedPath?.join(glue) ?? embeddedProp.fieldNames[0] + glue) +
|
|
1302
|
+
prop.name +
|
|
1303
|
+
(conflict ? `_${props.indexOf(first)}` : '');
|
|
1285
1304
|
meta.properties[name] = Utils.copy(prop);
|
|
1286
1305
|
meta.properties[name].name = name;
|
|
1306
|
+
// STI column tracking between the variants does not apply to the owner's columns
|
|
1307
|
+
delete meta.properties[name].stiFieldNames;
|
|
1287
1308
|
meta.properties[name].embedded = [embeddedProp.name, prop.name];
|
|
1288
1309
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
1289
|
-
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
1310
|
+
embeddedProp.embeddedProps[conflict ? name : prop.name] = meta.properties[name];
|
|
1290
1311
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
1291
1312
|
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
1292
|
-
|
|
1313
|
+
// each variant fills only its own column of a conflicting property
|
|
1314
|
+
if (embeddedProp.nullable || refInArray || sameName.some(p => !Utils.isSameKind(p, prop))) {
|
|
1293
1315
|
meta.properties[name].nullable = true;
|
|
1294
1316
|
}
|
|
1295
1317
|
// polymorphic relations derive their column names from the discriminator, so prefix it too
|
|
@@ -1462,6 +1484,13 @@ export class MetadataDiscovery {
|
|
|
1462
1484
|
rootProp.stiFieldNameMap[meta.discriminatorValue] = prop.fieldNames[0];
|
|
1463
1485
|
// subtypes that only differ in type share the column
|
|
1464
1486
|
rootProp.stiFieldNames = Utils.unique([...rootProp.stiFieldNames, ...prop.fieldNames]);
|
|
1487
|
+
// a column shared by relations to different targets cannot have a FK constraint
|
|
1488
|
+
for (const p of Object.values(meta.root.properties)) {
|
|
1489
|
+
const shared = p.type !== newProp.type && compareArrays(p.fieldNames ?? [], newProp.fieldNames);
|
|
1490
|
+
if (shared && p.kind !== ReferenceKind.SCALAR && Utils.isSameKind(p, newProp)) {
|
|
1491
|
+
p.createForeignKeyConstraint = newProp.createForeignKeyConstraint = false;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1465
1494
|
newProp.nullable = true;
|
|
1466
1495
|
newProp.name = name;
|
|
1467
1496
|
newProp.hydrate = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.2.6-dev.
|
|
3
|
+
"version": "7.2.6-dev.10",
|
|
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
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -165,7 +165,8 @@ export class ChangeSetComputer {
|
|
|
165
165
|
const targets = Utils.unwrapProperty(changeSet.entity, changeSet.meta, prop);
|
|
166
166
|
targets.forEach(([rawTarget, idx]) => {
|
|
167
167
|
const target = Reference.unwrapReference(rawTarget);
|
|
168
|
-
|
|
168
|
+
// a same-named scalar of another polymorphic embeddable variant is not a reference
|
|
169
|
+
const needsProcessing = Utils.isEntity(target) && (prop.targetKey != null || !target.__helper.hasPrimaryKey());
|
|
169
170
|
if (needsProcessing) {
|
|
170
171
|
// a reference known only by its `targetKey` has no PK, and is never inserted to generate one
|
|
171
172
|
if (!prop.targetKey && !target.__helper.__initialized) {
|
|
@@ -531,11 +531,22 @@ export class EntityComparator {
|
|
|
531
531
|
}
|
|
532
532
|
return true;
|
|
533
533
|
}
|
|
534
|
+
// a same-named property of a different kind in another polymorphic variant applies only to its own variants
|
|
535
|
+
const guard = (childProp, code) => {
|
|
536
|
+
const variants = prop.targetMeta?.polymorphs?.filter(m => m.properties[childProp.embedded[1]]) ?? [];
|
|
537
|
+
const owners = variants.filter(m => Utils.isSameKind(m.properties[childProp.embedded[1]], childProp));
|
|
538
|
+
if (owners.length === variants.length) {
|
|
539
|
+
return code;
|
|
540
|
+
}
|
|
541
|
+
const discriminator = `entity${[...path, prop.targetMeta.discriminatorColumn].map(k => this.wrap(k)).join('')}`;
|
|
542
|
+
const conds = owners.map(m => `${discriminator} == ${JSON.stringify(m.discriminatorValue)}`);
|
|
543
|
+
return `${padding} if (${conds.join(' || ')}) {\n${code}\n${padding} }`;
|
|
544
|
+
};
|
|
545
|
+
const childProps = meta.props.filter(p => p.embedded?.[0] === prop.name &&
|
|
546
|
+
// object for JSON embeddable
|
|
547
|
+
(p.object || p.persist !== false));
|
|
534
548
|
ret +=
|
|
535
|
-
|
|
536
|
-
.filter(p => p.embedded?.[0] === prop.name &&
|
|
537
|
-
// object for JSON embeddable
|
|
538
|
-
(p.object || p.persist !== false))
|
|
549
|
+
childProps
|
|
539
550
|
.map(childProp => {
|
|
540
551
|
const childDataKey = meta.embeddable || prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
541
552
|
const childEntityKey = [...path, childProp.embedded[1]].map(k => this.wrap(k)).join('');
|
|
@@ -558,6 +569,7 @@ export class EntityComparator {
|
|
|
558
569
|
}
|
|
559
570
|
return `${padding} if (${childCond}) ret${childDataKey} = clone(entity${childEntityKey});`;
|
|
560
571
|
})
|
|
572
|
+
.map((code, i) => guard(childProps[i], code))
|
|
561
573
|
.join('\n') + `\n`;
|
|
562
574
|
if (this.shouldSerialize(prop, dataKey)) {
|
|
563
575
|
return `${ret + padding} ret${dataKey} = cloneEmbeddable(ret${dataKey});\n${padding}}`;
|
package/utils/Utils.d.ts
CHANGED
|
@@ -175,6 +175,8 @@ export declare class Utils {
|
|
|
175
175
|
static getORMVersion(): string;
|
|
176
176
|
static createFunction(context: Map<string, any>, code: string, compiledFunctions?: CompiledFunctions, key?: string): any;
|
|
177
177
|
static callCompiledFunction<T extends unknown[], R>(fn: (...args: T) => R, ...args: T): R;
|
|
178
|
+
/** @internal Same-named properties of polymorphic embeddable variants conflict when their kinds differ, to-one kinds share the column. */
|
|
179
|
+
static isSameKind(a: EntityProperty, b: EntityProperty): boolean;
|
|
178
180
|
static unwrapProperty<T>(entity: T, meta: EntityMetadata<T>, prop: EntityProperty<T>, payload?: boolean): [unknown, number[]][];
|
|
179
181
|
static setPayloadProperty<T>(entity: EntityDictionary<T>, meta: EntityMetadata<T>, prop: EntityProperty<T>, value: unknown, idx: number[]): void;
|
|
180
182
|
static tryImport<T extends Dictionary = any>({ module, warning, }: {
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.2.6-dev.
|
|
156
|
+
static #ORM_VERSION = '7.2.6-dev.10';
|
|
157
157
|
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
158
158
|
static getRlsSettingName(filterName, argName) {
|
|
159
159
|
return `mikro.${filterName}.${argName}`;
|
|
@@ -775,6 +775,11 @@ export class Utils {
|
|
|
775
775
|
throw e;
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
|
+
/** @internal Same-named properties of polymorphic embeddable variants conflict when their kinds differ, to-one kinds share the column. */
|
|
779
|
+
static isSameKind(a, b) {
|
|
780
|
+
const toOne = (p) => p.kind === ReferenceKind.MANY_TO_ONE || p.kind === ReferenceKind.ONE_TO_ONE;
|
|
781
|
+
return a.kind === b.kind || (toOne(a) && toOne(b));
|
|
782
|
+
}
|
|
778
783
|
static unwrapProperty(entity, meta, prop, payload = false) {
|
|
779
784
|
let p = prop;
|
|
780
785
|
const path = [];
|