@athenna/database
Version:
The Athenna database handler for SQL/NoSQL.
141 lines (140 loc) • 4.98 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 { Macroable } from '@athenna/common';
import { ObjectId } from '#src/helpers/ObjectId';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { HasOneRelation } from '#src/models/relations/HasOne/HasOneRelation';
import { HasManyRelation } from '#src/models/relations/HasMany/HasManyRelation';
import { HasOneThroughRelation } from '#src/models/relations/HasOneThrough/HasOneThroughRelation';
import { HasManyThroughRelation } from '#src/models/relations/HasManyThrough/HasManyThroughRelation';
import { BelongsToRelation } from '#src/models/relations/BelongsTo/BelongsToRelation';
import { BelongsToManyRelation } from '#src/models/relations/BelongsToMany/BelongsToManyRelation';
export class ModelGenerator extends Macroable {
constructor(model, schema) {
super();
this.Model = model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.schema = schema;
}
/**
* Generate one model instance with relations loaded.
*/
async generateOne(data) {
if (!data) {
return undefined;
}
const model = this.instantiateOne(data);
return this.includeRelations(model);
}
/**
* Generate models instances with relations loaded.
*/
async generateMany(data) {
if (!data || !data.length) {
return [];
}
const models = await Promise.all(data.map(d => this.instantiateOne(d)));
return this.includeRelationsOfAll(models);
}
/**
* Instantiate one model using vanilla database data.
*/
instantiateOne(data) {
return this.populate(data, new this.Model());
}
/**
* Populate one object data in the model instance
* using the column dictionary to map keys.
*/
populate(object, model) {
Object.keys(object).forEach(key => {
const column = this.schema.getColumnByName(key);
if (!column) {
return;
}
if (ObjectId.isValidObject(object[key])) {
object[key] = object[key].toString();
}
model[column.property] = object[key];
});
return model;
}
/**
* Include one relation to one model.
*/
async includeRelation(model, relation) {
switch (relation.type) {
case 'hasOne':
return HasOneRelation.load(model, relation);
case 'hasMany':
return HasManyRelation.load(model, relation);
case 'hasOneThrough':
return HasOneThroughRelation.load(model, relation);
case 'hasManyThrough':
return HasManyThroughRelation.load(model, relation);
case 'belongsTo':
return BelongsToRelation.load(model, relation);
case 'belongsToMany':
return BelongsToManyRelation.load(model, relation);
default:
return model;
}
}
/**
* Include all relations to one model.
*/
async includeRelations(model) {
const relations = this.schema.getIncludedRelations();
if (!relations || !relations.length) {
return model.setOriginal();
}
for (const relation of relations) {
model = await this.includeRelation(model, relation);
}
if (!model) {
return undefined;
}
return model.setOriginal();
}
/**
* Include one relation for all models.
*/
async includeRelationOfAll(models, relation) {
switch (relation.type) {
case 'hasOne':
return HasOneRelation.loadAll(models, relation);
case 'hasMany':
return HasManyRelation.loadAll(models, relation);
case 'hasOneThrough':
return HasOneThroughRelation.loadAll(models, relation);
case 'hasManyThrough':
return HasManyThroughRelation.loadAll(models, relation);
case 'belongsTo':
return BelongsToRelation.loadAll(models, relation);
case 'belongsToMany':
return BelongsToManyRelation.loadAll(models, relation);
default:
return models;
}
}
/**
* Include all relations for all models.
*/
async includeRelationsOfAll(models) {
const relations = this.schema.getIncludedRelations();
if (!relations || !relations.length) {
return models.map(model => model.setOriginal());
}
for (const relation of relations) {
models = await this.includeRelationOfAll(models, relation);
}
return models.map(model => model.setOriginal());
}
}