@mikro-orm/core 7.0.0-dev.224 → 7.0.0-dev.226
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 +8 -2
- package/cache/FileCacheAdapter.js +9 -1
- package/drivers/DatabaseDriver.js +38 -0
- package/entity/EntityHelper.js +13 -1
- package/entity/EntityLoader.d.ts +1 -0
- package/entity/EntityLoader.js +123 -25
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/defineEntity.d.ts +8 -4
- package/entity/defineEntity.js +8 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/errors.d.ts +3 -2
- package/errors.js +9 -4
- package/hydration/ObjectHydrator.js +40 -8
- package/metadata/EntitySchema.d.ts +1 -1
- package/metadata/MetadataDiscovery.d.ts +22 -0
- package/metadata/MetadataDiscovery.js +222 -11
- package/metadata/MetadataValidator.d.ts +6 -0
- package/metadata/MetadataValidator.js +76 -3
- package/metadata/types.d.ts +21 -5
- package/naming-strategy/AbstractNamingStrategy.d.ts +4 -0
- package/naming-strategy/AbstractNamingStrategy.js +6 -0
- package/naming-strategy/NamingStrategy.d.ts +4 -0
- package/package.json +1 -1
- package/typings.d.ts +28 -13
- package/typings.js +5 -1
- package/unit-of-work/ChangeSetComputer.js +14 -4
- package/unit-of-work/ChangeSetPersister.js +7 -0
- package/unit-of-work/UnitOfWork.js +17 -3
- package/utils/EntityComparator.d.ts +5 -0
- package/utils/EntityComparator.js +66 -12
- package/utils/QueryHelper.d.ts +4 -0
- package/utils/QueryHelper.js +10 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -1
package/typings.d.ts
CHANGED
|
@@ -295,7 +295,14 @@ export type EntityName<T = any> = EntityClass<T> | EntityCtor<T> | EntitySchema<
|
|
|
295
295
|
export type GetRepository<Entity extends {
|
|
296
296
|
[k: PropertyKey]: any;
|
|
297
297
|
}, Fallback> = Entity[typeof EntityRepositoryType] extends EntityRepository<any> | undefined ? NonNullable<Entity[typeof EntityRepositoryType]> : Fallback;
|
|
298
|
-
|
|
298
|
+
type PolymorphicPrimaryInner<T> = T extends object ? Primary<T> extends readonly [infer First, infer Second, ...infer Rest] ? readonly [string, First, Second, ...Rest] | [string, First, Second, ...Rest] : readonly [string, Primary<T>] | [string, Primary<T>] : never;
|
|
299
|
+
/**
|
|
300
|
+
* Tuple format for polymorphic FK values: [discriminator, ...pkValues]
|
|
301
|
+
* Distributes over unions, so `Post | Comment` becomes `['post', number] | ['comment', number]`
|
|
302
|
+
* For composite keys like [tenantId, orgId], becomes ['discriminator', tenantId, orgId]
|
|
303
|
+
*/
|
|
304
|
+
export type PolymorphicPrimary<T> = true extends IsUnion<T> ? PolymorphicPrimaryInner<T> : never;
|
|
305
|
+
export type EntityDataPropValue<T> = T | Primary<T> | PolymorphicPrimary<T>;
|
|
299
306
|
type ExpandEntityProp<T, C extends boolean = false> = T extends Record<string, any> ? {
|
|
300
307
|
[K in keyof T as CleanKeys<T, K>]?: EntityDataProp<ExpandProperty<T[K]>, C> | EntityDataPropValue<ExpandProperty<T[K]>> | null;
|
|
301
308
|
} | EntityDataPropValue<ExpandProperty<T>> : T;
|
|
@@ -308,16 +315,17 @@ type ExpandRequiredEntityPropObject<T, I = never, C extends boolean = false> = {
|
|
|
308
315
|
type NonArrayObject = object & {
|
|
309
316
|
[Symbol.iterator]?: never;
|
|
310
317
|
};
|
|
311
|
-
export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends {
|
|
318
|
+
export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends {
|
|
312
319
|
__runtime?: infer Runtime;
|
|
313
320
|
__raw?: infer Raw;
|
|
314
|
-
} ? (C extends true ? Raw : Runtime) : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends
|
|
315
|
-
export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends RequiredNullable.Brand ? T | null : T extends Scalar ? T : T extends {
|
|
321
|
+
} ? (C extends true ? Raw : Runtime) : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends CollectionShape<infer U> ? U | U[] | EntityDataNested<U & object, C> | EntityDataNested<U & object, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
|
|
322
|
+
export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends RequiredNullable.Brand ? T | null : T extends Scalar ? T : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends {
|
|
316
323
|
__runtime?: infer Runtime;
|
|
317
324
|
__raw?: infer Raw;
|
|
318
|
-
} ? (C extends true ? Raw : Runtime) : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends
|
|
325
|
+
} ? (C extends true ? Raw : Runtime) : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends CollectionShape<infer U> ? U | U[] | RequiredEntityDataNested<U & object, O, C> | RequiredEntityDataNested<U & object, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
|
|
319
326
|
export type EntityDataNested<T, C extends boolean = false> = T extends undefined ? never : T extends any[] ? Readonly<T> : EntityData<T, C> | ExpandEntityProp<T, C>;
|
|
320
|
-
type
|
|
327
|
+
type UnwrapScalarRef<T> = T extends ScalarReference<infer U> ? U : T;
|
|
328
|
+
type EntityDataItem<T, C extends boolean> = C extends false ? UnwrapScalarRef<T> | EntityDataProp<T, C> | Raw | null : EntityDataProp<T, C> | Raw | null;
|
|
321
329
|
export type RequiredEntityDataNested<T, O, C extends boolean> = T extends any[] ? Readonly<T> : RequiredEntityData<T, O> | ExpandRequiredEntityProp<T, O, C>;
|
|
322
330
|
type ExplicitlyOptionalProps<T> = (T extends {
|
|
323
331
|
[OptionalProps]?: infer K;
|
|
@@ -338,9 +346,9 @@ export type EntityData<T, C extends boolean = false> = {
|
|
|
338
346
|
[K in EntityKey<T>]?: EntityDataItem<T[K] & {}, C>;
|
|
339
347
|
};
|
|
340
348
|
export type RequiredEntityData<T, I = never, C extends boolean = false> = {
|
|
341
|
-
[K in keyof T as RequiredKeys<T, K, I>]: T[K] | RequiredEntityDataProp<T[K], T, C> | Primary<T[K]> | Raw;
|
|
349
|
+
[K in keyof T as RequiredKeys<T, K, I>]: T[K] | RequiredEntityDataProp<T[K], T, C> | Primary<T[K]> | PolymorphicPrimary<T[K]> | Raw;
|
|
342
350
|
} & {
|
|
343
|
-
[K in keyof T as OptionalKeys<T, K, I>]?: T[K] | RequiredEntityDataProp<T[K], T, C> | Primary<T[K]> | Raw | null;
|
|
351
|
+
[K in keyof T as OptionalKeys<T, K, I>]?: T[K] | RequiredEntityDataProp<T[K], T, C> | Primary<T[K]> | PolymorphicPrimary<T[K]> | Raw | null;
|
|
344
352
|
};
|
|
345
353
|
export type EntityDictionary<T> = EntityData<T> & Record<any, any>;
|
|
346
354
|
type ExtractEagerProps<T> = T extends {
|
|
@@ -375,9 +383,9 @@ type PreferExplicitConfig<E, I> = IsNever<E, I, E>;
|
|
|
375
383
|
type PrimaryOrObject<T, U, C extends TypeConfig> = PreferExplicitConfig<C, ExtractConfig<T>>['forceObject'] extends true ? {
|
|
376
384
|
[K in PrimaryProperty<U> & keyof U]: U[K];
|
|
377
385
|
} : Primary<U>;
|
|
378
|
-
export type EntityDTOProp<E, T, C extends TypeConfig = never> = T extends Scalar ? T : T extends {
|
|
386
|
+
export type EntityDTOProp<E, T, C extends TypeConfig = never> = T extends Scalar ? T : T extends ScalarReference<infer U> ? U : T extends {
|
|
379
387
|
__serialized?: infer U;
|
|
380
|
-
} ? (IsUnknown<U> extends false ? U : T) : T extends LoadedReferenceShape<infer U> ? EntityDTO<U, C> : T extends ReferenceShape<infer U> ? PrimaryOrObject<E, U, C> : T extends
|
|
388
|
+
} ? (IsUnknown<U> extends false ? U : T) : T extends LoadedReferenceShape<infer U> ? EntityDTO<U, C> : T extends ReferenceShape<infer U> ? PrimaryOrObject<E, U, C> : T extends LoadedCollectionShape<infer U> ? EntityDTO<U & object, C>[] : T extends CollectionShape<infer U> ? PrimaryOrObject<E, U & object, C>[] : T extends readonly (infer U)[] ? U extends Scalar ? T : EntityDTOProp<E, U, C>[] : T extends Relation<T> ? EntityDTO<T, C> : T;
|
|
381
389
|
type UnwrapLoadedEntity<T> = T extends {
|
|
382
390
|
[__loadedType]?: infer U;
|
|
383
391
|
} ? NonNullable<U> : T;
|
|
@@ -452,6 +460,11 @@ export interface EntityProperty<Owner = any, Target = any> {
|
|
|
452
460
|
embeddable: EntityClass<Owner>;
|
|
453
461
|
embeddedProps: Dictionary<EntityProperty>;
|
|
454
462
|
discriminatorColumn?: string;
|
|
463
|
+
discriminator?: string;
|
|
464
|
+
polymorphic?: boolean;
|
|
465
|
+
polymorphTargets?: EntityMetadata[];
|
|
466
|
+
discriminatorMap?: Dictionary<EntityClass<Target>>;
|
|
467
|
+
discriminatorValue?: string;
|
|
455
468
|
object?: boolean;
|
|
456
469
|
index?: boolean | string;
|
|
457
470
|
unique?: boolean | string;
|
|
@@ -613,6 +626,8 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
|
|
|
613
626
|
polymorphs?: EntityMetadata[];
|
|
614
627
|
root: EntityMetadata<Entity>;
|
|
615
628
|
definedProperties: Dictionary;
|
|
629
|
+
/** For polymorphic M:N pivot tables, maps discriminator values to entity classes */
|
|
630
|
+
polymorphicDiscriminatorMap?: Dictionary<EntityClass>;
|
|
616
631
|
hasTriggers?: boolean;
|
|
617
632
|
/** @internal can be used for computed numeric cache keys */
|
|
618
633
|
readonly _id: number;
|
|
@@ -852,10 +867,10 @@ type ExtractStringKeys<T> = {
|
|
|
852
867
|
type StringKeys<T, E extends string = never> = T extends object ? ExtractStringKeys<ExtractType<T>> | E : never;
|
|
853
868
|
type GetStringKey<T, K extends StringKeys<T, string>, E extends string> = K extends keyof T ? ExtractType<T[K]> : (K extends E ? keyof T : never);
|
|
854
869
|
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
855
|
-
type
|
|
856
|
-
[K in keyof T]-?: T
|
|
870
|
+
type RelationKeys<T> = T extends object ? {
|
|
871
|
+
[K in keyof T]-?: CleanKeys<T, K, true>;
|
|
857
872
|
}[keyof T] & {} : never;
|
|
858
|
-
export type AutoPath<O, P extends string | boolean, E extends string = never, D extends Prev[number] = 9> = P extends boolean ? P : [D] extends [never] ? never : P extends any ? P extends string ? P extends `${infer A}.${infer B}` ? A extends StringKeys<O, E> ? `${A}.${AutoPath<NonNullable<GetStringKey<O, A, E>>, B, E, Prev[D]>}` : never : P extends StringKeys<O, E> ? (NonNullable<GetStringKey<O, P & StringKeys<O, E>, E>> extends unknown ? Exclude<P, `${string}.`> : never) | (StringKeys<NonNullable<GetStringKey<O, P & StringKeys<O, E>, E>>, E> extends never ? never : `${P & string}.`) : StringKeys<O, E> | `${
|
|
873
|
+
export type AutoPath<O, P extends string | boolean, E extends string = never, D extends Prev[number] = 9> = P extends boolean ? P : [D] extends [never] ? never : P extends any ? P extends string ? P extends `${infer A}.${infer B}` ? A extends StringKeys<O, E> ? `${A}.${AutoPath<NonNullable<GetStringKey<O, A, E>>, B, E, Prev[D]>}` : never : P extends StringKeys<O, E> ? (NonNullable<GetStringKey<O, P & StringKeys<O, E>, E>> extends unknown ? Exclude<P, `${string}.`> : never) | (StringKeys<NonNullable<GetStringKey<O, P & StringKeys<O, E>, E>>, E> extends never ? never : `${P & string}.`) : StringKeys<O, E> | `${RelationKeys<O>}:ref` : never : never;
|
|
859
874
|
export type UnboxArray<T> = T extends any[] ? ArrayElement<T> : T;
|
|
860
875
|
export type ArrayElement<ArrayType extends unknown[]> = ArrayType extends (infer ElementType)[] ? ElementType : never;
|
|
861
876
|
export type ExpandProperty<T> = T extends ReferenceShape<infer U> ? NonNullable<U> : T extends CollectionShape<infer U> ? NonNullable<U> : T extends (infer U)[] ? NonNullable<U> : NonNullable<T>;
|
package/typings.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReferenceKind, } from './enums.js';
|
|
2
2
|
import { Reference } from './entity/Reference.js';
|
|
3
3
|
import { EntityHelper } from './entity/EntityHelper.js';
|
|
4
|
+
import { helper } from './entity/wrap.js';
|
|
4
5
|
import { Utils } from './utils/Utils.js';
|
|
5
6
|
import { EntityComparator } from './utils/EntityComparator.js';
|
|
6
7
|
import { BaseEntity } from './entity/BaseEntity.js';
|
|
@@ -167,7 +168,10 @@ export class EntityMetadata {
|
|
|
167
168
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
168
169
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
169
170
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
170
|
-
|
|
171
|
+
const targetMeta = prop.targetMeta ?? helper(entity)?.__meta;
|
|
172
|
+
if (targetMeta) {
|
|
173
|
+
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, targetMeta, true);
|
|
174
|
+
}
|
|
171
175
|
}
|
|
172
176
|
EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
|
|
173
177
|
},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
2
3
|
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
|
|
3
4
|
import { helper } from '../entity/wrap.js';
|
|
4
5
|
import { validateEntity } from '../entity/validators.js';
|
|
6
|
+
import { Reference } from '../entity/Reference.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
5
8
|
import { ReferenceKind } from '../enums.js';
|
|
6
9
|
export class ChangeSetComputer {
|
|
7
10
|
collectionUpdates;
|
|
@@ -130,9 +133,10 @@ export class ChangeSetComputer {
|
|
|
130
133
|
return;
|
|
131
134
|
}
|
|
132
135
|
const targets = Utils.unwrapProperty(changeSet.entity, changeSet.meta, prop);
|
|
133
|
-
targets.forEach(([
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
targets.forEach(([rawTarget, idx]) => {
|
|
137
|
+
const target = Reference.unwrapReference(rawTarget);
|
|
138
|
+
const needsProcessing = target != null && (prop.targetKey != null || !target.__helper.hasPrimaryKey());
|
|
139
|
+
if (needsProcessing) {
|
|
136
140
|
let value = prop.targetKey ? target[prop.targetKey] : target.__helper.__identifier;
|
|
137
141
|
/* v8 ignore next */
|
|
138
142
|
if (prop.targetKey && prop.targetMeta) {
|
|
@@ -141,7 +145,13 @@ export class ChangeSetComputer {
|
|
|
141
145
|
value = targetProp.customType.convertToDatabaseValue(value, this.platform, { mode: 'serialization' });
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
|
-
|
|
148
|
+
if (prop.polymorphic) {
|
|
149
|
+
const discriminator = QueryHelper.findDiscriminatorValue(prop.discriminatorMap, target.constructor);
|
|
150
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, new PolymorphicRef(discriminator, value), idx);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, value, idx);
|
|
154
|
+
}
|
|
145
155
|
}
|
|
146
156
|
});
|
|
147
157
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
2
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
2
3
|
import { helper } from '../entity/wrap.js';
|
|
3
4
|
import { ChangeSetType } from './ChangeSet.js';
|
|
4
5
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
@@ -376,6 +377,12 @@ export class ChangeSetPersister {
|
|
|
376
377
|
changeSet.payload[prop.name] = value.getValue();
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
380
|
+
if (value instanceof PolymorphicRef) {
|
|
381
|
+
if (value.id instanceof EntityIdentifier) {
|
|
382
|
+
value.id = value.id.getValue();
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
379
386
|
if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
|
|
380
387
|
changeSet.payload[prop.name] = value.map(item => item.getValue());
|
|
381
388
|
return;
|
|
@@ -77,7 +77,10 @@ export class UnitOfWork {
|
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
79
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
80
|
-
|
|
80
|
+
// Skip polymorphic relations - they use PolymorphicRef wrapper
|
|
81
|
+
if (!prop.polymorphic) {
|
|
82
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
83
|
+
}
|
|
81
84
|
}
|
|
82
85
|
else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
|
|
83
86
|
for (const p of prop.targetMeta.props) {
|
|
@@ -254,7 +257,7 @@ export class UnitOfWork {
|
|
|
254
257
|
}
|
|
255
258
|
computeChangeSet(entity, type) {
|
|
256
259
|
const wrapped = helper(entity);
|
|
257
|
-
if (type) {
|
|
260
|
+
if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
|
|
258
261
|
this.changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
|
|
259
262
|
return;
|
|
260
263
|
}
|
|
@@ -262,6 +265,10 @@ export class UnitOfWork {
|
|
|
262
265
|
if (!cs || this.checkUniqueProps(cs)) {
|
|
263
266
|
return;
|
|
264
267
|
}
|
|
268
|
+
/* v8 ignore next */
|
|
269
|
+
if (type) {
|
|
270
|
+
cs.type = type;
|
|
271
|
+
}
|
|
265
272
|
this.initIdentifier(entity);
|
|
266
273
|
this.changeSets.set(entity, cs);
|
|
267
274
|
this.persistStack.delete(entity);
|
|
@@ -1010,7 +1017,14 @@ export class UnitOfWork {
|
|
|
1010
1017
|
set.forEach(meta => calc.addNode(meta._id));
|
|
1011
1018
|
for (const meta of set) {
|
|
1012
1019
|
for (const prop of meta.relations) {
|
|
1013
|
-
|
|
1020
|
+
if (prop.polymorphTargets) {
|
|
1021
|
+
for (const targetMeta of prop.polymorphTargets) {
|
|
1022
|
+
calc.discoverProperty({ ...prop, targetMeta }, meta._id);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
calc.discoverProperty(prop, meta._id);
|
|
1027
|
+
}
|
|
1014
1028
|
}
|
|
1015
1029
|
}
|
|
1016
1030
|
return calc.sort().map(id => this.metadata.getById(id));
|
|
@@ -85,6 +85,11 @@ export declare class EntityComparator {
|
|
|
85
85
|
private getPropertyComparator;
|
|
86
86
|
private wrap;
|
|
87
87
|
private safeKey;
|
|
88
|
+
/**
|
|
89
|
+
* Sets the toArray helper in the context if not already set.
|
|
90
|
+
* Used for converting composite PKs to arrays.
|
|
91
|
+
*/
|
|
92
|
+
private setToArrayHelper;
|
|
88
93
|
/**
|
|
89
94
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
90
95
|
*/
|
|
@@ -4,6 +4,7 @@ import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals,
|
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { Raw } from './RawQueryFragment.js';
|
|
6
6
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
7
8
|
export class EntityComparator {
|
|
8
9
|
metadata;
|
|
9
10
|
platform;
|
|
@@ -285,6 +286,7 @@ export class EntityComparator {
|
|
|
285
286
|
}
|
|
286
287
|
const lines = [];
|
|
287
288
|
const context = new Map();
|
|
289
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
288
290
|
const tz = this.platform.getTimezone();
|
|
289
291
|
const parseDate = (key, value, padding = '') => {
|
|
290
292
|
lines.push(`${padding} if (${value} == null || ${value} instanceof Date) {`);
|
|
@@ -311,12 +313,28 @@ export class EntityComparator {
|
|
|
311
313
|
if (!prop.fieldNames) {
|
|
312
314
|
continue;
|
|
313
315
|
}
|
|
316
|
+
if (prop.polymorphic && prop.fieldNames.length >= 2) {
|
|
317
|
+
const discriminatorField = prop.fieldNames[0];
|
|
318
|
+
const idFields = prop.fieldNames.slice(1);
|
|
319
|
+
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
320
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
321
|
+
if (idFields.length === 1) {
|
|
322
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, ${this.propName(idFields[0])});`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, [${idFields.map(f => this.propName(f)).join(', ')}]);`);
|
|
326
|
+
}
|
|
327
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
328
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
329
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
314
332
|
if (prop.targetMeta && prop.fieldNames.length > 1) {
|
|
315
333
|
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
316
334
|
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
317
335
|
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.createCompositeKeyArray(prop)};`);
|
|
318
336
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
319
|
-
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n ret${this.wrap(prop.name)} = null;`);
|
|
337
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
320
338
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
321
339
|
continue;
|
|
322
340
|
}
|
|
@@ -517,6 +535,27 @@ export class EntityComparator {
|
|
|
517
535
|
ret += ` ret${dataKey} = entity${entityKey};\n`;
|
|
518
536
|
}
|
|
519
537
|
}
|
|
538
|
+
else if (prop.polymorphic) {
|
|
539
|
+
const discriminatorMapKey = `discriminatorMapReverse_${prop.name}`;
|
|
540
|
+
const reverseMap = new Map();
|
|
541
|
+
for (const [key, value] of Object.entries(prop.discriminatorMap)) {
|
|
542
|
+
reverseMap.set(value, key);
|
|
543
|
+
}
|
|
544
|
+
context.set(discriminatorMapKey, reverseMap);
|
|
545
|
+
this.setToArrayHelper(context);
|
|
546
|
+
context.set('EntityIdentifier', EntityIdentifier);
|
|
547
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
548
|
+
ret += ` if (entity${entityKey} === null) {\n`;
|
|
549
|
+
ret += ` ret${dataKey} = null;\n`;
|
|
550
|
+
ret += ` } else if (typeof entity${entityKey} !== 'undefined') {\n`;
|
|
551
|
+
ret += ` const val${level} = entity${entityKey}${unwrap};\n`;
|
|
552
|
+
ret += ` const discriminator = ${discriminatorMapKey}.get(val${level}?.constructor);\n`;
|
|
553
|
+
ret += ` const pk = val${level}?.__helper?.__identifier && !val${level}?.__helper?.hasPrimaryKey()\n`;
|
|
554
|
+
ret += ` ? val${level}.__helper.__identifier\n`;
|
|
555
|
+
ret += ` : toArray(val${level}?.__helper?.getPrimaryKey(true));\n`;
|
|
556
|
+
ret += ` ret${dataKey} = new PolymorphicRef(discriminator, pk);\n`;
|
|
557
|
+
ret += ` }\n`;
|
|
558
|
+
}
|
|
520
559
|
else if (prop.targetKey) {
|
|
521
560
|
// When targetKey is set, extract that property value instead of the PK
|
|
522
561
|
const targetProp = prop.targetMeta?.properties[prop.targetKey];
|
|
@@ -535,13 +574,7 @@ export class EntityComparator {
|
|
|
535
574
|
ret += ` }\n`;
|
|
536
575
|
}
|
|
537
576
|
else {
|
|
538
|
-
|
|
539
|
-
if (Utils.isPlainObject(val)) {
|
|
540
|
-
return Object.values(val).map(v => toArray(v));
|
|
541
|
-
}
|
|
542
|
-
return val;
|
|
543
|
-
};
|
|
544
|
-
context.set('toArray', toArray);
|
|
577
|
+
this.setToArrayHelper(context);
|
|
545
578
|
context.set('EntityIdentifier', EntityIdentifier);
|
|
546
579
|
ret += ` if (entity${entityKey} === null) {\n`;
|
|
547
580
|
ret += ` ret${dataKey} = null;\n`;
|
|
@@ -613,12 +646,17 @@ export class EntityComparator {
|
|
|
613
646
|
getPropertyComparator(prop, context) {
|
|
614
647
|
let type = prop.type.toLowerCase();
|
|
615
648
|
if (prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
type = 'array';
|
|
649
|
+
if (prop.polymorphic) {
|
|
650
|
+
type = 'object';
|
|
619
651
|
}
|
|
620
652
|
else {
|
|
621
|
-
|
|
653
|
+
const meta2 = prop.targetMeta;
|
|
654
|
+
if (meta2.primaryKeys.length > 1) {
|
|
655
|
+
type = 'array';
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
type = meta2.getPrimaryProp().type.toLowerCase();
|
|
659
|
+
}
|
|
622
660
|
}
|
|
623
661
|
}
|
|
624
662
|
if (prop.customType) {
|
|
@@ -670,6 +708,22 @@ export class EntityComparator {
|
|
|
670
708
|
safeKey(key) {
|
|
671
709
|
return key.replace(/\W/g, '_');
|
|
672
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Sets the toArray helper in the context if not already set.
|
|
713
|
+
* Used for converting composite PKs to arrays.
|
|
714
|
+
*/
|
|
715
|
+
setToArrayHelper(context) {
|
|
716
|
+
if (context.has('toArray')) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const toArray = (val) => {
|
|
720
|
+
if (Utils.isPlainObject(val)) {
|
|
721
|
+
return Object.values(val).map(v => toArray(v));
|
|
722
|
+
}
|
|
723
|
+
return val;
|
|
724
|
+
};
|
|
725
|
+
context.set('toArray', toArray);
|
|
726
|
+
}
|
|
673
727
|
/**
|
|
674
728
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
675
729
|
*/
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
|
5
5
|
/** @internal */
|
|
6
6
|
export declare class QueryHelper {
|
|
7
7
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
10
|
+
*/
|
|
11
|
+
static findDiscriminatorValue<T>(discriminatorMap: Dictionary<T>, targetClass: T): string | undefined;
|
|
8
12
|
static processParams(params: unknown): any;
|
|
9
13
|
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
10
14
|
/**
|
package/utils/QueryHelper.js
CHANGED
|
@@ -7,6 +7,12 @@ import { isRaw, Raw } from './RawQueryFragment.js';
|
|
|
7
7
|
/** @internal */
|
|
8
8
|
export class QueryHelper {
|
|
9
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
10
|
+
/**
|
|
11
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
12
|
+
*/
|
|
13
|
+
static findDiscriminatorValue(discriminatorMap, targetClass) {
|
|
14
|
+
return Object.entries(discriminatorMap).find(([, cls]) => cls === targetClass)?.[0];
|
|
15
|
+
}
|
|
10
16
|
static processParams(params) {
|
|
11
17
|
if (Reference.isReference(params)) {
|
|
12
18
|
params = params.unwrap();
|
|
@@ -62,7 +68,10 @@ export class QueryHelper {
|
|
|
62
68
|
for (const k of keys) {
|
|
63
69
|
const value = where[k];
|
|
64
70
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
71
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
72
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
73
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
74
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
75
|
continue;
|
|
67
76
|
}
|
|
68
77
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
package/utils/Utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CompiledFunctions, Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadata, EntityName, EntityProperty, Primary } from '../typings.js';
|
|
2
2
|
import type { Collection } from '../entity/Collection.js';
|
|
3
3
|
import type { Platform } from '../platforms/Platform.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type ScalarReference } from '../entity/Reference.js';
|
|
5
5
|
import { type RawQueryFragmentSymbol } from './RawQueryFragment.js';
|
|
6
6
|
export declare function compareObjects(a: any, b: any): boolean;
|
|
7
7
|
export declare function compareArrays(a: any[] | string, b: any[] | string): boolean;
|
package/utils/Utils.js
CHANGED
|
@@ -123,7 +123,7 @@ export function parseJsonSafe(value) {
|
|
|
123
123
|
}
|
|
124
124
|
export class Utils {
|
|
125
125
|
static PK_SEPARATOR = '~~~';
|
|
126
|
-
static #ORM_VERSION = '7.0.0-dev.
|
|
126
|
+
static #ORM_VERSION = '7.0.0-dev.226';
|
|
127
127
|
/**
|
|
128
128
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
129
129
|
*/
|