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'; import { Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity() @Entity()
export abstract class Base { export class BaseEntity {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
public id: number; public id: number;
} }

View File

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

View File

@ -1,11 +1,14 @@
import { Column, Entity } from 'typeorm'; import { Column, Entity } from 'typeorm';
import { Base } from '../../base';
@Entity() export function MultiNamed<T extends Constructor>(BaseClass: T) {
export abstract class MultiNamed extends Base { @Entity()
@Column() class MixinClass extends BaseClass {
public nameCanonical: string; @Column()
public nameCanonical: string;
@Column('simple-array') @Column('simple-array')
public names: string[]; 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 { Entity, JoinTable, ManyToMany, OneToMany } from 'typeorm';
import { BaseEntity } from '../base-entity';
import { Author } from './author'; import { Author } from './author';
import { MultiNamed } from './base/multi-named'; import { MultiNamed } from './base/multi-named';
import { Rateable } from './base/rateable';
import { Character } from './character'; import { Character } from './character';
import { Copy } from './copy'; import { Copy } from './copy';
import { Fiction } from './fiction'; import { Fiction } from './fiction';
import { Tag } from './tag'; import { Tag } from './tag';
@Entity() @Entity()
export class Book extends MultiNamed { export class Book extends Rateable(MultiNamed(BaseEntity)) {
@OneToMany( @OneToMany(
() => Copy, () => Copy,
(copy: Copy) => copy.original, (copy: Copy) => copy.original,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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