@athenna/database
Version:
The Athenna database handler for SQL/NoSQL.
116 lines (115 loc) • 4.76 kB
JavaScript
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { String } from '@athenna/common';
export class BelongsToManyRelation {
/**
* Get the options with defined default values.
*/
static options(relation) {
const RelationModel = relation.model();
const PivotModel = relation.pivotModel();
relation.pivotTable = relation.pivotTable || PivotModel.table();
relation.relationPrimaryKey =
RelationModel.schema().getMainPrimaryKeyProperty();
relation.relationForeignKey = `${String.toCamelCase(RelationModel.name)}Id`;
return relation;
}
/**
* Load a belongs to many relation.
*/
static async load(model, relation) {
this.options(relation);
const pivotData = await relation
.pivotModel()
.query()
.where(relation.foreignKey, model[relation.primaryKey])
.findMany();
const relationIds = pivotData.map(d => d[relation.relationForeignKey]);
model[relation.property] = await relation
.model()
.query()
.whereIn(relation.relationPrimaryKey, relationIds)
.when(relation.withClosure, relation.withClosure)
.findMany();
return model;
}
/**
* Load all models that belongs to relation.
*/
static async loadAll(models, relation) {
this.options(relation);
const primaryKeys = models.map(m => m[relation.primaryKey]);
const pivotData = await relation
.pivotModel()
.query()
.whereIn(relation.foreignKey, primaryKeys)
.findMany();
const pivotDataMap = new Map();
const relationForeignKey = [];
pivotData.forEach(data => {
relationForeignKey.push(data[relation.relationForeignKey]);
const array = pivotDataMap.get(data[relation.foreignKey]) || [];
array.push(data[relation.relationForeignKey]);
pivotDataMap.set(data[relation.foreignKey], array);
});
const results = await relation
.model()
.query()
.whereIn(relation.relationPrimaryKey, relationForeignKey)
.when(relation.withClosure, relation.withClosure)
.findMany();
const map = new Map();
results.forEach(result => map.set(result[relation.relationPrimaryKey], result));
return models.map(model => {
const ids = pivotDataMap.get(model[relation.primaryKey]) || [];
model[relation.property] = ids
.map(id => map.get(id))
.filter(data => data !== undefined);
return model;
});
}
/**
* Apply a where has relation to the query when the given model
* belongs to many relations.
*/
static whereHas(Model, query, relation) {
const PivotModel = relation.pivotModel();
const RelationModel = relation.model();
const modelTable = Model.table();
const pivotTable = PivotModel.table();
const relatedTable = RelationModel.table();
const modelPK = Model.schema().getMainPrimaryKeyName();
const relatedPK = RelationModel.schema().getMainPrimaryKeyName();
const pivotFK = PivotModel.schema().getColumnNameByProperty(relation.foreignKey) ||
PivotModel.schema().getColumnNameByProperty(`${String.toCamelCase(Model.name)}Id`);
const pivotRK = PivotModel.schema().getColumnNameByProperty(relation.relationForeignKey) ||
PivotModel.schema().getColumnNameByProperty(`${String.toCamelCase(RelationModel.name)}Id`);
let whereRaw = `${pivotTable}.${pivotFK} = ${modelTable}.${modelPK}`;
switch (PivotModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${pivotTable}"."${pivotFK}" = "${modelTable}"."${modelPK}"`;
}
PivotModel.query()
.setDriver(query, pivotTable)
.whereRaw(whereRaw)
.whereExists(innerQuery => {
let whereRaw = `${relatedTable}.${relatedPK} = ${pivotTable}.${pivotRK}`;
switch (RelationModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${relatedTable}"."${relatedPK}" = "${pivotTable}"."${pivotRK}"`;
}
RelationModel.query()
.setDriver(innerQuery, relatedTable)
.whereRaw(whereRaw)
.when(relation.closure, relation.closure);
});
}
}