diff --git a/sample/sample28-single-table-inheritance/app.ts b/sample/sample28-single-table-inheritance/app.ts new file mode 100644 index 000000000..e69de29bb diff --git a/sample/sample28-single-table-inheritance/entity/Employee.ts b/sample/sample28-single-table-inheritance/entity/Employee.ts new file mode 100644 index 000000000..5364cca7f --- /dev/null +++ b/sample/sample28-single-table-inheritance/entity/Employee.ts @@ -0,0 +1,12 @@ +import {Column} from "../../../src/decorator/columns/Column"; +import {Table} from "../../../src/decorator/tables/Table"; +import {Person} from "./Person"; + +@Table() +// @DiscriminatorValue("employee") +export class Employee extends Person { + + @Column() + salary: number; + +} \ No newline at end of file diff --git a/sample/sample28-single-table-inheritance/entity/Homesitter.ts b/sample/sample28-single-table-inheritance/entity/Homesitter.ts new file mode 100644 index 000000000..483d9e214 --- /dev/null +++ b/sample/sample28-single-table-inheritance/entity/Homesitter.ts @@ -0,0 +1,13 @@ +import {Column} from "../../../src/decorator/columns/Column"; +import {Person} from "./Person"; +import {Table} from "../../../src/decorator/tables/Table"; +import {DiscriminatorName} from "../../../src/decorator/DiscriminatorValue"; + +@Table() +@DiscriminatorName("homesitter") // can be omitted +export class Homesitter extends Person { + + @Column() + numberOfKids: number; + +} \ No newline at end of file diff --git a/sample/sample28-single-table-inheritance/entity/Person.ts b/sample/sample28-single-table-inheritance/entity/Person.ts new file mode 100644 index 000000000..8966ec007 --- /dev/null +++ b/sample/sample28-single-table-inheritance/entity/Person.ts @@ -0,0 +1,17 @@ +import {AbstractTable} from "../../../src/decorator/tables/AbstractTable"; +import {Column} from "../../../src/decorator/columns/Column"; +import {TableInheritance} from "../../../src/decorator/TableInheritance"; +import {DiscriminatorColumn} from "../../../src/decorator/columns/DiscriminatorColumn"; + +@AbstractTable() +@TableInheritance("single-table") // also can be a class-table +@DiscriminatorColumn({ name: "type", type: "string"}) +export abstract class Person { + + @Column() + firstName: string; + + @Column() + lastName: string; + +} \ No newline at end of file diff --git a/sample/sample28-single-table-inheritance/entity/Student.ts b/sample/sample28-single-table-inheritance/entity/Student.ts new file mode 100644 index 000000000..cdc5c9c81 --- /dev/null +++ b/sample/sample28-single-table-inheritance/entity/Student.ts @@ -0,0 +1,13 @@ +import {Column} from "../../../src/decorator/columns/Column"; +import {Person} from "./Person"; +import {Table} from "../../../src/decorator/tables/Table"; +import {DiscriminatorName} from "../../../src/decorator/DiscriminatorValue"; + +@Table() +@DiscriminatorName("student") +export class Student extends Person { + + @Column() + faculty: string; + +} \ No newline at end of file diff --git a/src/decorator/DiscriminatorValue.ts b/src/decorator/DiscriminatorValue.ts new file mode 100644 index 000000000..2443a8537 --- /dev/null +++ b/src/decorator/DiscriminatorValue.ts @@ -0,0 +1,14 @@ +import {getMetadataArgsStorage} from "../index"; +import {DiscriminatorNameMetadataArgs} from "../metadata-args/DiscriminatorNameMetadataArgs"; + +/** + */ +export function DiscriminatorName(name: string): Function { + return function (target: Function) { + const args: DiscriminatorNameMetadataArgs = { + target: target, + name: name + }; + getMetadataArgsStorage().discriminatorNames.add(args); + }; +} diff --git a/src/decorator/TableInheritance.ts b/src/decorator/TableInheritance.ts new file mode 100644 index 000000000..04a4a107b --- /dev/null +++ b/src/decorator/TableInheritance.ts @@ -0,0 +1,14 @@ +import {getMetadataArgsStorage} from "../index"; +import {InheritanceMetadataArgs} from "../metadata-args/InheritanceMetadataArgs"; + +/** + */ +export function TableInheritance(type: "single-table"|"class-table") { + return function (object: Object) { + const args: InheritanceMetadataArgs = { + target: object.constructor, + type: type + }; + getMetadataArgsStorage().inheritances.add(args); + }; +} \ No newline at end of file diff --git a/src/decorator/columns/DiscriminatorColumn.ts b/src/decorator/columns/DiscriminatorColumn.ts new file mode 100644 index 000000000..90a88f407 --- /dev/null +++ b/src/decorator/columns/DiscriminatorColumn.ts @@ -0,0 +1,30 @@ +import {ColumnOptions} from "../options/ColumnOptions"; +import {ColumnTypes, ColumnType} from "../../metadata/types/ColumnTypes"; +import {getMetadataArgsStorage} from "../../index"; +import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; + +/** + */ +export function DiscriminatorColumn(discriminatorOptions: { name: string, type: ColumnType }): Function { + return function (object: Object, propertyName: string) { + + const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName)); + + // if column options are not given then create a new empty options + const options: ColumnOptions = { + name: discriminatorOptions.name, + type: discriminatorOptions.type + }; + + // create and register a new column metadata + const args: ColumnMetadataArgs = { + target: object.constructor, + propertyName: propertyName, + propertyType: reflectedType, + mode: "discriminator", + options: options + }; + getMetadataArgsStorage().columns.add(args); + }; +} + diff --git a/src/metadata-args/DiscriminatorNameMetadataArgs.ts b/src/metadata-args/DiscriminatorNameMetadataArgs.ts new file mode 100644 index 000000000..5b7b14513 --- /dev/null +++ b/src/metadata-args/DiscriminatorNameMetadataArgs.ts @@ -0,0 +1,16 @@ +/** + * Arguments for DiscriminatorNameMetadata class. + */ +export interface DiscriminatorNameMetadataArgs { + + /** + * Class to which discriminator name is applied. + */ + readonly target: Function|string; + + /** + * Discriminator name. + */ + readonly name: string; + +} diff --git a/src/metadata-args/InheritanceMetadataArgs.ts b/src/metadata-args/InheritanceMetadataArgs.ts new file mode 100644 index 000000000..ea4aba79f --- /dev/null +++ b/src/metadata-args/InheritanceMetadataArgs.ts @@ -0,0 +1,16 @@ +/** + * Arguments for InheritanceMetadata class. + */ +export interface InheritanceMetadataArgs { + + /** + * Class to which inheritance is applied. + */ + readonly target?: Function|string; + + /** + * Inheritance type. + */ + readonly type: "single-table"|"class-table"; + +} diff --git a/src/metadata-args/MetadataArgsStorage.ts b/src/metadata-args/MetadataArgsStorage.ts index f48b06e07..4d9a79341 100644 --- a/src/metadata-args/MetadataArgsStorage.ts +++ b/src/metadata-args/MetadataArgsStorage.ts @@ -12,6 +12,8 @@ import {JoinColumnMetadataArgs} from "./JoinColumnMetadataArgs"; import {EmbeddedMetadataArgs} from "./EmbeddedMetadataArgs"; import {EntitySubscriberMetadataArgs} from "./EntitySubscriberMetadataArgs"; import {RelationIdMetadataArgs} from "./RelationIdMetadataArgs"; +import {InheritanceMetadataArgs} from "./InheritanceMetadataArgs"; +import {DiscriminatorNameMetadataArgs} from "./DiscriminatorNameMetadataArgs"; /** * Storage all metadatas of all available types: tables, fields, subscribers, relations, etc. @@ -40,6 +42,8 @@ export class MetadataArgsStorage { readonly relationCounts = new PropertyMetadataArgsCollection(); readonly relationIds = new PropertyMetadataArgsCollection(); readonly embeddeds = new PropertyMetadataArgsCollection(); + readonly inheritances = new TargetMetadataArgsCollection(); + readonly discriminatorNames = new TargetMetadataArgsCollection(); // ------------------------------------------------------------------------- // Public Methods @@ -87,6 +91,8 @@ export class MetadataArgsStorage { const relationCounts = this.relationCounts.filterByTarget(tableMetadata.target); const relationIds = this.relationIds.filterByTarget(tableMetadata.target); const embeddeds = this.embeddeds.filterByTarget(tableMetadata.target); + const inheritances = this.inheritances.filterByTarget(tableMetadata.target); + const discriminatorNames = this.discriminatorNames.filterByTarget(tableMetadata.target); allTableMetadatas .filter(metadata => { @@ -128,6 +134,7 @@ export class MetadataArgsStorage { metadatasFromAbstract.embeddeds .filterRepeatedMetadatas(embeddeds) .forEach(metadata => embeddeds.push(metadata)); + }); return { @@ -140,7 +147,9 @@ export class MetadataArgsStorage { entityListeners: entityListeners, relationCounts: relationCounts, relationIds: relationIds, - embeddeds: embeddeds + embeddeds: embeddeds, + inheritances: inheritances, + discriminatorNames: discriminatorNames }; } diff --git a/src/metadata/ColumnMetadata.ts b/src/metadata/ColumnMetadata.ts index 0709adf7a..acdbdd496 100644 --- a/src/metadata/ColumnMetadata.ts +++ b/src/metadata/ColumnMetadata.ts @@ -10,7 +10,7 @@ import {EmbeddedMetadata} from "./EmbeddedMetadata"; * 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"; +export type ColumnMode = "regular"|"virtual"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel"|"discriminator"; /** * This metadata contains all information about entity's column. diff --git a/src/metadata/DiscriminatorNameMetadata.ts b/src/metadata/DiscriminatorNameMetadata.ts new file mode 100644 index 000000000..35fd1f3c3 --- /dev/null +++ b/src/metadata/DiscriminatorNameMetadata.ts @@ -0,0 +1,33 @@ +import {TargetMetadata} from "./TargetMetadata"; +import {DiscriminatorNameMetadataArgs} from "../metadata-args/DiscriminatorNameMetadataArgs"; + +/** + * This metadata contains information about table specific discriminator name. + */ +export class DiscriminatorNameMetadata extends TargetMetadata { + + // --------------------------------------------------------------------- + // Public Properties + // --------------------------------------------------------------------- + + // entityMetadata: EntityMetadata; + + // --------------------------------------------------------------------- + // Private Properties + // --------------------------------------------------------------------- + + /** + * Inheritance name. + */ + private readonly name: string; + + // --------------------------------------------------------------------- + // Constructor + // --------------------------------------------------------------------- + + constructor(args: DiscriminatorNameMetadataArgs) { + super(args.target); + this.name = args.name; + } + +} diff --git a/src/metadata/InheritanceMetadata.ts b/src/metadata/InheritanceMetadata.ts new file mode 100644 index 000000000..689f45fa5 --- /dev/null +++ b/src/metadata/InheritanceMetadata.ts @@ -0,0 +1,51 @@ +import {TargetMetadata} from "./TargetMetadata"; +import {InheritanceMetadataArgs} from "../metadata-args/InheritanceMetadataArgs"; + +/** + * This metadata contains information about table inheritance. + */ +export class InheritanceMetadata extends TargetMetadata { + + // --------------------------------------------------------------------- + // Public Properties + // --------------------------------------------------------------------- + + // entityMetadata: EntityMetadata; + + // --------------------------------------------------------------------- + // Private Properties + // --------------------------------------------------------------------- + + /** + * Inheritance type. + */ + private readonly type: "single-table"|"class-table"; + + // --------------------------------------------------------------------- + // Constructor + // --------------------------------------------------------------------- + + constructor(args: InheritanceMetadataArgs) { + super(args.target); + this.type = args.type; + } + + // --------------------------------------------------------------------- + // Accessors + // --------------------------------------------------------------------- + + /** + * Checks if class table inheritance is used. + */ + get isClassTable() { + return this.type === "class-table"; + } + + /** + * Checks if single table inheritance is used. + */ + get isSingleTable() { + return this.type === "single-table"; + } + +}