UNPKG

@dazejs/framework

Version:

Daze.js - A powerful web framework for Node.js

175 lines (160 loc) 6.18 kB
import pluralize from 'pluralize'; import { Builder } from '../../database/builder'; import { Join } from '../../database/builder/join'; import { ModelBuilder } from '../builder'; import { Model } from '../model'; import { PivotEntity } from '../pivot-model'; import { Repository } from '../repository'; import { HasRelations } from './has-relations.abstract'; export class BelongsToMany extends HasRelations { /** * 中间表实体实例 */ pivot: Model<PivotEntity>; /** * 关联外键 */ foreignPivotKey: string; /** * 关联中间表键 */ relatedPivotKey: string; /** * 创建多对多关联关系 * @param parent * @param model * @param Pivot * @param foreignPivotKey * @param relatedPivotKey */ constructor(parent: Model<any>, model: Model, Pivot?: any, foreignPivotKey?: string, relatedPivotKey?: string) { super(); this.parent = parent; this.model = model; // 默认单数形式的表名_主键名 this.foreignPivotKey = foreignPivotKey ?? this.getDefaultForeignPivotKey(); // 默认单数形式的表名_主键名 this.relatedPivotKey = relatedPivotKey ?? this.getDefaultRelatedPivotKey(); // 中间表实体实例 this.pivot = this.newPivot(Pivot); } /** * 获取默认外键名 */ getDefaultForeignPivotKey() { return `${pluralize.singular(this.parent.getTable())}_${this.parent.getPrimaryKey()}`; } /** * 获取默认关联外键名 */ getDefaultRelatedPivotKey() { return `${pluralize.singular(this.model.getTable())}_${this.model.getPrimaryKey()}`; } /** * 新建中间表实体实例 */ newPivot(Pivot?: any) { const _class = Pivot ?? PivotEntity; const _pivot = new Model(_class); const table = _pivot.getTable() ?? `${pluralize.singular(this.parent.getTable())}_${pluralize.singular(this.model.getTable())}`; _pivot.setTable(`${table} as pivot`); return _pivot; } /** * 渴求式加载单条记录的关联数据 * @param resultRepos 需要被加载的模型 * @param relation 关联名 */ async eagerly(resultRepos: Repository, relation: string, queryCallback?: (query: ModelBuilder<any> & Builder) => void) { const foreignPivotKey = this.foreignPivotKey; const relatedPivotKey = this.relatedPivotKey; // 需要被加载的模型主键值 const pk = resultRepos.getPrimaryValue(); // // 关联表名,默认父模型单数行书的表名_当前模型单数形式的表名 // const table = this.pivot.getTable() ?? `${pluralize.singular(this.parent.getTable())}_${pluralize.singular(this.model.getTable())}`; // // 设置表名到中间表 // this.pivot.setTable(`${table} as pivot`); // const query = this.pivot.newModelBuilderInstance(); // 获取中间表与当前模型的关联数据 const model = this.model.createRepository(); const [currentRelation, ...restRelation] = relation.split('.'); if (restRelation.length) { model.with(restRelation.join('.')); } const query = model.createQueryBuilder(); if (queryCallback) queryCallback(query); const records: Record<string, any>[] = await query .getBuilder() .columns([...this.model.getColumns().keys()], `pivot.${relatedPivotKey}`, `pivot.${foreignPivotKey}`) .alias('relate') .join((join: Join) => { return join.table(this.pivot.getTable(), 'pivot') .on(`pivot.${relatedPivotKey}`, `relate.${this.model.getPrimaryKey()}`) .where(`pivot.${foreignPivotKey}`, '=', pk); }).find(); // console.log(await this.model.resultsToModels(records)); // 添加关联数据到需要被加载的模型中 if (records) { resultRepos.setAttribute( currentRelation, await this.model.resultToRepositories(model, records) ); } } /** * 渴求式加载多个模型的关联数据 * @param resultReposes * @param relation */ async eagerlyMap(resultReposes: Repository[], relation: string, queryCallback?: (query: ModelBuilder<any> & Builder) => void) { const foreignPivotKey = this.foreignPivotKey; const relatedPivotKey = this.relatedPivotKey; // 查询范围 - foreignPivotKey const range = new Set(); for (const repos of resultReposes) { const id = repos.getPrimaryValue(); id && range.add(id); } if (range.size > 0) { // // 关联表名,默认父模型单数行书的表名_当前模型单数形式的表名 // const table = this.pivot.getTable() ?? `${pluralize.singular(this.parent.getTable())}_${pluralize.singular(this.model.getTable())}`; // // 设置表名到中间表 // this.pivot.setTable(`${table} as pivot`); // const query = this.pivot.newModelBuilderInstance(); // 获取中间表与当前模型的关联数据 const model: Repository = this.model.createRepository(); const [currentRelation, ...restRelation] = relation.split('.'); if (restRelation.length) { model.with(restRelation.join('.')); } const query = model.createQueryBuilder(); if (queryCallback) queryCallback(query); const records: Record<string, any>[] = await query .getBuilder() .columns([...this.model.getColumns().keys()], `pivot.${relatedPivotKey}`, `pivot.${foreignPivotKey}`) .alias('relate') .join((join: Join) => { return join.table(this.pivot.getTable(), 'pivot') .on(`pivot.${relatedPivotKey}`, `relate.${this.model.getPrimaryKey()}`) .whereIn(`pivot.${foreignPivotKey}`, [...range]); }).find(); const map = new Map(); for (const record of records) { const items = map.get(record[foreignPivotKey]) || []; if (items && Array.isArray(items)) { items.push(record); } map.set(record[foreignPivotKey], items); } for (const repos of resultReposes) { const items = map.get(repos.getPrimaryValue()); if (items) { repos.setAttribute( currentRelation, await this.model.resultToRepositories(model, items) ); } } } } }