@mikro-orm/core 7.3.0-dev.18 → 7.3.0-dev.19
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.
|
@@ -29,6 +29,11 @@ export declare class MetadataDiscovery {
|
|
|
29
29
|
private initFieldName;
|
|
30
30
|
private initManyToOneFieldName;
|
|
31
31
|
private initManyToManyFieldName;
|
|
32
|
+
/**
|
|
33
|
+
* Walks up the TPT hierarchy to the entity whose M:N declaration this property inherits. A child redeclaring
|
|
34
|
+
* the property with a different target, pivot table, join columns or fixed order keeps its own pivot table.
|
|
35
|
+
*/
|
|
36
|
+
private getPivotDeclaringMeta;
|
|
32
37
|
private initManyToManyFields;
|
|
33
38
|
private isExplicitTableName;
|
|
34
39
|
private initManyToOneFields;
|
|
@@ -485,8 +485,53 @@ export class MetadataDiscovery {
|
|
|
485
485
|
const meta2 = prop.targetMeta;
|
|
486
486
|
return meta2.primaryKeys.map(() => this.#namingStrategy.propertyToColumnName(name));
|
|
487
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Walks up the TPT hierarchy to the entity whose M:N declaration this property inherits. A child redeclaring
|
|
490
|
+
* the property with a different target, pivot table, join columns or fixed order keeps its own pivot table.
|
|
491
|
+
*/
|
|
492
|
+
getPivotDeclaringMeta(meta, prop) {
|
|
493
|
+
const keys = [
|
|
494
|
+
'type',
|
|
495
|
+
'pivotTable',
|
|
496
|
+
'pivotEntity',
|
|
497
|
+
'joinColumns',
|
|
498
|
+
'inverseJoinColumns',
|
|
499
|
+
'fixedOrder',
|
|
500
|
+
'fixedOrderColumn',
|
|
501
|
+
];
|
|
502
|
+
// Rails-style polymorphic owners already share an explicit pivot table, each under its own discriminator value
|
|
503
|
+
if (prop.polymorphic && !QueryHelper.isUnionTargetPolymorphic(prop)) {
|
|
504
|
+
return meta;
|
|
505
|
+
}
|
|
506
|
+
while (meta.tptParent) {
|
|
507
|
+
const parentProp = meta.tptParent.properties[prop.name];
|
|
508
|
+
if (parentProp?.kind !== ReferenceKind.MANY_TO_MANY) {
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
// resolve the parent first so explicit options are compared against its final mapping regardless of discovery order
|
|
512
|
+
this.initManyToManyFields(meta.tptParent, parentProp);
|
|
513
|
+
if (keys.some(key => prop[key] != null && !Utils.equals(prop[key], parentProp[key]))) {
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
meta = meta.tptParent;
|
|
517
|
+
}
|
|
518
|
+
return meta;
|
|
519
|
+
}
|
|
488
520
|
initManyToManyFields(meta, prop) {
|
|
489
521
|
const meta2 = prop.targetMeta;
|
|
522
|
+
// inverse sides derive their mapping from the owner via `mappedBy`
|
|
523
|
+
const declaringMeta = prop.mappedBy ? meta : this.getPivotDeclaringMeta(meta, prop);
|
|
524
|
+
if (declaringMeta !== meta) {
|
|
525
|
+
const prop2 = declaringMeta.properties[prop.name];
|
|
526
|
+
prop.pivotTable = prop2.pivotTable;
|
|
527
|
+
prop.pivotEntity = prop2.pivotEntity;
|
|
528
|
+
prop.fixedOrder = prop2.fixedOrder;
|
|
529
|
+
prop.fixedOrderColumn = prop2.fixedOrderColumn;
|
|
530
|
+
prop.joinColumns = prop2.joinColumns;
|
|
531
|
+
prop.inverseJoinColumns = prop2.inverseJoinColumns;
|
|
532
|
+
prop.referencedColumnNames = prop2.referencedColumnNames;
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
490
535
|
Utils.defaultValue(prop, 'fixedOrder', !!prop.fixedOrderColumn);
|
|
491
536
|
const pivotMeta = this.#metadata.find(prop.pivotEntity);
|
|
492
537
|
const props = Object.values(pivotMeta?.properties ?? {});
|
|
@@ -528,12 +573,16 @@ export class MetadataDiscovery {
|
|
|
528
573
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
529
574
|
// Union-target polymorphic M:N: owner side is fixed (real FK), target side uses discriminator-derived names.
|
|
530
575
|
const isUnionTargetMN = QueryHelper.isUnionTargetPolymorphic(prop);
|
|
576
|
+
// both sides of a TPT hierarchy would collide on the root name, so name them after the entities themselves
|
|
577
|
+
const sameTPTRoot = meta.inheritanceType === 'tpt' && meta.root.class === meta2.root.class;
|
|
578
|
+
const ownerName = sameTPTRoot ? meta.className : meta.root.className;
|
|
579
|
+
const targetName = sameTPTRoot ? meta2.className : meta2.root.className;
|
|
531
580
|
if (prop.polymorphic && prop.discriminator && !isUnionTargetMN) {
|
|
532
581
|
// Rails-style: owner side is polymorphic, uses discriminator base name (e.g. taggable_id instead of post_id)
|
|
533
582
|
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, referencedColumnName, prop.referencedColumnNames.length > 1));
|
|
534
583
|
}
|
|
535
584
|
else {
|
|
536
|
-
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(
|
|
585
|
+
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(ownerName, referencedColumnName, meta.compositePK));
|
|
537
586
|
}
|
|
538
587
|
if (isUnionTargetMN) {
|
|
539
588
|
// Target side uses discriminator base name (e.g. attachable_id — shared across Image/Video)
|
|
@@ -541,7 +590,7 @@ export class MetadataDiscovery {
|
|
|
541
590
|
prop.inverseJoinColumns ??= targetPkCols.map(fieldName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, fieldName, targetPkCols.length > 1));
|
|
542
591
|
}
|
|
543
592
|
else {
|
|
544
|
-
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop,
|
|
593
|
+
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, targetName);
|
|
545
594
|
}
|
|
546
595
|
}
|
|
547
596
|
isExplicitTableName(meta) {
|
|
@@ -640,8 +689,13 @@ export class MetadataDiscovery {
|
|
|
640
689
|
return Object.values(meta.properties)
|
|
641
690
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
642
691
|
.map(prop => {
|
|
643
|
-
|
|
692
|
+
// an inherited M:N lives in the pivot table of the declaring TPT entity, whose table the pivot FK references
|
|
693
|
+
const declaringMeta = this.getPivotDeclaringMeta(meta, prop);
|
|
694
|
+
const ownerProp = declaringMeta.properties[prop.name];
|
|
695
|
+
const pivotMeta = this.definePivotTableEntity(declaringMeta, ownerProp);
|
|
644
696
|
prop.pivotEntity = pivotMeta.class;
|
|
697
|
+
prop.inversedBy ??= ownerProp.inversedBy;
|
|
698
|
+
prop.fixedOrderColumn = ownerProp.fixedOrderColumn;
|
|
645
699
|
if (prop.inversedBy) {
|
|
646
700
|
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
647
701
|
const targetRoot = prop.targetMeta.root;
|
|
@@ -654,14 +708,22 @@ export class MetadataDiscovery {
|
|
|
654
708
|
// - regular inverse (Tag.posts mappedBy Post.tags) — handled by inversedBy above
|
|
655
709
|
// - union-target inverse (Image.posts mappedBy Post.attachments) — on each polymorph target
|
|
656
710
|
// - merged inverse (Tag.owners mappedBy [Post,Video].tags) — union collection on the target
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
711
|
+
// inherited inverse copies on TPT descendants of the target need the pivot as well
|
|
712
|
+
const withTPTDescendants = (m) => [
|
|
713
|
+
m,
|
|
714
|
+
...(m.tptChildren ?? []).flatMap(withTPTDescendants),
|
|
715
|
+
];
|
|
716
|
+
const targets = QueryHelper.isUnionTargetPolymorphic(prop) ? prop.polymorphTargets : [prop.targetMeta];
|
|
717
|
+
const inverseCandidates = targets.flatMap(withTPTDescendants);
|
|
660
718
|
for (const targetMeta of inverseCandidates) {
|
|
661
719
|
for (const inverseProp of Object.values(targetMeta.properties)) {
|
|
662
720
|
if (inverseProp.kind === ReferenceKind.MANY_TO_MANY &&
|
|
663
721
|
inverseProp.mappedBy === prop.name &&
|
|
664
|
-
!inverseProp.pivotEntity
|
|
722
|
+
!inverseProp.pivotEntity &&
|
|
723
|
+
// a descendant can declare its own inverse of another owner's same-named M:N
|
|
724
|
+
targets.some(target => target.properties[inverseProp.name]?.mappedBy === prop.name) &&
|
|
725
|
+
// the target itself can carry an inherited inverse of a parent M:N it redeclares
|
|
726
|
+
(inverseProp.targetMeta?.properties[prop.name]?.pivotTable ?? prop.pivotTable) === prop.pivotTable) {
|
|
665
727
|
inverseProp.pivotEntity = pivotMeta.class;
|
|
666
728
|
inverseProp.pivotTable = pivotMeta.tableName;
|
|
667
729
|
}
|
|
@@ -832,15 +894,20 @@ export class MetadataDiscovery {
|
|
|
832
894
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
833
895
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
834
896
|
}
|
|
835
|
-
// propagate updated joinColumns to all entities
|
|
836
|
-
|
|
897
|
+
// propagate updated joinColumns to all entities that inherit this property or its inverse (STI, or TPT sharing the pivot)
|
|
898
|
+
const inherits = (m) => m.root === baseMeta || (m.inheritanceType === 'tpt' && m.root === meta.root);
|
|
899
|
+
// includes `meta` itself: an STI root carries the inverse copy of an M:N declared on a child
|
|
900
|
+
for (const childMeta of this.#discovered.filter(inherits)) {
|
|
837
901
|
const childProp = childMeta.properties[prop.name];
|
|
838
|
-
|
|
902
|
+
// a TPT subclass redeclaring its own pivot table keeps its own columns
|
|
903
|
+
const sharesPivot = childMeta.inheritanceType !== 'tpt' || childProp?.pivotTable === prop.pivotTable;
|
|
904
|
+
if (childProp && sharesPivot) {
|
|
839
905
|
childProp.joinColumns = prop.joinColumns;
|
|
840
906
|
childProp.inverseJoinColumns = prop.inverseJoinColumns;
|
|
841
907
|
}
|
|
842
908
|
const childProp2 = prop.inversedBy ? childMeta.properties[prop.inversedBy] : undefined;
|
|
843
|
-
|
|
909
|
+
// the inherited inverse can stay on this pivot even when the child redeclares the owner side
|
|
910
|
+
if (childProp2 && (childMeta.inheritanceType !== 'tpt' || childProp2.pivotTable === prop.pivotTable)) {
|
|
844
911
|
childProp2.inverseJoinColumns = prop.joinColumns;
|
|
845
912
|
childProp2.joinColumns = prop.inverseJoinColumns;
|
|
846
913
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.3.0-dev.
|
|
3
|
+
"version": "7.3.0-dev.19",
|
|
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",
|
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.3.0-dev.
|
|
156
|
+
static #ORM_VERSION = '7.3.0-dev.19';
|
|
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}`;
|