feat: refactor entity classes to use mixins, add rateable mixin to book and copy

This commit is contained in:
Xymorot 2019-11-22 21:22:13 +01:00
parent 8bfaba185b
commit 2006815b2b
18 changed files with 112 additions and 23 deletions

View File

@ -1,7 +1,7 @@
import { Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export abstract class Base {
export class BaseEntity {
@PrimaryGeneratedColumn()
public id: number;
}

View File

@ -1,9 +1,10 @@
import { Entity, ManyToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Book } from './book';
@Entity()
export class Author extends MultiNamed {
export class Author extends MultiNamed(BaseEntity) {
@ManyToMany(() => Book, {
nullable: false,
onDelete: 'CASCADE',

View File

@ -1,11 +1,14 @@
import { Column, Entity } from 'typeorm';
import { Base } from '../../base';
@Entity()
export abstract class MultiNamed extends Base {
@Column()
public nameCanonical: string;
export function MultiNamed<T extends Constructor>(BaseClass: T) {
@Entity()
class MixinClass extends BaseClass {
@Column()
public nameCanonical: string;
@Column('simple-array')
public names: string[];
@Column('simple-array')
public names: string[];
}
return MixinClass;
}

View File

@ -0,0 +1,11 @@
import { Column, Entity } from 'typeorm';
export function Rateable<T extends Constructor>(BaseClass: T) {
@Entity()
class MixinClass extends BaseClass {
@Column({ nullable: true })
public rating: number;
}
return MixinClass;
}

View File

@ -0,0 +1,6 @@
{
"extends": ["../../../../../tslint.json"],
"rules": {
"typedef": false
}
}

View File

@ -1,13 +1,15 @@
import { Entity, JoinTable, ManyToMany, OneToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { Author } from './author';
import { MultiNamed } from './base/multi-named';
import { Rateable } from './base/rateable';
import { Character } from './character';
import { Copy } from './copy';
import { Fiction } from './fiction';
import { Tag } from './tag';
@Entity()
export class Book extends MultiNamed {
export class Book extends Rateable(MultiNamed(BaseEntity)) {
@OneToMany(
() => Copy,
(copy: Copy) => copy.original,

View File

@ -1,10 +1,11 @@
import { Entity, ManyToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Book } from './book';
import { Fiction } from './fiction';
@Entity()
export class Character extends MultiNamed {
export class Character extends MultiNamed(BaseEntity) {
@ManyToMany(() => Book, {
nullable: false,
onDelete: 'CASCADE',

View File

@ -1,5 +1,5 @@
import { Column, Entity, ManyToOne } from 'typeorm';
import { Base } from '../base';
import { BaseEntity } from '../base-entity';
import { Copy } from './copy';
const enum CopyTypes {
@ -10,7 +10,7 @@ const enum CopyTypes {
}
@Entity()
export class CopyType extends Base {
export class CopyType extends BaseEntity {
@ManyToOne(
() => Copy,
(copy: Copy) => copy.types,

View File

@ -1,5 +1,6 @@
import { Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany } from 'typeorm';
import { Base } from '../base';
import { BaseEntity } from '../base-entity';
import { Rateable } from './base/rateable';
import { Book } from './book';
import { CopyType } from './copy-type';
import { Language } from './language';
@ -7,7 +8,7 @@ import { Source } from './source';
import { Translator } from './translator';
@Entity()
export class Copy extends Base {
export class Copy extends Rateable(BaseEntity) {
@ManyToOne(
() => Book,
(book: Book) => book.copies,

View File

@ -1,10 +1,11 @@
import { Entity, ManyToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Book } from './book';
import { Character } from './character';
@Entity()
export class Fiction extends MultiNamed {
export class Fiction extends MultiNamed(BaseEntity) {
@ManyToMany(() => Book, {
nullable: false,
onDelete: 'CASCADE',

View File

@ -1,9 +1,9 @@
import { Column, Entity, ManyToMany } from 'typeorm';
import { Base } from '../base';
import { BaseEntity } from '../base-entity';
import { Copy } from './copy';
@Entity()
export class Language extends Base {
export class Language extends BaseEntity {
@Column({
nullable: false,
unique: true,

View File

@ -1,9 +1,10 @@
import { Entity, OneToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Source } from './source';
@Entity()
export class Site extends MultiNamed {
export class Site extends MultiNamed(BaseEntity) {
@OneToMany(
() => Source,
(source: Source) => source.site,

View File

@ -1,10 +1,10 @@
import { Column, Entity, ManyToMany, ManyToOne } from 'typeorm';
import { Base } from '../base';
import { BaseEntity } from '../base-entity';
import { Copy } from './copy';
import { Site } from './site';
@Entity()
export class Source extends Base {
export class Source extends BaseEntity {
@Column({
nullable: false,
unique: true,

View File

@ -1,9 +1,10 @@
import { Entity, ManyToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Book } from './book';
@Entity()
export class Tag extends MultiNamed {
export class Tag extends MultiNamed(BaseEntity) {
@ManyToMany(() => Book, {
nullable: false,
onDelete: 'CASCADE',

View File

@ -1,9 +1,10 @@
import { Entity, ManyToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { MultiNamed } from './base/multi-named';
import { Copy } from './copy';
@Entity()
export class Translator extends MultiNamed {
export class Translator extends MultiNamed(BaseEntity) {
@ManyToMany(() => Copy, {
nullable: false,
onDelete: 'CASCADE',

View File

@ -0,0 +1,51 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class addRating1574452206367 implements MigrationInterface {
name = 'addRating1574452206367';
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(
`CREATE TABLE "temporary_copy" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "favorited" boolean NOT NULL DEFAULT (0), "isDigital" boolean NOT NULL, "originalId" integer NOT NULL, "rating" integer, CONSTRAINT "FK_e8ce0011cf0a8b9fdc8ad908a44" FOREIGN KEY ("originalId") REFERENCES "book" ("id") ON DELETE CASCADE ON UPDATE CASCADE)`,
undefined
);
await queryRunner.query(
`INSERT INTO "temporary_copy"("id", "favorited", "isDigital", "originalId") SELECT "id", "favorited", "isDigital", "originalId" FROM "copy"`,
undefined
);
await queryRunner.query(`DROP TABLE "copy"`, undefined);
await queryRunner.query(`ALTER TABLE "temporary_copy" RENAME TO "copy"`, undefined);
await queryRunner.query(
`CREATE TABLE "temporary_book" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "nameCanonical" varchar NOT NULL, "names" text NOT NULL, "rating" integer)`,
undefined
);
await queryRunner.query(
`INSERT INTO "temporary_book"("id", "nameCanonical", "names") SELECT "id", "nameCanonical", "names" FROM "book"`,
undefined
);
await queryRunner.query(`DROP TABLE "book"`, undefined);
await queryRunner.query(`ALTER TABLE "temporary_book" RENAME TO "book"`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "book" RENAME TO "temporary_book"`, undefined);
await queryRunner.query(
`CREATE TABLE "book" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "nameCanonical" varchar NOT NULL, "names" text NOT NULL)`,
undefined
);
await queryRunner.query(
`INSERT INTO "book"("id", "nameCanonical", "names") SELECT "id", "nameCanonical", "names" FROM "temporary_book"`,
undefined
);
await queryRunner.query(`DROP TABLE "temporary_book"`, undefined);
await queryRunner.query(`ALTER TABLE "copy" RENAME TO "temporary_copy"`, undefined);
await queryRunner.query(
`CREATE TABLE "copy" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "favorited" boolean NOT NULL DEFAULT (0), "isDigital" boolean NOT NULL, "originalId" integer NOT NULL, CONSTRAINT "FK_e8ce0011cf0a8b9fdc8ad908a44" FOREIGN KEY ("originalId") REFERENCES "book" ("id") ON DELETE CASCADE ON UPDATE CASCADE)`,
undefined
);
await queryRunner.query(
`INSERT INTO "copy"("id", "favorited", "isDigital", "originalId") SELECT "id", "favorited", "isDigital", "originalId" FROM "temporary_copy"`,
undefined
);
await queryRunner.query(`DROP TABLE "temporary_copy"`, undefined);
}
}

View File

@ -0,0 +1,8 @@
{
"extends": ["../../../../tslint.json"],
"rules": {
"class-name": false,
"member-access": false,
"typedef": false
}
}

1
src/types/constructor.ts Normal file
View File

@ -0,0 +1 @@
type Constructor<T = {}> = new (...args: any[]) => T;