mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
entity metadata refactoring - phase I
This commit is contained in:
parent
05f8c25869
commit
280fcd5af6
@ -19,6 +19,8 @@ each for its own `findOne*` or `find*` methods
|
||||
* `QueryBuilder#setFirstResult` has been renamed to `QueryBuilder#skip`
|
||||
* `QueryBuilder#setMaxResults` has been renamed to `QueryBuilder#take`
|
||||
* renamed `entityManager` to `manager`
|
||||
* `@AbstractEntity` is deprecated. Now there is no need to mark class with a decorator, it can extend any class with columns
|
||||
*
|
||||
|
||||
### NEW FEATURES
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import {InheritanceMetadataArgs} from "./InheritanceMetadataArgs";
|
||||
import {DiscriminatorValueMetadataArgs} from "./DiscriminatorValueMetadataArgs";
|
||||
import {EntityRepositoryMetadataArgs} from "./EntityRepositoryMetadataArgs";
|
||||
import {TransactionEntityMetadataArgs} from "./TransactionEntityMetadataArgs";
|
||||
import {MetadataArgsUtils} from "./MetadataArgsUtils";
|
||||
|
||||
/**
|
||||
* Storage all metadatas of all available types: tables, fields, subscribers, relations, etc.
|
||||
@ -57,10 +58,14 @@ export class MetadataArgsStorage {
|
||||
* Gets merged (with all abstract classes) table metadatas for the given classes.
|
||||
*/
|
||||
getMergedTableMetadatas(classes?: Function[]) {
|
||||
const allTableMetadataArgs = classes ? this.tables.filterByTargets(classes) : this.tables;
|
||||
const tableMetadatas = allTableMetadataArgs.filter(table => table.type === "regular" || table.type === "closure" || table.type === "class-table-child");
|
||||
|
||||
return tableMetadatas.toArray().map(tableMetadata => {
|
||||
// filter out tables by a given allowed classes
|
||||
const allTableMetadataArgs = MetadataArgsUtils.filterByTargetClasses(this.tables.toArray(), classes);
|
||||
|
||||
// filter out table metadata args for those we really create entity metadatas and tables in the db
|
||||
const realTables = allTableMetadataArgs.filter(table => table.type === "regular" || table.type === "closure" || table.type === "class-table-child");
|
||||
|
||||
return realTables.map(tableMetadata => {
|
||||
return this.mergeWithAbstract(this.tables, tableMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
34
src/metadata-args/MetadataArgsUtils.ts
Normal file
34
src/metadata-args/MetadataArgsUtils.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Metadata args utility functions.
|
||||
*/
|
||||
export class MetadataArgsUtils {
|
||||
|
||||
/**
|
||||
* Gets given's entity all inherited classes.
|
||||
* Gives in order from parents to children.
|
||||
* For example Post extends ContentModel which extends Unit it will give
|
||||
* [Unit, ContentModel, Post]
|
||||
*/
|
||||
static getInheritanceTree(entity: Function): Function[] {
|
||||
const tree: Function[] = [entity];
|
||||
const getPrototypeOf = (object: Function): void => {
|
||||
const proto = Object.getPrototypeOf(object);
|
||||
if (proto && proto.name) {
|
||||
tree.push(proto);
|
||||
getPrototypeOf(proto);
|
||||
}
|
||||
};
|
||||
getPrototypeOf(entity);
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters given array of targets by a given classes.
|
||||
* If classes are not given, then it returns array itself.
|
||||
*/
|
||||
static filterByTargetClasses<T extends { target?: any }>(array: T[], classes?: Function[]): T[] {
|
||||
if (!classes) return array;
|
||||
return array.filter(item => item.target && classes.indexOf(item.target) !== -1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -45,8 +45,7 @@ export class EntityMetadataBuilder {
|
||||
/**
|
||||
* Builds a complete metadata aggregations for the given entity classes.
|
||||
*/
|
||||
build(
|
||||
entityClasses?: Function[]): EntityMetadata[] {
|
||||
build(entityClasses?: Function[]): EntityMetadata[] {
|
||||
const embeddableMergedArgs = this.metadataArgsStorage.getMergedEmbeddableTableMetadatas(entityClasses);
|
||||
const entityMetadatas: EntityMetadata[] = [];
|
||||
const allMergedArgs = this.metadataArgsStorage.getMergedTableMetadatas(entityClasses);
|
||||
@ -101,8 +100,6 @@ export class EntityMetadataBuilder {
|
||||
tableName: argsForTable.name,
|
||||
tableType: argsForTable.type,
|
||||
orderBy: argsForTable.orderBy,
|
||||
engine: argsForTable.engine,
|
||||
skipSchemaSync: argsForTable.skipSchemaSync,
|
||||
columnMetadatas: columns,
|
||||
relationMetadatas: relations,
|
||||
relationIdMetadatas: relationIds,
|
||||
@ -116,6 +113,8 @@ export class EntityMetadataBuilder {
|
||||
target: tableArgs.target,
|
||||
tableType: argsForTable.type,
|
||||
userSpecifiedTableName: argsForTable.name,
|
||||
engine: argsForTable.engine,
|
||||
skipSchemaSync: argsForTable.skipSchemaSync,
|
||||
});
|
||||
|
||||
entityMetadatas.push(entityMetadata);
|
||||
@ -590,7 +589,9 @@ export class EntityMetadataBuilder {
|
||||
target?: Function|string,
|
||||
tableType: TableType,
|
||||
userSpecifiedTableName?: string,
|
||||
closureOwnerTableName?: string
|
||||
closureOwnerTableName?: string,
|
||||
engine?: string,
|
||||
skipSchemaSync?: boolean,
|
||||
}) {
|
||||
|
||||
let target = options.target;
|
||||
@ -613,6 +614,8 @@ export class EntityMetadataBuilder {
|
||||
metadata.tableNameWithoutPrefix = tableNameWithoutPrefix;
|
||||
metadata.tableName = tableName;
|
||||
metadata.name = targetName ? targetName : tableName;
|
||||
metadata.engine = options.engine;
|
||||
metadata.skipSchemaSync = options.skipSchemaSync || false;
|
||||
}
|
||||
|
||||
/*protected createEntityMetadata(tableArgs: any, argsForTable: any, ): EntityMetadata {
|
||||
|
||||
@ -44,19 +44,6 @@ export class EntityMetadata {
|
||||
*/
|
||||
readonly namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Target class to which this entity metadata is bind.
|
||||
* Note, that when using table inheritance patterns target can be different rather then table's target.
|
||||
* For virtual tables which lack of real entity (like junction tables) target is equal to their table name.
|
||||
*/
|
||||
target: Function|string;
|
||||
|
||||
/**
|
||||
* Indicates if this entity metadata of a junction table, or not.
|
||||
* Junction table is a table created by many-to-many relationship.
|
||||
*/
|
||||
readonly isJunction: boolean;
|
||||
|
||||
/**
|
||||
* Specifies a default order by used for queries from this table when no explicit order by is specified.
|
||||
*/
|
||||
@ -104,26 +91,6 @@ export class EntityMetadata {
|
||||
*/
|
||||
readonly discriminatorValue?: string;
|
||||
|
||||
/**
|
||||
* Global tables prefix. Customer can set a global table prefix for all tables in the database.
|
||||
*/
|
||||
readonly tablesPrefix?: string;
|
||||
|
||||
/**
|
||||
* Table's database engine type (like "InnoDB", "MyISAM", etc).
|
||||
*/
|
||||
readonly engine?: string;
|
||||
|
||||
/**
|
||||
* Whether table must be synced during schema build or not
|
||||
*/
|
||||
readonly skipSchemaSync?: boolean;
|
||||
|
||||
/**
|
||||
* Table type. Tables can be abstract, closure, junction, embedded, etc.
|
||||
*/
|
||||
tableType: TableType = "regular";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -141,7 +108,6 @@ export class EntityMetadata {
|
||||
private lazyRelationsWrapper: LazyRelationsWrapper) {
|
||||
this.target = args.target;
|
||||
this.isJunction = args.junction;
|
||||
this.tablesPrefix = args.tablesPrefix;
|
||||
this.namingStrategy = args.namingStrategy;
|
||||
this.tableType = args.tableType;
|
||||
this._columns = args.columnMetadatas || [];
|
||||
@ -153,8 +119,6 @@ export class EntityMetadata {
|
||||
this.embeddeds = args.embeddedMetadatas || [];
|
||||
this.discriminatorValue = args.discriminatorValue;
|
||||
this.inheritanceType = args.inheritanceType;
|
||||
this.engine = args.engine;
|
||||
this.skipSchemaSync = args.skipSchemaSync;
|
||||
this._orderBy = args.orderBy;
|
||||
this._columns.forEach(column => column.entityMetadata = this);
|
||||
this._relations.forEach(relation => relation.entityMetadata = this);
|
||||
@ -177,6 +141,26 @@ export class EntityMetadata {
|
||||
// Accessors
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Table type. Tables can be abstract, closure, junction, embedded, etc.
|
||||
*/
|
||||
tableType: TableType = "regular";
|
||||
|
||||
/**
|
||||
* Target class to which this entity metadata is bind.
|
||||
* Note, that when using table inheritance patterns target can be different rather then table's target.
|
||||
* For virtual tables which lack of real entity (like junction tables) target is equal to their table name.
|
||||
*/
|
||||
target: Function|string;
|
||||
|
||||
/**
|
||||
* Indicates if this entity metadata of a junction table, or not.
|
||||
* Junction table is a table created by many-to-many relationship.
|
||||
*
|
||||
* Its also possible to understand if entity is junction via tableType.
|
||||
*/
|
||||
isJunction: boolean;
|
||||
|
||||
/**
|
||||
* Entity's name.
|
||||
* Equal to entity target class's name if target is set to table.
|
||||
@ -221,6 +205,18 @@ export class EntityMetadata {
|
||||
*/
|
||||
tableNameWithoutPrefix: string;
|
||||
|
||||
/**
|
||||
* Indicates if schema sync is skipped for this entity.
|
||||
*/
|
||||
skipSchemaSync: boolean;
|
||||
|
||||
/**
|
||||
* Table's database engine type (like "InnoDB", "MyISAM", etc).
|
||||
*/
|
||||
engine?: string;
|
||||
|
||||
// propertiesMap: ObjectLiteral;
|
||||
|
||||
/**
|
||||
* Specifies a default order by used for queries from this table when no explicit order by is specified.
|
||||
* If default order by was not set, then returns undefined.
|
||||
|
||||
@ -6,6 +6,8 @@ export type TableType = "regular"|"abstract"|"junction"|"closure"|"closure-junct
|
||||
|
||||
/**
|
||||
* Represents a class with constants - list of all possible table types.
|
||||
*
|
||||
* todo: remove if only regular table will left here
|
||||
*/
|
||||
export class TableTypes {
|
||||
|
||||
@ -17,37 +19,52 @@ export class TableTypes {
|
||||
/**
|
||||
* This type is for the tables that does not exist in the database,
|
||||
* but provide columns and relations for the tables of the child classes who inherit them.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
static ABSTRACT: TableType = "abstract";
|
||||
|
||||
/**
|
||||
* Junction table is a table automatically created by many-to-many relationship.
|
||||
*
|
||||
* todo: remove and isJunction condition is enough in entity metadata?
|
||||
*/
|
||||
static JUNCTION: TableType = "junction";
|
||||
|
||||
/**
|
||||
* Closure table is one of the tree-specific tables that supports closure database pattern.
|
||||
*
|
||||
* todo: maybe we can determine if it is closure if it has some closure-specific decorator?
|
||||
* todo: or if its not possible then maybe create a separate decorator for closure?
|
||||
*/
|
||||
static CLOSURE: TableType = "closure";
|
||||
|
||||
/**
|
||||
* This type is for tables that contain junction metadata of the closure tables.
|
||||
*
|
||||
* todo: remove and isClosureJunction condition is enough in entity metadata?
|
||||
*/
|
||||
static CLOSURE_JUNCTION: TableType = "closure-junction";
|
||||
|
||||
/**
|
||||
* Embeddable tables are not stored in the database as separate tables.
|
||||
* Instead their columns are embed into tables who owns them.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
static EMBEDDABLE: TableType = "embeddable";
|
||||
|
||||
/**
|
||||
* Special table type for tables that are mapped into single table using Single Table Inheritance pattern.
|
||||
*
|
||||
* todo: create separate decorators?
|
||||
*/
|
||||
static SINGLE_TABLE_CHILD: TableType = "single-table-child";
|
||||
|
||||
/**
|
||||
* Special table type for tables that are mapped into multiple tables using Class Table Inheritance pattern.
|
||||
*
|
||||
* todo: create separate decorators?
|
||||
*/
|
||||
static CLASS_TABLE_CHILD: TableType = "class-table-child";
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {Unit} from "./Unit";
|
||||
|
||||
export class ContentModule extends Unit {
|
||||
|
||||
@Column()
|
||||
tag: string;
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {ContentModule} from "./ContentModule";
|
||||
|
||||
@Entity()
|
||||
export class Post extends ContentModule {
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
|
||||
export class Unit {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
type: string;
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
import "reflect-metadata";
|
||||
import {Post} from "./entity/Post";
|
||||
import {ContentModule} from "./entity/ContentModule";
|
||||
import {Unit} from "./entity/Unit";
|
||||
import {MetadataArgsUtils} from "../../../../src/metadata-args/MetadataArgsUtils";
|
||||
|
||||
describe("metadata builder > MetadataArgsUtils", () => {
|
||||
|
||||
it("getInheritanceTree", () => {
|
||||
const inheritanceTree = MetadataArgsUtils.getInheritanceTree(Post);
|
||||
inheritanceTree.should.be.eql([
|
||||
Post,
|
||||
ContentModule,
|
||||
Unit,
|
||||
]);
|
||||
});
|
||||
|
||||
it("filterByTargetClasses", () => {
|
||||
MetadataArgsUtils.filterByTargetClasses([
|
||||
{ },
|
||||
{ target: undefined },
|
||||
{ target: null },
|
||||
{ target: 1 },
|
||||
{ target: "" },
|
||||
{ target: Post },
|
||||
{ target: ContentModule },
|
||||
{ target: Unit },
|
||||
], [Post, Unit]).should.be.eql([
|
||||
{ target: Post },
|
||||
{ target: Unit },
|
||||
]);
|
||||
|
||||
MetadataArgsUtils.filterByTargetClasses([
|
||||
{ },
|
||||
{ target: undefined },
|
||||
{ target: null },
|
||||
{ target: 1 },
|
||||
{ target: "" },
|
||||
{ target: ContentModule },
|
||||
{ target: Unit },
|
||||
], [Post, Unit]).should.be.eql([
|
||||
{ target: Unit },
|
||||
]);
|
||||
|
||||
MetadataArgsUtils.filterByTargetClasses([
|
||||
{ },
|
||||
{ target: undefined },
|
||||
{ target: null },
|
||||
{ target: 1 },
|
||||
{ target: "" },
|
||||
{ target: ContentModule },
|
||||
{ target: Post },
|
||||
{ target: Unit },
|
||||
], [Post, Unit, ContentModule]).should.be.eql([
|
||||
{ target: ContentModule },
|
||||
{ target: Post },
|
||||
{ target: Unit },
|
||||
]);
|
||||
|
||||
MetadataArgsUtils.filterByTargetClasses([
|
||||
], [Post, Unit, ContentModule]).should.be.eql([
|
||||
]);
|
||||
|
||||
MetadataArgsUtils.filterByTargetClasses([
|
||||
{ },
|
||||
{ target: undefined },
|
||||
{ target: null },
|
||||
{ target: 1 },
|
||||
{ target: "" },
|
||||
{ target: ContentModule },
|
||||
{ target: Post },
|
||||
{ target: Unit },
|
||||
]).should.be.eql([
|
||||
{ },
|
||||
{ target: undefined },
|
||||
{ target: null },
|
||||
{ target: 1 },
|
||||
{ target: "" },
|
||||
{ target: ContentModule },
|
||||
{ target: Post },
|
||||
{ target: Unit },
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user