swaf/src/db/ModelFactory.ts

105 lines
3.8 KiB
TypeScript
Raw Normal View History

2020-07-24 12:13:28 +02:00
import ModelComponent from "./ModelComponent";
import Model, {ModelType} from "./Model";
import ModelQuery, {ModelQueryResult, QueryFields} from "./ModelQuery";
2020-07-24 12:13:28 +02:00
import {Request} from "express";
export default class ModelFactory<M extends Model> {
private static readonly factories: { [modelType: string]: ModelFactory<Model> | undefined } = {};
2020-07-24 12:13:28 +02:00
public static register<M extends Model>(modelType: ModelType<M>): void {
2020-07-24 12:13:28 +02:00
if (this.factories[modelType.name]) throw new Error(`Factory for type ${modelType.name} already defined.`);
this.factories[modelType.name] = new ModelFactory<M>(modelType) as unknown as ModelFactory<Model>;
2020-07-24 12:13:28 +02:00
}
public static get<M extends Model>(modelType: ModelType<M>): ModelFactory<M> {
2020-07-24 12:13:28 +02:00
const factory = this.factories[modelType.name];
if (!factory) throw new Error(`No factory registered for ${modelType.name}.`);
return factory as unknown as ModelFactory<M>;
2020-07-24 12:13:28 +02:00
}
public static has<M extends Model>(modelType: ModelType<M>): boolean {
return !!this.factories[modelType.name];
}
private readonly modelType: ModelType<M>;
private readonly components: ModelComponentFactory<M>[] = [];
2020-07-24 12:13:28 +02:00
protected constructor(modelType: ModelType<M>) {
2020-07-24 12:13:28 +02:00
this.modelType = modelType;
}
public addComponent(modelComponentFactory: ModelComponentFactory<M>): void {
2020-07-24 12:13:28 +02:00
this.components.push(modelComponentFactory);
}
2020-11-11 18:35:49 +01:00
public hasComponent(modelComponentFactory: ModelComponentFactory<M>): boolean {
return !!this.components.find(c => c === modelComponentFactory);
}
public create(data: Pick<M, keyof M>, isNewModel: boolean): M {
const model = new this.modelType(this as unknown as ModelFactory<never>, isNewModel);
2020-07-24 12:13:28 +02:00
for (const component of this.components) {
model.addComponent(new component(model));
}
model.updateWithData(data);
2020-07-24 12:13:28 +02:00
return model;
}
public get table(): string {
return this.modelType.table;
2020-07-24 12:13:28 +02:00
}
public select(...fields: QueryFields): ModelQuery<M> {
2020-07-24 12:13:28 +02:00
return ModelQuery.select(this, ...fields);
}
public insert(data: Pick<M, keyof M>): ModelQuery<M> {
return ModelQuery.insert(this, data);
}
public update(data: Pick<M, keyof M>): ModelQuery<M> {
2020-07-24 12:13:28 +02:00
return ModelQuery.update(this, data);
}
public delete(): ModelQuery<M> {
2020-07-24 12:13:28 +02:00
return ModelQuery.delete(this);
}
public getPrimaryKeyFields(): (keyof M & string)[] {
return this.modelType.getPrimaryKeyFields();
2020-07-24 12:13:28 +02:00
}
public getPrimaryKey(modelData: Pick<M, keyof M>): Pick<M, keyof M>[keyof M & string][] {
2020-07-24 12:13:28 +02:00
return this.getPrimaryKeyFields().map(f => modelData[f]);
}
public getPrimaryKeyString(modelData: Pick<M, keyof M>): string {
2020-07-24 12:13:28 +02:00
return this.getPrimaryKey(modelData).join(',');
}
public async getById(...id: PrimaryKeyValue[]): Promise<M | null> {
2020-07-24 12:13:28 +02:00
let query = this.select();
const primaryKeyFields = this.getPrimaryKeyFields();
for (let i = 0; i < primaryKeyFields.length; i++) {
query = query.where(primaryKeyFields[i], id[i]);
}
return await query.first();
2020-07-24 12:13:28 +02:00
}
public async paginate(request: Request, perPage: number = 20, query?: ModelQuery<M>): Promise<ModelQueryResult<M>> {
const page = request.params.page ? parseInt(request.params.page) : 1;
2020-07-24 12:13:28 +02:00
if (!query) query = this.select();
if (request.params.sortBy) {
const dir = request.params.sortDirection;
query = query.sortBy(request.params.sortBy, dir === 'ASC' || dir === 'DESC' ? dir : undefined);
} else {
query = query.sortBy('id');
}
return await query.paginate(page, perPage);
}
}
export type ModelComponentFactory<M extends Model> = new (model: M) => ModelComponent<M>;
export type PrimaryKeyValue = string | number | boolean | null | undefined;