mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
refactoring entity metadata builder stuff
This commit is contained in:
parent
311773b942
commit
46a24cec0c
@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
import {EntityManager} from "../entity-manager/EntityManager";
|
||||
import {importClassesFromDirectories, importJsonsFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
import {getFromContainer, getMetadataArgsStorage} from "../index";
|
||||
import {AllEntityMetadataBuilder} from "../metadata-builder/AllEntityMetadataBuilder";
|
||||
import {EntityMetadataBuilder} from "../metadata-builder/EntityMetadataBuilder";
|
||||
import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy";
|
||||
import {CannotImportAlreadyConnectedError} from "./error/CannotImportAlreadyConnectedError";
|
||||
import {CannotCloseNotConnectedError} from "./error/CannotCloseNotConnectedError";
|
||||
@ -699,7 +699,7 @@ export class Connection {
|
||||
.forEach(metadata => this.entityListeners.push(new EntityListenerMetadata(metadata)));
|
||||
|
||||
// build entity metadatas from metadata args storage (collected from decorators)
|
||||
new AllEntityMetadataBuilder(this.driver, lazyRelationsWrapper, getMetadataArgsStorage(), namingStrategy)
|
||||
new EntityMetadataBuilder(this.driver, lazyRelationsWrapper, getMetadataArgsStorage(), namingStrategy)
|
||||
.build(this.entityClasses)
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
@ -710,7 +710,7 @@ export class Connection {
|
||||
// build entity metadatas from given entity schemas
|
||||
if (this.entitySchemas && this.entitySchemas.length) {
|
||||
const metadataArgsStorage = getFromContainer(EntitySchemaTransformer).transform(this.entitySchemas);
|
||||
new AllEntityMetadataBuilder(this.driver, lazyRelationsWrapper, metadataArgsStorage, namingStrategy)
|
||||
new EntityMetadataBuilder(this.driver, lazyRelationsWrapper, metadataArgsStorage, namingStrategy)
|
||||
.build()
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
|
||||
@ -1,413 +0,0 @@
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
|
||||
import {RelationIdMetadata} from "../metadata/RelationIdMetadata";
|
||||
import {RelationCountMetadata} from "../metadata/RelationCountMetadata";
|
||||
import {MetadataUtils} from "../metadata-args/MetadataUtils";
|
||||
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
|
||||
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder";
|
||||
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder";
|
||||
import {RelationJoinColumnBuilder} from "./RelationJoinColumnBuilder";
|
||||
|
||||
/**
|
||||
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
|
||||
*/
|
||||
export class AllEntityMetadataBuilder {
|
||||
|
||||
// todo: type in function validation, inverse side function validation
|
||||
// todo: check on build for duplicate names, since naming checking was removed from MetadataStorage
|
||||
// todo: duplicate name checking for: table, relation, column, index, naming strategy, join tables/columns?
|
||||
// todo: check if multiple tree parent metadatas in validator
|
||||
// todo: tree decorators can be used only on closure table (validation)
|
||||
// todo: throw error if parent tree metadata was not specified in a closure table
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
protected junctionEntityMetadataBuilder: JunctionEntityMetadataBuilder;
|
||||
protected closureJunctionEntityMetadataBuilder: ClosureJunctionEntityMetadataBuilder;
|
||||
protected relationJoinColumnBuilder: RelationJoinColumnBuilder;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private driver: Driver,
|
||||
private lazyRelationsWrapper: LazyRelationsWrapper,
|
||||
private metadataArgsStorage: MetadataArgsStorage,
|
||||
private namingStrategy: NamingStrategyInterface) {
|
||||
this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder(driver, lazyRelationsWrapper, namingStrategy);
|
||||
this.closureJunctionEntityMetadataBuilder = new ClosureJunctionEntityMetadataBuilder(driver, lazyRelationsWrapper, namingStrategy);
|
||||
this.relationJoinColumnBuilder = new RelationJoinColumnBuilder(namingStrategy);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Builds a complete entity metadatas for the given entity classes.
|
||||
*/
|
||||
build(entityClasses?: Function[]): EntityMetadata[] {
|
||||
|
||||
// if entity classes to filter entities by are given then do filtering, otherwise use all
|
||||
const allTables = entityClasses ? this.metadataArgsStorage.filterTables(entityClasses) : this.metadataArgsStorage.tables.toArray();
|
||||
|
||||
// filter out table metadata args for those we really create entity metadatas and tables in the db
|
||||
const realTables = allTables.filter(table => table.type === "regular" || table.type === "closure" || table.type === "class-table-child");
|
||||
|
||||
// create entity metadatas for a user defined entities (marked with @Entity decorator or loaded from entity schemas)
|
||||
const entityMetadatas = realTables.map(tableArgs => this.createEntityMetadata(tableArgs));
|
||||
|
||||
// calculate entity metadata computed properties and all its sub-metadatas
|
||||
entityMetadatas.forEach(entityMetadata => this.computeEntityMetadata(entityMetadata));
|
||||
|
||||
// calculate entity metadata's inverse properties
|
||||
entityMetadatas.forEach(entityMetadata => this.computeInverseProperties(entityMetadata, entityMetadatas));
|
||||
|
||||
// go through all entity metadatas and create foreign keys / junction entity metadatas for their relations
|
||||
entityMetadatas.forEach(entityMetadata => {
|
||||
|
||||
// create entity's relations join columns (for many-to-one and one-to-one owner)
|
||||
entityMetadata.relations.filter(relation => relation.isOneToOne || relation.isManyToOne).forEach(relation => {
|
||||
const joinColumns = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
|
||||
const foreignKey = this.relationJoinColumnBuilder.build(joinColumns, relation); // create a foreign key based on its metadata args
|
||||
if (foreignKey) {
|
||||
relation.registerForeignKeys(foreignKey); // push it to the relation and thus register there a join column
|
||||
entityMetadata.foreignKeys.push(foreignKey);
|
||||
}
|
||||
});
|
||||
|
||||
// create junction entity metadatas for entity many-to-many relations
|
||||
entityMetadata.relations.filter(relation => relation.isManyToMany).forEach(relation => {
|
||||
const joinTable = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName);
|
||||
if (!joinTable) return; // no join table set - no need to do anything (it means this is many-to-many inverse side)
|
||||
|
||||
// here we create a junction entity metadata for a new junction table of many-to-many relation
|
||||
const junctionEntityMetadata = this.junctionEntityMetadataBuilder.build(relation, joinTable);
|
||||
relation.registerForeignKeys(...junctionEntityMetadata.foreignKeys);
|
||||
relation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
if (relation.inverseRelation)
|
||||
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
|
||||
// compute new entity metadata properties and push it to entity metadatas pool
|
||||
this.computeEntityMetadata(junctionEntityMetadata);
|
||||
this.computeInverseProperties(junctionEntityMetadata, entityMetadatas);
|
||||
entityMetadatas.push(junctionEntityMetadata);
|
||||
});
|
||||
|
||||
// update entity metadata depend properties
|
||||
entityMetadata.relationsWithJoinColumns = entityMetadata.relations.filter(relation => relation.isWithJoinColumn);
|
||||
entityMetadata.hasNonNullableRelations = entityMetadata.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary);
|
||||
});
|
||||
|
||||
// generate closure junction tables for all closure tables
|
||||
entityMetadatas
|
||||
.filter(metadata => metadata.isClosure)
|
||||
.forEach(entityMetadata => {
|
||||
const closureJunctionEntityMetadata = this.closureJunctionEntityMetadataBuilder.build(entityMetadata);
|
||||
entityMetadata.closureJunctionTable = closureJunctionEntityMetadata;
|
||||
this.computeEntityMetadata(closureJunctionEntityMetadata);
|
||||
this.computeInverseProperties(closureJunctionEntityMetadata, entityMetadatas);
|
||||
entityMetadatas.push(closureJunctionEntityMetadata);
|
||||
});
|
||||
|
||||
// generate keys for tables with single-table inheritance
|
||||
entityMetadatas
|
||||
.filter(metadata => metadata.inheritanceType === "single-table")
|
||||
.forEach(entityMetadata => this.createKeysForTableInheritance(entityMetadata));
|
||||
|
||||
// add lazy initializer for entity relations
|
||||
entityMetadatas
|
||||
.filter(metadata => metadata.target instanceof Function)
|
||||
.forEach(entityMetadata => {
|
||||
entityMetadata.relations
|
||||
.filter(relation => relation.isLazy)
|
||||
.forEach(relation => {
|
||||
this.lazyRelationsWrapper.wrap((entityMetadata.target as Function).prototype, relation);
|
||||
});
|
||||
});
|
||||
|
||||
return entityMetadatas;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates entity metadata from the given table args.
|
||||
* Creates column, relation, etc. metadatas for everything this entity metadata owns.
|
||||
*/
|
||||
protected createEntityMetadata(tableArgs: TableMetadataArgs): EntityMetadata {
|
||||
|
||||
// we take all "inheritance tree" from a target entity to collect all stored metadata args
|
||||
// (by decorators or inside entity schemas). For example for target Post < ContentModel < Unit
|
||||
// it will be an array of [Post, ContentModel, Unit] and we can then get all metadata args of those classes
|
||||
const inheritanceTree = tableArgs.target instanceof Function
|
||||
? MetadataUtils.getInheritanceTree(tableArgs.target)
|
||||
: [tableArgs.target]; // todo: implement later here inheritance for string-targets
|
||||
|
||||
const entityMetadata = new EntityMetadata({
|
||||
namingStrategy: this.namingStrategy,
|
||||
lazyRelationsWrapper: this.lazyRelationsWrapper,
|
||||
tablesPrefix: this.driver.options.tablesPrefix,
|
||||
args: tableArgs
|
||||
});
|
||||
entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(inheritanceTree));
|
||||
|
||||
entityMetadata.ownColumns = this.metadataArgsStorage.filterColumns(inheritanceTree).map(args => {
|
||||
return new ColumnMetadata({ entityMetadata, args });
|
||||
});
|
||||
entityMetadata.ownRelations = this.metadataArgsStorage.filterRelations(inheritanceTree).map(args => {
|
||||
return new RelationMetadata({ entityMetadata, args });
|
||||
});
|
||||
entityMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(inheritanceTree).map(args => {
|
||||
return new RelationIdMetadata({ entityMetadata, args });
|
||||
});
|
||||
entityMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(inheritanceTree).map(args => {
|
||||
return new RelationCountMetadata({ entityMetadata, args });
|
||||
});
|
||||
entityMetadata.indices = this.metadataArgsStorage.filterIndices(inheritanceTree).map(args => {
|
||||
return new IndexMetadata({ entityMetadata, args });
|
||||
});
|
||||
return entityMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates from the given embedded metadata args real embedded metadatas with its columns and relations,
|
||||
* and does the same for all its sub-embeddeds (goes recursively).
|
||||
*/
|
||||
protected createEmbeddedsRecursively(entityMetadata: EntityMetadata, embeddedArgs: EmbeddedMetadataArgs[]): EmbeddedMetadata[] {
|
||||
return embeddedArgs.map(embeddedArgs => {
|
||||
const embeddedMetadata = new EmbeddedMetadata({ entityMetadata: entityMetadata, args: embeddedArgs });
|
||||
embeddedMetadata.columns = this.metadataArgsStorage.filterColumns(embeddedMetadata.type).map(args => {
|
||||
return new ColumnMetadata({ entityMetadata, embeddedMetadata, args});
|
||||
});
|
||||
embeddedMetadata.relations = this.metadataArgsStorage.filterRelations(embeddedMetadata.type).map(args => {
|
||||
return new RelationMetadata({ entityMetadata, embeddedMetadata, args });
|
||||
});
|
||||
embeddedMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(embeddedMetadata.type));
|
||||
embeddedMetadata.embeddeds.forEach(subEmbedded => subEmbedded.parentEmbeddedMetadata = embeddedMetadata);
|
||||
return embeddedMetadata;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
|
||||
*/
|
||||
protected computeEntityMetadata(entityMetadata: EntityMetadata) {
|
||||
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.namingStrategy));
|
||||
entityMetadata.embeddeds.forEach(embedded => {
|
||||
embedded.columnsFromTree.forEach(column => column.build(this.namingStrategy));
|
||||
embedded.relationsFromTree.forEach(relation => relation.build(this.namingStrategy));
|
||||
});
|
||||
entityMetadata.ownColumns.forEach(column => column.build(this.namingStrategy));
|
||||
entityMetadata.ownRelations.forEach(relation => relation.build(this.namingStrategy));
|
||||
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
|
||||
entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne);
|
||||
entityMetadata.oneToManyRelations = entityMetadata.relations.filter(relation => relation.isOneToMany);
|
||||
entityMetadata.manyToOneRelations = entityMetadata.relations.filter(relation => relation.isManyToOne);
|
||||
entityMetadata.manyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToMany);
|
||||
entityMetadata.ownerOneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOneOwner);
|
||||
entityMetadata.ownerManyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToManyOwner);
|
||||
entityMetadata.treeParentRelation = entityMetadata.relations.find(relation => relation.isTreeParent)!; // todo: fix ! later
|
||||
entityMetadata.treeChildrenRelation = entityMetadata.relations.find(relation => relation.isTreeChildren)!; // todo: fix ! later
|
||||
entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns);
|
||||
entityMetadata.primaryColumns = entityMetadata.columns.filter(column => column.isPrimary);
|
||||
entityMetadata.hasMultiplePrimaryKeys = entityMetadata.primaryColumns.length > 1;
|
||||
entityMetadata.generatedColumn = entityMetadata.columns.find(column => column.isGenerated)!; // todo: fix ! later
|
||||
entityMetadata.createDateColumn = entityMetadata.columns.find(column => column.mode === "createDate")!; // todo: fix ! later
|
||||
entityMetadata.updateDateColumn = entityMetadata.columns.find(column => column.mode === "updateDate")!; // todo: fix ! later
|
||||
entityMetadata.versionColumn = entityMetadata.columns.find(column => column.mode === "version")!; // todo: fix ! later
|
||||
entityMetadata.discriminatorColumn = entityMetadata.columns.find(column => column.mode === "discriminator")!; // todo: fix ! later
|
||||
entityMetadata.treeLevelColumn = entityMetadata.columns.find(column => column.mode === "treeLevel")!; // todo: fix ! later
|
||||
entityMetadata.parentIdColumns = entityMetadata.columns.filter(column => column.mode === "parentId")!; // todo: fix ! later
|
||||
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.mode === "objectId")!; // todo: fix ! later
|
||||
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.namingStrategy));
|
||||
entityMetadata.indices.forEach(index => index.build(this.namingStrategy));
|
||||
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes entity metadata's relations inverse side properties.
|
||||
*/
|
||||
protected computeInverseProperties(entityMetadata: EntityMetadata, entityMetadatas: EntityMetadata[]) {
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
|
||||
// compute inverse side (related) entity metadatas for all relation metadatas
|
||||
const inverseEntityMetadata = entityMetadatas.find(m => m.target === relation.type || (typeof relation.type === "string" && m.targetName === relation.type));
|
||||
if (!inverseEntityMetadata)
|
||||
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyPath + " was not found. Check if you specified a correct entity object, check its really entity and its connected in the connection options.");
|
||||
|
||||
relation.inverseEntityMetadata = inverseEntityMetadata;
|
||||
relation.inverseSidePropertyPath = relation.buildInverseSidePropertyPath();
|
||||
|
||||
// and compute inverse relation and mark if it has such
|
||||
relation.inverseRelation = inverseEntityMetadata.relations.find(foundRelation => foundRelation.propertyPath === relation.inverseSidePropertyPath)!; // todo: remove ! later
|
||||
relation.hasInverseSide = !!relation.inverseRelation; // todo: do we really need this flag
|
||||
});
|
||||
}
|
||||
|
||||
protected createKeysForTableInheritance(entityMetadata: EntityMetadata) {
|
||||
const indexForKey = new IndexMetadata({
|
||||
entityMetadata: entityMetadata,
|
||||
columns: [entityMetadata.discriminatorColumn],
|
||||
args: {
|
||||
target: entityMetadata.target,
|
||||
unique: false
|
||||
}
|
||||
});
|
||||
entityMetadata.indices.push(indexForKey);
|
||||
|
||||
const indexForKeyWithPrimary = new IndexMetadata({
|
||||
entityMetadata: entityMetadata,
|
||||
columns: [entityMetadata.primaryColumns[0], entityMetadata.discriminatorColumn],
|
||||
args: {
|
||||
target: entityMetadata.target,
|
||||
unique: false
|
||||
}
|
||||
});
|
||||
entityMetadata.indices.push(indexForKeyWithPrimary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// generate virtual column with foreign key for class-table inheritance
|
||||
/*entityMetadatas.forEach(entityMetadata => {
|
||||
if (!entityMetadata.parentEntityMetadata)
|
||||
return;
|
||||
|
||||
const parentPrimaryColumns = entityMetadata.parentEntityMetadata.primaryColumns;
|
||||
const parentIdColumns = parentPrimaryColumns.map(primaryColumn => {
|
||||
const columnName = this.namingStrategy.classTableInheritanceParentColumnName(entityMetadata.parentEntityMetadata.tableName, primaryColumn.propertyName);
|
||||
const column = new ColumnMetadataBuilder(entityMetadata);
|
||||
column.type = primaryColumn.type;
|
||||
column.propertyName = primaryColumn.propertyName; // todo: check why needed
|
||||
column.givenName = columnName;
|
||||
column.mode = "parentId";
|
||||
column.isUnique = true;
|
||||
column.isNullable = false;
|
||||
// column.entityTarget = entityMetadata.target;
|
||||
return column;
|
||||
});
|
||||
|
||||
// add foreign key
|
||||
const foreignKey = new ForeignKeyMetadataBuilder(
|
||||
entityMetadata,
|
||||
parentIdColumns,
|
||||
entityMetadata.parentEntityMetadata,
|
||||
parentPrimaryColumns,
|
||||
"CASCADE"
|
||||
);
|
||||
entityMetadata.ownColumns.push(...parentIdColumns);
|
||||
entityMetadata.foreignKeys.push(foreignKey);
|
||||
});*/
|
||||
|
||||
|
||||
/*protected createEntityMetadata(metadata: EntityMetadata, options: {
|
||||
userSpecifiedTableName?: string,
|
||||
closureOwnerTableName?: string,
|
||||
}) {
|
||||
|
||||
const tableNameUserSpecified = options.userSpecifiedTableName;
|
||||
const isClosureJunction = metadata.tableType === "closure-junction";
|
||||
const targetName = metadata.target instanceof Function ? (metadata.target as any).name : metadata.target;
|
||||
const tableNameWithoutPrefix = isClosureJunction
|
||||
? this.namingStrategy.closureJunctionTableName(options.closureOwnerTableName!)
|
||||
: this.namingStrategy.tableName(targetName, options.userSpecifiedTableName);
|
||||
|
||||
const tableName = this.namingStrategy.prefixTableName(this.driver.options.tablesPrefix, tableNameWithoutPrefix);
|
||||
|
||||
// for virtual tables (like junction table) target is equal to undefined at this moment
|
||||
// we change this by setting virtual's table name to a target name
|
||||
// todo: add validation so targets with same schema names won't conflicts with virtual table names
|
||||
metadata.target = metadata.target ? metadata.target : tableName;
|
||||
metadata.targetName = targetName;
|
||||
metadata.givenTableName = tableNameUserSpecified;
|
||||
metadata.tableNameWithoutPrefix = tableNameWithoutPrefix;
|
||||
metadata.tableName = tableName;
|
||||
metadata.name = targetName ? targetName : tableName;
|
||||
// metadata.namingStrategy = this.namingStrategy;
|
||||
}*/
|
||||
|
||||
/*protected createEntityMetadata(tableArgs: any, argsForTable: any, ): EntityMetadata {
|
||||
const metadata = new EntityMetadata({
|
||||
junction: false,
|
||||
target: tableArgs.target,
|
||||
tablesPrefix: this.driver.options.tablesPrefix,
|
||||
namingStrategy: this.namingStrategy,
|
||||
tableName: argsForTable.name,
|
||||
tableType: argsForTable.type,
|
||||
orderBy: argsForTable.orderBy,
|
||||
engine: argsForTable.engine,
|
||||
skipSchemaSync: argsForTable.skipSchemaSync,
|
||||
columnMetadatas: columns,
|
||||
relationMetadatas: relations,
|
||||
relationIdMetadatas: relationIds,
|
||||
relationCountMetadatas: relationCounts,
|
||||
indexMetadatas: indices,
|
||||
embeddedMetadatas: embeddeds,
|
||||
inheritanceType: mergedArgs.inheritance ? mergedArgs.inheritance.type : undefined,
|
||||
discriminatorValue: discriminatorValueArgs ? discriminatorValueArgs.value : (tableArgs.target as any).name // todo: pass this to naming strategy to generate a name
|
||||
}, this.lazyRelationsWrapper);
|
||||
return metadata;
|
||||
}*/
|
||||
|
||||
|
||||
// const tables = [mergedArgs.table].concat(mergedArgs.children);
|
||||
// tables.forEach(tableArgs => {
|
||||
|
||||
// find embeddable tables for embeddeds registered in this table and create EmbeddedMetadatas from them
|
||||
// const findEmbeddedsRecursively = (embeddedArgs: EmbeddedMetadataArgs[]) => {
|
||||
// const embeddeds: EmbeddedMetadata[] = [];
|
||||
// embeddedArgs.forEach(embedded => {
|
||||
// const embeddableTable = embeddableMergedArgs.find(embeddedMergedArgs => embeddedMergedArgs.table.target === embedded.type());
|
||||
// if (embeddableTable) {
|
||||
// const columns = embeddableTable.columns.toArray().map(args => new ColumnMetadata(args));
|
||||
// const relations = embeddableTable.relations.toArray().map(args => new RelationMetadata(args));
|
||||
// const subEmbeddeds = findEmbeddedsRecursively(embeddableTable.embeddeds.toArray());
|
||||
// embeddeds.push(new EmbeddedMetadata(columns, relations, subEmbeddeds, embedded));
|
||||
// }
|
||||
// });
|
||||
// return embeddeds;
|
||||
// };
|
||||
// const embeddeds = findEmbeddedsRecursively(mergedArgs.embeddeds.toArray());
|
||||
|
||||
// create metadatas from args
|
||||
// const argsForTable = mergedArgs.inheritance && mergedArgs.inheritance.type === "single-table" ? mergedArgs.table : tableArgs;
|
||||
|
||||
// const table = new TableMetadata(argsForTable);
|
||||
// const columns = mergedArgs.columns.toArray().map(args => {
|
||||
//
|
||||
// // if column's target is a child table then this column should have all nullable columns
|
||||
// if (mergedArgs.inheritance &&
|
||||
// mergedArgs.inheritance.type === "single-table" &&
|
||||
// args.target !== mergedArgs.table.target && !!mergedArgs.children.find(childTable => childTable.target === args.target)) {
|
||||
// args.options.nullable = true;
|
||||
// }
|
||||
// return new ColumnMetadata(args);
|
||||
// });
|
||||
// const discriminatorValueArgs = mergedArgs.discriminatorValues.find(discriminatorValueArgs => {
|
||||
// return discriminatorValueArgs.target === tableArgs.target;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
// after all metadatas created we set parent entity metadata for class-table inheritance
|
||||
// entityMetadatas.forEach(entityMetadata => {
|
||||
// const mergedArgs = realTables.find(args => args.target === entityMetadata.target);
|
||||
// if (mergedArgs && mergedArgs.parent) {
|
||||
// const parentEntityMetadata = entityMetadatas.find(entityMetadata => entityMetadata.target === (mergedArgs!.parent! as any).target); // todo: weird compiler error here, thats why type casing is used
|
||||
// if (parentEntityMetadata)
|
||||
// entityMetadata.parentEntityMetadata = parentEntityMetadata;
|
||||
// }
|
||||
// });
|
||||
@ -1,317 +0,0 @@
|
||||
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
|
||||
import {ColumnType} from "../metadata/types/ColumnTypes";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
|
||||
/**
|
||||
* Kinda type of the column. Not a type in the database, but locally used type to determine what kind of column
|
||||
* we are working with.
|
||||
* For example, "primary" means that it will be a primary column, or "createDate" means that it will create a create
|
||||
* date column.
|
||||
*/
|
||||
export type ColumnMode = "regular"|"virtual"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel"|"discriminator"|"parentId"|"objectId"|"array";
|
||||
|
||||
/**
|
||||
* This metadata contains all information about entity's column.
|
||||
*/
|
||||
export class ColumnMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this column metadata is.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Embedded metadata where this column metadata is.
|
||||
* If this column is not in embed then this property value is undefined.
|
||||
*/
|
||||
embeddedMetadata: EmbeddedMetadata;
|
||||
|
||||
/**
|
||||
* If column is a foreign key of some relation then this relation's metadata will be there.
|
||||
* If this column does not have a foreign key then this property value is undefined.
|
||||
*/
|
||||
relationMetadata: RelationMetadata;
|
||||
|
||||
/**
|
||||
* Column's mode in which this column is working.
|
||||
*/
|
||||
mode: ColumnMode;
|
||||
|
||||
/**
|
||||
* Class's property name on which this column is applied.
|
||||
*/
|
||||
propertyName: string;
|
||||
|
||||
/**
|
||||
* The database type of the column.
|
||||
*/
|
||||
type: ColumnType;
|
||||
|
||||
/**
|
||||
* Type's length in the database.
|
||||
*/
|
||||
length: string = "";
|
||||
|
||||
/**
|
||||
* Indicates if this column is a primary key.
|
||||
*/
|
||||
isPrimary: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates if this column is generated (auto increment or generated other way).
|
||||
*/
|
||||
isGenerated: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates if value in the database should be unique or not.
|
||||
*/
|
||||
isUnique: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates if column can contain nulls or not.
|
||||
*/
|
||||
isNullable: boolean = false;
|
||||
|
||||
/**
|
||||
* Column comment.
|
||||
* This feature is not supported by all databases.
|
||||
*/
|
||||
comment: string = "";
|
||||
|
||||
/**
|
||||
* Default database value.
|
||||
*/
|
||||
default: any;
|
||||
|
||||
/**
|
||||
* The precision for a decimal (exact numeric) column (applies only for decimal column),
|
||||
* which is the maximum number of digits that are stored for the values.
|
||||
*/
|
||||
precision: number;
|
||||
|
||||
/**
|
||||
* The scale for a decimal (exact numeric) column (applies only for decimal column),
|
||||
* which represents the number of digits to the right of the decimal point and must not be greater than precision.
|
||||
*/
|
||||
scale: number;
|
||||
|
||||
/**
|
||||
* Indicates if date column will contain a timezone.
|
||||
* Used only for date-typed column types.
|
||||
* Note that timezone option is not supported by all databases (only postgres for now).
|
||||
*/
|
||||
timezone: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if date object must be stored in given date's timezone.
|
||||
* By default date is saved in UTC timezone.
|
||||
* Works only with "datetime" columns.
|
||||
*/
|
||||
localTimezone?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column's type will be set as a fixed-length data type.
|
||||
* Works only with "string" columns.
|
||||
*/
|
||||
fixedLength?: boolean;
|
||||
|
||||
/**
|
||||
* User's specified custom column name.
|
||||
*/
|
||||
givenName: string;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, args?: ColumnMetadataArgs) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
// this.entityTarget = entityMetadata.target;
|
||||
if (args) {
|
||||
this.propertyName = args.propertyName;
|
||||
|
||||
if (args.mode)
|
||||
this.mode = args.mode;
|
||||
if (args.options.name)
|
||||
this.givenName = args.options.name;
|
||||
if (args.options.type)
|
||||
this.type = args.options.type;
|
||||
|
||||
if (args.options.length)
|
||||
this.length = String(args.options.length);
|
||||
if (args.options.primary)
|
||||
this.isPrimary = args.options.primary;
|
||||
if (args.options.generated)
|
||||
this.isGenerated = args.options.generated;
|
||||
if (args.options.unique)
|
||||
this.isUnique = args.options.unique;
|
||||
if (args.options.nullable)
|
||||
this.isNullable = args.options.nullable;
|
||||
if (args.options.comment)
|
||||
this.comment = args.options.comment;
|
||||
if (args.options.default !== undefined && args.options.default !== null)
|
||||
this.default = args.options.default;
|
||||
if (args.options.scale)
|
||||
this.scale = args.options.scale;
|
||||
if (args.options.precision)
|
||||
this.precision = args.options.precision;
|
||||
if (args.options.timezone)
|
||||
this.timezone = args.options.timezone;
|
||||
if (args.options.localTimezone)
|
||||
this.localTimezone = args.options.localTimezone;
|
||||
if (args.options.fixedLength)
|
||||
this.fixedLength = args.options.fixedLength;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets column's entity target.
|
||||
* Original target returns target of the class where column is.
|
||||
* This class can be an abstract class, but column even is from that class,
|
||||
* but its more related to a specific entity. That's why we need this field.
|
||||
*/
|
||||
get entityTarget(): Function|string {
|
||||
return this.entityMetadata.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets full path to this column property (including column property name).
|
||||
* Full path is relevant when column is used in embeds (one or multiple nested).
|
||||
* For example it will return "counters.subcounters.likes".
|
||||
* If property is not in embeds then it returns just property name of the column.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
get propertyPath(): string {
|
||||
if (!this.embeddedMetadata || !this.embeddedMetadata.parentPropertyNames.length)
|
||||
return this.propertyName;
|
||||
|
||||
return this.embeddedMetadata.parentPropertyNames.join(".") + "." + this.propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete column name in the database including its embedded prefixes.
|
||||
*/
|
||||
get databaseName(): string {
|
||||
|
||||
// if this column is embedded's column then apply different entity
|
||||
if (this.embeddedMetadata) {
|
||||
|
||||
// because embedded can be inside other embedded we need to go recursively and collect all prefix name
|
||||
const prefixes: string[] = [];
|
||||
const buildPrefixRecursively = (embeddedMetadata: EmbeddedMetadata) => {
|
||||
if (embeddedMetadata.parentEmbeddedMetadata)
|
||||
buildPrefixRecursively(embeddedMetadata.parentEmbeddedMetadata);
|
||||
|
||||
prefixes.push(embeddedMetadata.prefix);
|
||||
};
|
||||
buildPrefixRecursively(this.embeddedMetadata);
|
||||
|
||||
return (this as any).namingStrategy.embeddedColumnName(prefixes, this.propertyName, this.givenName);
|
||||
}
|
||||
|
||||
// if there is a naming strategy then use it to normalize propertyName as column name
|
||||
if (this.entityMetadata)
|
||||
return (this as any).namingStrategy.columnName(this.propertyName, this.givenName);
|
||||
|
||||
return this.givenName;
|
||||
// throw new Error(`Column ${this._name ? this._name + " " : ""}is not attached to any entity or embedded.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if column is virtual. Virtual columns are not mapped to the entity.
|
||||
*/
|
||||
get isVirtual() {
|
||||
return this.mode === "virtual";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if column is a parent id. Parent id columns are not mapped to the entity.
|
||||
*/
|
||||
get isParentId() {
|
||||
return this.mode === "parentId";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if column is discriminator. Discriminator columns are not mapped to the entity.
|
||||
*/
|
||||
get isDiscriminator() {
|
||||
return this.mode === "discriminator";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity creation date.
|
||||
*/
|
||||
get isCreateDate() {
|
||||
return this.mode === "createDate";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity update date.
|
||||
*/
|
||||
get isUpdateDate() {
|
||||
return this.mode === "updateDate";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity version.
|
||||
*/
|
||||
get isVersion() {
|
||||
return this.mode === "version";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an object id.
|
||||
*/
|
||||
get isObjectId() {
|
||||
return this.mode === "objectId";
|
||||
}
|
||||
|
||||
/**
|
||||
* If this column is foreign key then it references some other column,
|
||||
* and this property will contain reference to this column.
|
||||
*/
|
||||
get referencedColumn(): ColumnMetadata|undefined {
|
||||
const foreignKeys = this.relationMetadata ? this.relationMetadata.foreignKeys : this.entityMetadata.foreignKeys;
|
||||
const foreignKey = foreignKeys.find(foreignKey => foreignKey.columns.indexOf(this as any) !== -1);
|
||||
if (foreignKey) {
|
||||
const columnIndex = foreignKey.columns.indexOf(this as any);
|
||||
return foreignKey.referencedColumns[columnIndex];
|
||||
}
|
||||
|
||||
return undefined!;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
build(options: {
|
||||
namingStrategy: NamingStrategyInterface,
|
||||
entityMetadata: EntityMetadata,
|
||||
userSpecifiedName: string,
|
||||
propertyName: string,
|
||||
propertyPath: string,
|
||||
}) {
|
||||
this.entityMetadata = options.entityMetadata;
|
||||
// this.entityTarget = options.entityMetadata.target;
|
||||
this.propertyName = options.propertyName;
|
||||
// this.name = options.namingStrategy.columnName(options.propertyName, options.userSpecifiedName);
|
||||
|
||||
// return this;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
|
||||
/**
|
||||
* Contains all information about entity's embedded property.
|
||||
*/
|
||||
export class EmbeddedMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this embedded is.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Parent embedded in the case if this embedded inside other embedded.
|
||||
*/
|
||||
parentEmbeddedMetadata: EmbeddedMetadata;
|
||||
|
||||
/**
|
||||
* Property name on which this embedded is attached.
|
||||
*/
|
||||
propertyName: string;
|
||||
|
||||
/**
|
||||
* Columns inside this embed.
|
||||
*/
|
||||
columns: ColumnMetadata[];
|
||||
|
||||
/**
|
||||
* Relations inside this embed.
|
||||
*/
|
||||
relations: RelationMetadata[];
|
||||
|
||||
/**
|
||||
* Nested embeddable in this embeddable (which has current embedded as parent embedded).
|
||||
*/
|
||||
embeddeds: EmbeddedMetadata[];
|
||||
|
||||
/**
|
||||
* Embedded target type.
|
||||
*/
|
||||
type?: Function;
|
||||
|
||||
/**
|
||||
* Indicates if this embedded is in array mode.
|
||||
*
|
||||
* This option works only in monogodb.
|
||||
*/
|
||||
isArray: boolean;
|
||||
|
||||
/**
|
||||
* Prefix of the embedded, used instead of propertyName.
|
||||
* If set to empty string, then prefix is not set at all.
|
||||
*/
|
||||
customPrefix: string|boolean|undefined;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata,
|
||||
columns: ColumnMetadata[],
|
||||
relations: RelationMetadata[],
|
||||
embeddeds: EmbeddedMetadata[],
|
||||
args: EmbeddedMetadataArgs) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.type = args.type ? args.type() : undefined;
|
||||
this.propertyName = args.propertyName;
|
||||
this.isArray = args.isArray;
|
||||
this.customPrefix = args.prefix;
|
||||
this.columns = columns;
|
||||
this.relations = relations;
|
||||
this.embeddeds = embeddeds;
|
||||
this.embeddeds.forEach(embedded => {
|
||||
// embedded.parentEmbeddedMetadata = this;
|
||||
});
|
||||
this.columns.forEach(column => {
|
||||
// column.embeddedMetadata = this;
|
||||
});
|
||||
this.relations.forEach(relation => {
|
||||
// relation.embeddedMetadata = this;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new embedded object.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
create() {
|
||||
return new (this.type as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prefix of the columns.
|
||||
* By default its a property name of the class where this prefix is.
|
||||
* But if custom prefix is set then it takes its value as a prefix.
|
||||
* However if custom prefix is set to empty string prefix to column is not applied at all.
|
||||
*/
|
||||
get prefix(): string {
|
||||
let prefixes: string[] = [];
|
||||
if (this.parentEmbeddedMetadata)
|
||||
prefixes.push(this.parentEmbeddedMetadata.prefix);
|
||||
|
||||
if (this.customPrefix === undefined) {
|
||||
prefixes.push(this.propertyName);
|
||||
|
||||
} else if (typeof this.customPrefix === "string") {
|
||||
prefixes.push(this.customPrefix);
|
||||
}
|
||||
|
||||
return prefixes.join("_"); // todo: use naming strategy instead of "_" !!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of property names of current embed and all its parent embeds.
|
||||
*
|
||||
* example: post[data][information][counters].id where "data", "information" and "counters" are embeds
|
||||
* we need to get value of "id" column from the post real entity object.
|
||||
* this method will return ["data", "information", "counters"]
|
||||
*
|
||||
* @stable just need move to builder process
|
||||
*/
|
||||
get parentPropertyNames(): string[] {
|
||||
return this.parentEmbeddedMetadata ? this.parentEmbeddedMetadata.parentPropertyNames.concat(this.propertyName) : [this.propertyName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns embed metadatas from all levels of the parent tree.
|
||||
*
|
||||
* example: post[data][information][counters].id where "data", "information" and "counters" are embeds
|
||||
* this method will return [embed metadata of data, embed metadata of information, embed metadata of counters]
|
||||
*
|
||||
* @stable just need move to builder process
|
||||
*/
|
||||
get embeddedMetadataTree(): EmbeddedMetadata[] {
|
||||
return [];
|
||||
// return this.parentEmbeddedMetadata ? this.parentEmbeddedMetadata.embeddedMetadataTree.concat(this as any) : [this];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all columns of this embed and all columns from its child embeds.
|
||||
*
|
||||
* @stable just need move to builder process
|
||||
*/
|
||||
get columnsFromTree(): ColumnMetadata[] {
|
||||
return this.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), this.columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all relations of this embed and all relations from its child embeds.
|
||||
*
|
||||
* @stable just need move to builder process
|
||||
*/
|
||||
get relationsFromTree(): RelationMetadata[] {
|
||||
return this.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), this.relations);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,99 +0,0 @@
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {OnDeleteType} from "../metadata/ForeignKeyMetadata";
|
||||
|
||||
/**
|
||||
* Contains all information about entity's foreign key.
|
||||
*/
|
||||
export class ForeignKeyMetadataBuilder {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this foreign key is.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Entity metadata which this foreign key is references.
|
||||
*/
|
||||
referencedEntityMetadata: EntityMetadata;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Readonly Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Array of columns of this foreign key.
|
||||
*/
|
||||
readonly columns: ColumnMetadata[];
|
||||
|
||||
/**
|
||||
* Array of referenced columns.
|
||||
*/
|
||||
readonly referencedColumns: ColumnMetadata[];
|
||||
|
||||
/**
|
||||
* What to do with a relation on deletion of the row containing a foreign key.
|
||||
*/
|
||||
readonly onDelete: OnDeleteType;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata,
|
||||
columns: ColumnMetadata[],
|
||||
referencedEntityMetadata: EntityMetadata,
|
||||
referencedColumns: ColumnMetadata[],
|
||||
onDelete?: OnDeleteType) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.columns = columns;
|
||||
this.referencedEntityMetadata = referencedEntityMetadata;
|
||||
this.referencedColumns = referencedColumns;
|
||||
if (onDelete)
|
||||
this.onDelete = onDelete;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Accessors
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the table name to which this foreign key is applied.
|
||||
*/
|
||||
get tableName() {
|
||||
return this.entityMetadata.tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table name to which this foreign key is referenced.
|
||||
*/
|
||||
get referencedTableName() {
|
||||
return this.referencedEntityMetadata.tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets foreign key name.
|
||||
*/
|
||||
get name() {
|
||||
return (this as any).namingStrategy.foreignKeyName(this.tableName, this.columnNames, this.referencedEntityMetadata.tableName, this.referencedColumnNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets array of column names.
|
||||
*/
|
||||
get columnNames(): string[] {
|
||||
return this.columns.map(column => column.databaseName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets array of referenced column names.
|
||||
*/
|
||||
get referencedColumnNames(): string[] {
|
||||
return this.referencedColumns.map(column => column.databaseName);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,143 +0,0 @@
|
||||
import {IndexMetadataArgs} from "../metadata-args/IndexMetadataArgs";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Index metadata contains all information about table's index.
|
||||
*/
|
||||
export class IndexMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata of the class to which this index is applied.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Indicates if this index must be unique.
|
||||
*/
|
||||
readonly isUnique: boolean;
|
||||
|
||||
/**
|
||||
* Target class to which metadata is applied.
|
||||
*/
|
||||
readonly target?: Function|string;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Composite index name.
|
||||
*/
|
||||
private readonly _name: string|undefined;
|
||||
|
||||
/**
|
||||
* Columns combination to be used as index.
|
||||
*/
|
||||
private readonly _columns: ((object?: any) => (any[]|{ [key: string]: number }))|string[];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, args: IndexMetadataArgs) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.target = args.target;
|
||||
// this._columns = args.columns;
|
||||
this._name = args.name;
|
||||
this.isUnique = args.unique;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets index's name.
|
||||
*/
|
||||
get name() {
|
||||
return (this as any).namingStrategy.indexName(this._name, this.entityMetadata.tableName, this.columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table name on which index is applied.
|
||||
*/
|
||||
get tableName() {
|
||||
return this.entityMetadata.tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the column names which are in this index.
|
||||
*/
|
||||
get columns(): string[] {
|
||||
|
||||
// if columns already an array of string then simply return it
|
||||
let columnPropertyNames: string[] = [];
|
||||
if (this._columns instanceof Array) {
|
||||
columnPropertyNames = this._columns;
|
||||
} else {
|
||||
// if columns is a function that returns array of field names then execute it and get columns names from it
|
||||
const columnsFnResult = this._columns(this.entityMetadata.propertiesMap);
|
||||
const columnsNamesFromFnResult = columnsFnResult instanceof Array ? columnsFnResult : Object.keys(columnsFnResult);
|
||||
columnPropertyNames = columnsNamesFromFnResult.map((i: any) => String(i));
|
||||
}
|
||||
|
||||
const columns = this.entityMetadata.columns.filter(column => columnPropertyNames.indexOf(column.propertyPath) !== -1);
|
||||
this.entityMetadata.relations
|
||||
.filter(relation => relation.isWithJoinColumn && columnPropertyNames.indexOf(relation.propertyName) !== -1)
|
||||
.forEach(relation => columns.push(...relation.joinColumns));
|
||||
|
||||
// todo: better to extract all validation into single place if possible
|
||||
const missingColumnNames = columnPropertyNames.filter(columnPropertyName => {
|
||||
return !this.entityMetadata.columns.find(column => column.propertyPath === columnPropertyName) &&
|
||||
!this.entityMetadata.relations.find(relation => relation.isWithJoinColumn && columnPropertyNames.indexOf(relation.propertyName) !== -1);
|
||||
});
|
||||
if (missingColumnNames.length > 0) {
|
||||
// console.log(this.entityMetadata.columns);
|
||||
throw new Error(`Index ${this._name ? "\"" + this._name + "\" " : ""}contains columns that are missing in the entity: ` + missingColumnNames.join(", "));
|
||||
}
|
||||
|
||||
return columns.map(column => column.databaseName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds columns as a map of values where column name is key of object and value is a value provided by
|
||||
* function or default value given to this function.
|
||||
*/
|
||||
buildColumnsAsMap(defaultValue = 0): { [key: string]: number } {
|
||||
|
||||
const map: { [key: string]: number } = {};
|
||||
|
||||
// if columns already an array of string then simply create a map from it
|
||||
if (this._columns instanceof Array) {
|
||||
this._columns.forEach(columnName => map[columnName] = defaultValue);
|
||||
|
||||
} else {
|
||||
// if columns is a function that returns array of field names then execute it and get columns names from it
|
||||
const columnsFnResult = this._columns(this.entityMetadata.propertiesMap);
|
||||
if (columnsFnResult instanceof Array) {
|
||||
columnsFnResult.forEach(columnName => map[columnName] = defaultValue);
|
||||
} else {
|
||||
Object.keys(columnsFnResult).forEach(columnName => map[columnName] = columnsFnResult[columnName]);
|
||||
}
|
||||
}
|
||||
|
||||
// replace each propertyNames with column names
|
||||
return Object.keys(map).reduce((updatedMap, key) => {
|
||||
const column = this.entityMetadata.columns.find(column => column.propertyName === key);
|
||||
if (!column)
|
||||
throw new Error(`Index ${this._name ? "\"" + this._name + "\" " : ""}contains columns that are missing in the entity: ${key}`);
|
||||
|
||||
updatedMap[column.databaseName] = map[key];
|
||||
return updatedMap;
|
||||
}, {} as { [key: string]: number });
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
import {RelationCountMetadataArgs} from "../metadata-args/RelationCountMetadataArgs";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
|
||||
/**
|
||||
* Contains all information about entity's relation count.
|
||||
*/
|
||||
export class RelationCountMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this column metadata is.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Relation name which need to count.
|
||||
*/
|
||||
readonly relationNameOrFactory: string|((object: any) => any);
|
||||
|
||||
/**
|
||||
* Target class to which metadata is applied.
|
||||
*/
|
||||
readonly target: Function|string;
|
||||
|
||||
/**
|
||||
* Target's property name to which this metadata is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Alias of the joined (destination) table.
|
||||
*/
|
||||
readonly alias?: string;
|
||||
|
||||
/**
|
||||
* Extra condition applied to "ON" section of join.
|
||||
*/
|
||||
readonly queryBuilderFactory?: (qb: QueryBuilder<any>) => QueryBuilder<any>;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, args: RelationCountMetadataArgs) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.target = args.target;
|
||||
this.propertyName = args.propertyName;
|
||||
this.relationNameOrFactory = args.relation;
|
||||
this.alias = args.alias;
|
||||
this.queryBuilderFactory = args.queryBuilderFactory;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Relation which need to count.
|
||||
*/
|
||||
get relation(): RelationMetadata {
|
||||
const propertyName = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
|
||||
const relation = this.entityMetadata.relations.find(relation => relation.propertyName === propertyName);
|
||||
if (!relation)
|
||||
throw new Error(`Cannot find relation ${propertyName}. Wrong relation specified for @RelationCount decorator.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
import {RelationIdMetadataArgs} from "../metadata-args/RelationIdMetadataArgs";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
|
||||
/**
|
||||
* Contains all information about entity's relation count.
|
||||
*/
|
||||
export class RelationIdMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this column metadata is.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Relation name which need to count.
|
||||
*/
|
||||
readonly relationNameOrFactory: string|((object: any) => any);
|
||||
|
||||
/**
|
||||
* Target class to which metadata is applied.
|
||||
*/
|
||||
readonly target: Function|string;
|
||||
|
||||
/**
|
||||
* Target's property name to which this metadata is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Alias of the joined (destination) table.
|
||||
*/
|
||||
readonly alias?: string;
|
||||
|
||||
/**
|
||||
* Extra condition applied to "ON" section of join.
|
||||
*/
|
||||
readonly queryBuilderFactory?: (qb: QueryBuilder<any>) => QueryBuilder<any>;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, args: RelationIdMetadataArgs) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.target = args.target;
|
||||
this.propertyName = args.propertyName;
|
||||
this.relationNameOrFactory = args.relation;
|
||||
this.alias = args.alias;
|
||||
this.queryBuilderFactory = args.queryBuilderFactory;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Relation which need to count.
|
||||
*/
|
||||
get relation(): RelationMetadata {
|
||||
const propertyName = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
|
||||
const relation = this.entityMetadata.relations.find(relation => relation.propertyName === propertyName);
|
||||
if (!relation)
|
||||
throw new Error(`Cannot find relation ${propertyName}. Wrong relation specified for @RelationId decorator.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,9 +2,6 @@ import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterfac
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
|
||||
|
||||
// cases it should cover:
|
||||
|
||||
@ -1,370 +0,0 @@
|
||||
import {PropertyTypeInFunction, RelationMetadata, RelationTypeInFunction} from "../metadata/RelationMetadata";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
import {ForeignKeyMetadata, OnDeleteType} from "../metadata/ForeignKeyMetadata";
|
||||
import {RelationType} from "../metadata/types/RelationTypes";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
|
||||
/**
|
||||
* Contains all information about some entity's relation.
|
||||
*/
|
||||
export class RelationMetadataBuilder {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Its own entity metadata.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Embedded metadata where this relation is.
|
||||
* If this relation is not in embed then this property value is undefined.
|
||||
*/
|
||||
embeddedMetadata: EmbeddedMetadata;
|
||||
|
||||
/**
|
||||
* Related entity metadata.
|
||||
*/
|
||||
inverseEntityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Junction entity metadata.
|
||||
*/
|
||||
junctionEntityMetadata: EntityMetadata;
|
||||
|
||||
foreignKeys: ForeignKeyMetadata[] = [];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Target class to which metadata is applied.
|
||||
*/
|
||||
target: Function|string;
|
||||
|
||||
/**
|
||||
* Gets relation's entity target.
|
||||
* Original target returns target of the class where relation is.
|
||||
* This class can be an abstract class, but relation even is from that class,
|
||||
* but its more related to a specific entity. That's why we need this field.
|
||||
*
|
||||
* Note: this property is available only after relation metadata complete build
|
||||
*/
|
||||
entityTarget: Function|string;
|
||||
|
||||
/**
|
||||
* Target's property name to which this metadata is applied.
|
||||
*/
|
||||
propertyName: string;
|
||||
|
||||
/**
|
||||
* Indicates if this is a parent (can be only many-to-one relation) relation in the tree tables.
|
||||
*/
|
||||
isTreeParent: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates if this is a children (can be only one-to-many relation) relation in the tree tables.
|
||||
*/
|
||||
isTreeChildren: boolean = false;
|
||||
|
||||
/**
|
||||
* Relation type.
|
||||
*/
|
||||
relationType: RelationType;
|
||||
|
||||
/**
|
||||
* Indicates if this relation will be a primary key.
|
||||
* Can be used only for many-to-one and owner one-to-one relations.
|
||||
*/
|
||||
isPrimary: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this relation will be lazily loaded.
|
||||
*/
|
||||
isLazy: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be inserted to the db.
|
||||
*/
|
||||
isCascadeInsert: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be updated in the db.
|
||||
*/
|
||||
isCascadeUpdate: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be remove from the db.
|
||||
*/
|
||||
isCascadeRemove: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if relation column value can be nullable or not.
|
||||
*/
|
||||
isNullable: boolean = true;
|
||||
|
||||
/**
|
||||
* What to do with a relation on deletion of the row containing a foreign key.
|
||||
*/
|
||||
onDelete: OnDeleteType;
|
||||
|
||||
/**
|
||||
* The real reflected property type.
|
||||
*/
|
||||
// propertyType: any;
|
||||
|
||||
/**
|
||||
* Join table name.
|
||||
*/
|
||||
joinTableName: string;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The type of the field.
|
||||
*/
|
||||
private _type: RelationTypeInFunction;
|
||||
|
||||
/**
|
||||
* Inverse side of the relation.
|
||||
*/
|
||||
private _inverseSideProperty: PropertyTypeInFunction<any>;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
private relationMetadata: RelationMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor() {
|
||||
this.relationMetadata = new RelationMetadata({} as any);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets full path to this column property (including relation name).
|
||||
* Full path is relevant when column is used in embeds (one or multiple nested).
|
||||
* For example it will return "counters.subcounters.likes".
|
||||
* If property is not in embeds then it returns just property name of the column.
|
||||
*/
|
||||
get propertyPath(): string {
|
||||
if (!this.embeddedMetadata || !this.embeddedMetadata.parentPropertyNames.length)
|
||||
return this.propertyName;
|
||||
|
||||
return this.embeddedMetadata.parentPropertyNames.join(".") + "." + this.propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table columns.
|
||||
*/
|
||||
get joinColumns(): ColumnMetadata[] {
|
||||
if (!this.isOwning)
|
||||
throw new Error(`Inverse join columns are only supported from owning side`);
|
||||
|
||||
return this.foreignKeys[0].columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table columns.
|
||||
*/
|
||||
get inverseJoinColumns(): ColumnMetadata[] {
|
||||
if (!this.isOwning)
|
||||
throw new Error(`Inverse join columns are only supported from owning side`);
|
||||
|
||||
if (!this.isManyToMany)
|
||||
throw new Error(`Inverse join columns are not supported by non-many-to-many relations`);
|
||||
|
||||
return this.foreignKeys[1].columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property's type to which this relation is applied.
|
||||
*/
|
||||
get type(): Function|string { // todo: when this can be a string?
|
||||
return this._type instanceof Function ? (this._type as () => any)() : this._type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this side is an owner of this relation.
|
||||
*/
|
||||
get isOwning() {
|
||||
return !!(this.isManyToOne ||
|
||||
(this.isManyToMany && this.foreignKeys.length > 0) ||
|
||||
(this.isOneToOne && this.foreignKeys.length > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "one-to-one".
|
||||
*/
|
||||
get isOneToOne(): boolean {
|
||||
return this.relationType === "one-to-one";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation is owner side of the "one-to-one" relation.
|
||||
* Owner side means this side of relation has a join column in the table.
|
||||
*/
|
||||
get isOneToOneOwner(): boolean {
|
||||
return this.isOneToOne && this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation has a join column (e.g. is it many-to-one or one-to-one owner side).
|
||||
*/
|
||||
get isWithJoinColumn(): boolean {
|
||||
return this.isManyToOne || this.isOneToOneOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation is NOT owner side of the "one-to-one" relation.
|
||||
* NOT owner side means this side of relation does not have a join column in the table.
|
||||
*/
|
||||
get isOneToOneNotOwner(): boolean {
|
||||
return this.isOneToOne && !this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "one-to-many".
|
||||
*/
|
||||
get isOneToMany(): boolean {
|
||||
return this.relationType === "one-to-many";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-one".
|
||||
*/
|
||||
get isManyToOne(): boolean {
|
||||
return this.relationType === "many-to-one";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-many".
|
||||
*/
|
||||
get isManyToMany(): boolean {
|
||||
return this.relationType === "many-to-many";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-many", and is owner side of the relationship.
|
||||
* Owner side means this side of relation has a join table.
|
||||
*/
|
||||
get isManyToManyOwner(): boolean {
|
||||
return this.isManyToMany && this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-many", and is NOT owner side of the relationship.
|
||||
* Not owner side means this side of relation does not have a join table.
|
||||
*/
|
||||
get isManyToManyNotOwner(): boolean {
|
||||
return this.isManyToMany && !this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if inverse side is specified by a relation.
|
||||
*/
|
||||
get hasInverseSide(): boolean {
|
||||
return this.inverseEntityMetadata && this.inverseEntityMetadata.hasRelationWithPropertyPath(this.inverseSidePropertyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property name of the inverse side of the relation.
|
||||
*/
|
||||
get inverseSidePropertyPath(): string { // todo: should be called inverseSidePropertyName ?
|
||||
|
||||
if (this._inverseSideProperty) {
|
||||
return this.computeInverseSide(this._inverseSideProperty);
|
||||
|
||||
} else if (this.isTreeParent && this.entityMetadata.treeChildrenRelation) {
|
||||
return this.entityMetadata.treeChildrenRelation.propertyName;
|
||||
|
||||
} else if (this.isTreeChildren && this.entityMetadata.treeParentRelation) {
|
||||
return this.entityMetadata.treeParentRelation.propertyName;
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relation metadata of the inverse side of this relation.
|
||||
*/
|
||||
get inverseRelation(): RelationMetadata {
|
||||
const relation = this.inverseEntityMetadata.findRelationWithPropertyPath(this.inverseSidePropertyPath);
|
||||
if (!relation)
|
||||
throw new Error(`Inverse side was not found in the relation ${this.entityMetadata.name}#${this.inverseSidePropertyPath}`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
build(): RelationMetadata {
|
||||
const metadata = this.relationMetadata;
|
||||
metadata.relationType = this.relationType;
|
||||
metadata.entityMetadata = this.entityMetadata;
|
||||
metadata.inverseEntityMetadata = this.inverseEntityMetadata;
|
||||
metadata.junctionEntityMetadata = this.junctionEntityMetadata;
|
||||
metadata.embeddedMetadata = this.embeddedMetadata;
|
||||
metadata.foreignKeys = this.foreignKeys;
|
||||
// metadata.entityTarget = this.entityMetadata.target;
|
||||
metadata.propertyPath = this.propertyPath;
|
||||
metadata.joinColumns = this.joinColumns;
|
||||
metadata.inverseJoinColumns = this.inverseJoinColumns;
|
||||
metadata.type = this.type;
|
||||
metadata.isOwning = this.isOwning;
|
||||
metadata.isOneToOne = this.isOneToOne;
|
||||
metadata.isOneToOneOwner = this.isOneToOneOwner;
|
||||
metadata.isWithJoinColumn = this.isWithJoinColumn;
|
||||
metadata.isOneToMany = this.isOneToMany;
|
||||
metadata.isManyToOne = this.isManyToOne;
|
||||
metadata.isManyToMany = this.isManyToMany;
|
||||
metadata.isManyToManyOwner = this.isManyToManyOwner;
|
||||
metadata.hasInverseSide = this.hasInverseSide;
|
||||
metadata.propertyPath = this.propertyPath;
|
||||
metadata.inverseSidePropertyPath = this.inverseSidePropertyPath;
|
||||
metadata.inverseRelation = this.inverseRelation;
|
||||
|
||||
if (this.junctionEntityMetadata) {
|
||||
metadata.junctionEntityMetadata = this.junctionEntityMetadata;
|
||||
metadata.joinTableName = this.junctionEntityMetadata.tableName;
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Inverse side set in the relation can be either string - property name of the column on inverse side,
|
||||
* either can be a function that accepts a map of properties with the object and returns one of them.
|
||||
* Second approach is used to achieve type-safety.
|
||||
*/
|
||||
protected computeInverseSide(inverseSide: PropertyTypeInFunction<any>): string {
|
||||
const ownerEntityPropertiesMap = this.inverseEntityMetadata.propertiesMap;
|
||||
if (typeof inverseSide === "function")
|
||||
return (<Function> inverseSide)(ownerEntityPropertiesMap);
|
||||
if (typeof inverseSide === "string")
|
||||
return <string> inverseSide;
|
||||
|
||||
// throw new Error("Cannot compute inverse side of the relation");
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user