import ModelComponent from "./ModelComponent"; import Model from "./Model"; import ModelQuery, {ModelQueryResult} from "./ModelQuery"; import {Request} from "express"; import {Type} from "../Utils"; export default class ModelFactory { private static readonly factories: { [modelType: string]: ModelFactory } = {}; public static register(modelType: Type) { if (this.factories[modelType.name]) throw new Error(`Factory for type ${modelType.name} already defined.`); this.factories[modelType.name] = new ModelFactory(modelType); } public static get(modelType: Type): ModelFactory { const factory = this.factories[modelType.name]; if (!factory) throw new Error(`No factory registered for ${modelType.name}.`); return factory; } private readonly modelType: Type; private readonly components: ModelComponentFactory[] = []; protected constructor(modelType: Type) { this.modelType = modelType; } public addComponent(modelComponentFactory: ModelComponentFactory) { this.components.push(modelComponentFactory); } public create(data: any): T { const model = new this.modelType(this, data); for (const component of this.components) { model.addComponent(new component(model)); } model.updateWithData(data); return model; } public get table(): string { return this.modelType.name .replace(/(?:^|\.?)([A-Z])/g, (x, y) => '_' + y.toLowerCase()) .replace(/^_/, '') + 's'; } public select(...fields: string[]): ModelQuery { return ModelQuery.select(this, ...fields); } public update(data: { [key: string]: any }): ModelQuery { return ModelQuery.update(this, data); } public delete(): ModelQuery { return ModelQuery.delete(this); } public getPrimaryKeyFields(): string[] { return ['id']; } public getPrimaryKey(modelData: any): any[] { return this.getPrimaryKeyFields().map(f => modelData[f]); } public getPrimaryKeyString(modelData: any): string { return this.getPrimaryKey(modelData).join(','); } public async getById(...id: any): Promise { let query = this.select(); const primaryKeyFields = this.getPrimaryKeyFields(); for (let i = 0; i < primaryKeyFields.length; i++) { query = query.where(primaryKeyFields[i], id[i]); } return query.first(); } public async paginate(request: Request, perPage: number = 20, query?: ModelQuery): Promise> { let page = request.params.page ? parseInt(request.params.page) : 1; 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); } public async loadRelation(models: T[], relation: string, model: Function, localField: string) { const loadMap: { [p: number]: (model: T) => void } = {}; const ids = models.map(m => { m.relations[relation] = null; if (m[localField]) loadMap[m[localField]] = v => m.relations[relation] = v; return m[localField]; }).filter(id => id); for (const v of await (model).models((model).select().whereIn('id', ids))) { loadMap[v.id!](v); } } } export type ModelComponentFactory = new (model: T) => ModelComponent;