@smallprod/models
Version:
602 lines (585 loc) • 19.9 kB
text/typescript
import EntityManager, { Context } from './entitymanager';
import CreateQuery from './querys/create.query';
import DeleteQuery from './querys/delete.query';
import FieldEntity from './field.entity';
import FindQuery from './querys/find.query';
import { Relationship } from './types';
import UpdateQuery from './querys/update.query';
export default class Entity {
public static tableName: string;
public static dbName?: string;
public static columns: FieldEntity[];
public static nonPersistentColumns: string[];
public static primaryKeys: string[];
public static id: FieldEntity | null;
public static autoCreateNUpdate: boolean;
public static initialized = false;
public static ready: boolean;
public static relations: Relationship[];
public static create = (): any => {
/**/
};
public static findOne(context?: Context) {
const query = new FindQuery(
this.tableName,
async (res: any[]) => {
if (res.length) {
return await this.generateEntity(res[0], context);
}
return null;
},
this.dbName,
);
query.limit(1);
return query;
}
public static findMany(context?: Context, dbName?: string) {
const query = new FindQuery(
this.tableName,
async (res: any[]) => {
const result: Entity[] = [];
await res.reduce(async (prev: any, r: any) => {
await prev;
result.push(await this.generateEntity(r, context, dbName));
}, Promise.resolve());
return result;
},
this.dbName,
);
return query;
}
public static async findById(id: any, context?: Context, dbName?: string) {
if (!this.id) throw new Error('No id specified');
const entity = EntityManager.findEntity(this.tableName, id);
if (entity) return entity;
const query = new FindQuery(this.tableName);
query.where(this.id.fieldName, '=', id);
const res = await query.exec(dbName);
if (res.length) {
return await this.generateEntity(res[0], context, dbName);
}
return null;
}
public static async deleteById(id: any, context?: Context, dbName?: string) {
const query = new DeleteQuery(this.tableName);
if (!this.id) throw new Error('No id specified');
query.where(this.id.fieldName, '=', id);
const res = await query.exec(dbName);
if (res) {
const entity: any = EntityManager.findEntity(this.tableName, id, context);
if (entity) EntityManager.removeEntity(entity, context);
}
return res;
}
private static async generateEntity(
res: any,
context?: Context,
dbName?: string,
) {
const newObj: any = this.create();
newObj.persisted = true;
for (const [key, value] of Object.entries(res)) {
const field: FieldEntity | undefined = this.columns.find(
(f: FieldEntity) => f.fieldName === key,
);
if (field) {
switch (field.type) {
case 'date':
case 'datetime':
case 'timestamp':
case 'time':
newObj[field.key] = new Date(value as any);
break;
default:
newObj[field.key] = value;
}
} else {
newObj[key] = value;
}
}
const entity = EntityManager.findEntity(
newObj.constructor.tableName,
newObj[newObj.constructor.id.key],
context,
);
if (entity) {
return entity;
}
EntityManager.addEntity(newObj, context);
await this.relations.reduce(async (prev: any, cur: Relationship) => {
await prev;
if (cur.autoFetch) {
const relationEntity = EntityManager.entities.find(
(e) => e.tableName === cur.entity,
);
if (relationEntity) {
const ent: typeof Entity = relationEntity.entity;
switch (cur.type) {
case 'manytomany': {
const relationTable = cur.relationTable;
if (relationTable) {
const relations = await new FindQuery(relationTable)
.where(
`${
this.tableName.endsWith('s')
? this.tableName.substr(0, this.tableName.length - 1)
: this.tableName
}_id`,
'=',
res[this.id?.fieldName || ''],
)
.exec(dbName);
if (relations && relations.length) {
newObj.relations.push({
entity: cur.entity,
data: relations,
});
newObj[cur.fieldName] = await ent
.findMany(context, dbName)
.where(
ent.id?.fieldName || '',
'IN',
relations.map(
(r: any) =>
r[
`${
ent.tableName.endsWith('s')
? ent.tableName.substr(
0,
ent.tableName.length - 1,
)
: ent.tableName
}_id`
],
),
)
.exec(dbName);
} else {
newObj[cur.fieldName] = [];
}
}
break;
}
case 'manytoone': {
if (res[`${cur.fieldName}_id`]) {
newObj[cur.fieldName] = await ent.findById(
res[`${cur.fieldName}_id`],
context,
dbName,
);
}
break;
}
case 'onetomany': {
newObj[cur.fieldName] = await ent
.findMany(context)
.where(
`${this.tableName}_id`,
'=',
res[this.id?.fieldName || ''],
)
.exec(dbName);
break;
}
default:
}
}
}
}, Promise.resolve());
return newObj;
}
public persisted = false;
public relations: any[] = [];
public create = async (dbName: string | null = null, context?: Context) => {
const base = this as any;
const query = new CreateQuery(base.constructor.tableName);
const nonPersistentColumns: string[] =
base.constructor.nonPersistentColumns ?? [];
const primaryKeys: string[] = base.constructor.primaryKeys ?? [];
let column: FieldEntity;
for (column of base.constructor.columns) {
if (
!primaryKeys.includes(column.key) &&
!(base[column.key] instanceof Array) &&
typeof base[column.key] !== 'function'
) {
if (
typeof base[column.key] !== 'object' ||
typeof base[column.key] === null
) {
query.setAttribute(column.fieldName, base[column.key]);
} else if (base[column.key] instanceof Date) {
const field: FieldEntity = base.constructor.columns.find(
(f: FieldEntity) => f.name === column.name,
);
if (field) {
const d = base[column.key] as Date;
let formattedD = '';
switch (field.type) {
case 'date': {
formattedD = `${d.getFullYear()}-${
d.getMonth() + 1
}-${d.getDate()}`;
break;
}
case 'datetime': {
formattedD = `${d.getFullYear()}-${
d.getMonth() + 1
}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
break;
}
case 'timestamp': {
formattedD = d.getTime().toString();
break;
}
case 'time': {
formattedD = `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
break;
}
default: {
formattedD = '';
}
}
if (formattedD) {
query.setAttribute(column.fieldName, formattedD);
}
}
}
}
}
const manyToManyQueries: CreateQuery[] = [];
base.constructor.relations.forEach((relation: Relationship) => {
if (base[relation.fieldName]) {
switch (relation.type) {
case 'manytomany':
const rel = this.relations.find(
(r: any) => r.entity === relation.entity,
);
const relData = rel ? rel.data : [];
const relationTable = relation.relationTable;
if (relationTable) {
base[relation.fieldName].forEach((elem: any) => {
if (
elem.persisted &&
!relData.includes(elem[elem.constructor.id.key])
) {
const manyToManyQuery = new CreateQuery(relationTable);
const tableName: string = elem.constructor.tableName;
manyToManyQuery.setAttribute(
`${
tableName.endsWith('s')
? tableName.substr(0, tableName.length - 1)
: tableName
}_id`,
elem[elem.constructor.id.key],
);
manyToManyQueries.push(manyToManyQuery);
}
});
}
break;
case 'manytoone':
if (base[relation.fieldName].persisted) {
query.setAttribute(
`${relation.fieldName}_id`,
base[relation.fieldName][
base[relation.fieldName].constructor.id.key
],
);
}
break;
case 'onetomany':
break;
default:
}
}
});
const res = await query.exec(dbName);
if (res) {
if (base.constructor.id) {
this.persisted = true;
base[base.constructor.id.key] = res;
EntityManager.addEntity(this, context);
await manyToManyQueries.reduce(async (prev: any, cur: CreateQuery) => {
await prev;
const tableName: string = base.constructor.tableName;
cur.setAttribute(
`${
tableName.endsWith('s')
? tableName.substr(0, tableName.length - 1)
: tableName
}_id`,
base[base.constructor.id.key],
);
await cur.exec(dbName);
}, Promise.resolve());
}
return this;
}
return null;
};
public update = async (dbName: string | null = null) => {
const base = this as any;
const query = new UpdateQuery(base.constructor.tableName); // TODO fix this
const nonPersistentColumns: string[] =
base.constructor.nonPersistentColumns ?? [];
const primaryKeys: string[] = base.constructor.primaryKeys ?? [];
let column: FieldEntity;
for (column of base.constructor.columns) {
if (
!primaryKeys.includes(column.key) &&
!(base[column.key] instanceof Array) &&
typeof base[column.key] !== 'function'
) {
if (
typeof base[column.key] !== 'object' ||
typeof base[column.key] === null
) {
query.setAttribute(column.fieldName, base[column.key]);
} else if (base[column.key] instanceof Date) {
const field: FieldEntity = base.constructor.columns.find(
(f: FieldEntity) => f.key === column.key,
);
if (field) {
const d = base[column.key] as Date;
let formattedD = '';
switch (field.type) {
case 'date': {
formattedD = `${d.getFullYear()}-${
d.getMonth() + 1
}-${d.getDate()}`;
break;
}
case 'datetime': {
formattedD = `${d.getFullYear()}-${
d.getMonth() + 1
}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
break;
}
case 'timestamp': {
formattedD = d.getTime().toString();
break;
}
case 'time': {
formattedD = `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
break;
}
default: {
formattedD = '';
}
}
if (formattedD) {
query.setAttribute(column.fieldName, formattedD);
}
}
}
}
}
if (base.constructor.id) {
query.where(
base.constructor.id.fieldName,
'=',
base[base.constructor.id.key],
);
} else {
throw new Error('No specified id');
}
await base.constructor.relations.reduce(
async (prev: any, relation: Relationship) => {
await prev;
if (base[relation.fieldName]) {
switch (relation.type) {
case 'manytomany': {
const rel = this.relations.find(
(r: any) => r.entity === relation.entity,
);
const relData = rel ? rel.data : [];
const relationTable = relation.relationTable;
if (relationTable) {
const curTableName: string = base.constructor.tableName;
const curTableField = `${
curTableName.endsWith('s')
? curTableName.substr(0, curTableName.length - 1)
: curTableName
}_id`;
const removeOldQuery = new DeleteQuery(relationTable);
removeOldQuery.where(
curTableField,
'=',
base[base.constructor.id.key],
);
await removeOldQuery.exec(dbName);
await base[relation.fieldName].reduce(
async (previous: any, elem: any) => {
await previous;
if (
elem.persisted &&
!relData.includes(elem[elem.constructor.id.key])
) {
const tableName: string = elem.constructor.tableName;
const tableField = `${
tableName.endsWith('s')
? tableName.substr(0, tableName.length - 1)
: tableName
}_id`;
const manyToManyQuery = new CreateQuery(relationTable);
manyToManyQuery.setAttribute(
tableField,
elem[elem.constructor.id.key],
);
manyToManyQuery.setAttribute(
curTableField,
base[base.constructor.id.key],
);
await manyToManyQuery.exec(dbName);
}
},
Promise.resolve(),
);
}
break;
}
case 'manytoone':
if (base[relation.fieldName].persisted) {
query.setAttribute(
`${relation.fieldName}_id`,
base[relation.fieldName][
base[relation.fieldName].constructor.id.key
],
);
}
break;
case 'onetomany':
break;
default:
}
}
},
Promise.resolve(),
);
const res = await query.exec(dbName);
if (res) {
return this;
}
return null;
};
public delete = async (dbName: string | null = null, context?: Context) => {
const base = this as any;
const query = new DeleteQuery(base.constructor.tableName); // TODO fix this
if (!base.constructor.id) {
throw new Error('No id specified');
}
await base.constructor.relations.reduce(
async (prev: any, relation: Relationship) => {
await prev;
if (relation.type === 'manytomany' && relation.relationTable) {
const curTableName: string = base.constructor.tableName;
const curTableField = `${
curTableName.endsWith('s')
? curTableName.substr(0, curTableName.length - 1)
: curTableName
}_id`;
const deleteQuery = new DeleteQuery(relation.relationTable);
deleteQuery.where(curTableField, '=', base[base.constructor.id.key]);
await deleteQuery.exec(dbName);
}
},
Promise.resolve(),
);
query.where(
base.constructor.id.fieldName,
'=',
base[base.constructor.id.key],
);
const res = await query.exec(dbName);
if (res) {
EntityManager.removeEntity(this, context);
return true;
}
return false;
};
public fetch = async (field: string, context?: Context, dbName?: string) => {
const base = this as any;
const relation: Relationship = base.constructor.relations.find(
(r: Relationship) => r.fieldName === field,
);
if (relation) {
const rel = EntityManager.entities.find(
(e) => e.tableName === relation.entity,
);
if (rel) {
const ent: typeof Entity = rel.entity;
switch (relation.type) {
case 'manytomany': {
const relationTable = relation.relationTable;
if (relationTable) {
const tableName: string = base.constructor.tableName;
const relations = await new FindQuery(relationTable)
.where(
`${
tableName.endsWith('s')
? tableName.substr(0, tableName.length - 1)
: tableName
}_id`,
'=',
base[base.constructor.id.key],
)
.exec(dbName);
if (relations && relations.length) {
this.relations.push({
entity: relation.entity,
data: relations,
});
base[relation.fieldName] = await ent
.findMany(context, dbName)
.where(
ent.id?.fieldName || '',
'IN',
relations.map(
(r: any) =>
r[
`${
ent.tableName.endsWith('s')
? ent.tableName.substr(
0,
ent.tableName.length - 1,
)
: ent.tableName
}_id`
],
),
)
.exec(dbName);
} else {
base[field] = [];
}
}
break;
}
case 'manytoone': {
if (base[`${relation.entity}_id`]) {
base[field] = await ent.findById(
base[`${relation.entity}_id`],
context,
dbName,
);
}
break;
}
case 'onetomany': {
base[field] = await ent
.findMany(context, dbName)
.where(
`${base.constructor.tableName}_id`,
'=',
base[base.constructor.id.key],
)
.exec(dbName);
break;
}
default:
}
}
}
};
}