entity persist operation builder refactoring

This commit is contained in:
Umed Khudoiberdiev 2016-03-14 12:19:26 +05:00
parent b9d9f19995
commit 1c2a53e46d
2 changed files with 87 additions and 61 deletions

View File

@ -6,6 +6,8 @@ import {InsertOperation} from "./operation/InsertOperation";
import {UpdateByRelationOperation} from "./operation/UpdateByRelationOperation";
import {JunctionInsertOperation} from "./operation/JunctionInsertOperation";
import {UpdateOperation} from "./operation/UpdateOperation";
import {CascadesNotAllowedError} from "./error/CascadesNotAllowedError";
import {RemoveOperation} from "./operation/RemoveOperation";
interface EntityWithId {
id: any;
@ -75,87 +77,81 @@ export class EntityPersistOperationBuilder {
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
private findCascadeInsertedEntities(newEntity: any,
dbEntities: EntityWithId[],
fromRelation: RelationMetadata): InsertOperation[] {
let operations: InsertOperation[] = [];
const metadata = this.connection.getMetadata(newEntity.constructor);
const operations: InsertOperation[] = [];
const isObjectNew = !dbEntities.find(dbEntity => {
return dbEntity.id === newEntity[metadata.primaryColumn.name] && dbEntity.entity.constructor === metadata.target;
});
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.name]);
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
if (isObjectNew && !this.checkCascadesAllowed("insert", metadata, fromRelation)) {
return operations; // return empty operations here
if (isObjectNew && fromRelation && !fromRelation.isCascadeInsert) {
if (this.strictCascadesMode) {
throw new Error("Cascade inserts are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
} else {
return [];
}
// looks like object is new here, but cascades are not allowed - then we should stop iteration
} else if (isObjectNew) {
operations.push(new InsertOperation(newEntity));
}
if (isObjectNew)
operations.push(new InsertOperation(newEntity));
return metadata.relations
metadata.relations
.filter(relation => !!newEntity[relation.propertyName])
.reduce((insertedEntities, relation) => {
.forEach(relation => {
const value = newEntity[relation.propertyName];
if (value instanceof Array) {
value.forEach((subEntity: any) => {
const subInserted = this.findCascadeInsertedEntities(subEntity, dbEntities, relation);
insertedEntities = insertedEntities.concat(subInserted);
operations = operations.concat(subInserted);
});
} else {
const subInserted = this.findCascadeInsertedEntities(value, dbEntities, relation);
insertedEntities = insertedEntities.concat(subInserted);
operations = operations.concat(subInserted);
}
return insertedEntities;
}, operations);
});
return operations;
}
private findCascadeUpdateEntities(metadata: EntityMetadata, dbEntity: any, newEntity: any, fromRelation: RelationMetadata): UpdateOperation[] {
private findCascadeUpdateEntities(metadata: EntityMetadata,
dbEntity: any,
newEntity: any,
fromRelation: RelationMetadata): UpdateOperation[] {
let operations: UpdateOperation[] = [];
if (!dbEntity)
return [];
return operations;
const updatedEntities: any[] = [];
const diff = this.diffColumns(metadata, newEntity, dbEntity);
if (diff.length && fromRelation && !fromRelation.isCascadeUpdate) {
if (this.strictCascadesMode) {
throw new Error("Cascade updates are not allowed in " + metadata.name + "#" + fromRelation.propertyName);
} else {
return [];
}
if (diff.length && !this.checkCascadesAllowed("update", metadata, fromRelation)) {
return operations;
} else if (diff.length) {
operations.push(new UpdateOperation(newEntity, diff));
}
if (diff.length) {
updatedEntities.push(new UpdateOperation(newEntity, diff));
}
return metadata.relations
metadata.relations
.filter(relation => newEntity[relation.propertyName] && dbEntity[relation.propertyName])
.reduce((updatedColumns, relation) => {
.forEach(relation => {
const relMetadata = relation.relatedEntityMetadata;
const relationIdColumnName = relMetadata.primaryColumn.name;
if (newEntity[relation.propertyName] instanceof Array) {
newEntity[relation.propertyName].forEach((subEntity: any) => {
const subDbEntity = (dbEntity[relation.propertyName] as any[]).find(subDbEntity => {
const value = newEntity[relation.propertyName];
const dbValue = dbEntity[relation.propertyName];
if (value instanceof Array) {
value.forEach((subEntity: any) => {
const subDbEntity = dbValue.find((subDbEntity: any) => {
return subDbEntity[relationIdColumnName] === subEntity[relationIdColumnName];
});
if (subDbEntity) {
const relationUpdatedColumns = this.findCascadeUpdateEntities(relMetadata, subDbEntity, subEntity, relation);
updatedColumns = updatedColumns.concat(relationUpdatedColumns);
}
const relationOperations = this.findCascadeUpdateEntities(relMetadata, subDbEntity, subEntity, relation);
operations = operations.concat(relationOperations);
});
} else {
const relationUpdatedColumns = this.findCascadeUpdateEntities(relMetadata, dbEntity[relation.propertyName], newEntity[relation.propertyName], relation);
updatedColumns = updatedColumns.concat(relationUpdatedColumns);
const relationOperations = this.findCascadeUpdateEntities(relMetadata, dbValue, newEntity[relation.propertyName], relation);
operations = operations.concat(relationOperations);
}
return updatedColumns;
}, updatedEntities);
});
return operations;
}
private findCascadeRemovedEntities(metadata: EntityMetadata, dbEntity: any, newEntities: EntityWithId[]): any[] {
@ -173,12 +169,7 @@ export class EntityPersistOperationBuilder {
return newEntity.id === subEntity[relationIdColumnName] && newEntity.entity.constructor === relMetadata.target;
});
if (isObjectRemoved && relation.isCascadeRemove)
removedEntities.push({
entity: subEntity,
fromEntityId: dbEntity[metadata.primaryColumn.name],
metadata: metadata,
relation: relation
});
removedEntities.push(new RemoveOperation(metadata, relation, subEntity, dbEntity[metadata.primaryColumn.name]));
removedEntities = removedEntities.concat(this.findCascadeRemovedEntities(relMetadata, subEntity, newEntities));
});
@ -188,12 +179,7 @@ export class EntityPersistOperationBuilder {
return newEntity.id === relationId && newEntity.entity.constructor === relMetadata.target;
});
if (isObjectRemoved && relation.isCascadeRemove)
removedEntities.push({
entity: dbEntity[relation.propertyName],
fromEntityId: dbEntity[metadata.primaryColumn.name],
metadata: metadata,
relation: relation
});
removedEntities.push(new RemoveOperation(metadata, relation, dbEntity[relation.propertyName], dbEntity[metadata.primaryColumn.name]));
removedEntities = removedEntities.concat(this.findCascadeRemovedEntities(relMetadata, dbEntity[relation.propertyName], newEntities));
}
@ -339,4 +325,31 @@ export class EntityPersistOperationBuilder {
.filter(column => newEntity[column.propertyName] !== dbEntity[column.propertyName]);
}
private findEntityWithId(entityWithIds: EntityWithId[], entityClass: Function, id: any) {
return entityWithIds.find(entityWithId => entityWithId.id === id && entityWithId.entity.constructor === entityClass);
}
private checkCascadesAllowed(type: "insert"|"update"|"remove", metadata: EntityMetadata, relation: RelationMetadata) {
if (!relation)
return true;
let isAllowed = false;
switch (type) {
case "insert":
isAllowed = relation.isCascadeInsert;
break;
case "update":
isAllowed = relation.isCascadeUpdate;
break;
case "remove":
isAllowed = relation.isCascadeRemove;
break;
}
if (isAllowed === false && this.strictCascadesMode)
throw new CascadesNotAllowedError(type, metadata, relation);
return isAllowed;
}
}

View File

@ -0,0 +1,13 @@
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
import {RelationMetadata} from "../../metadata-builder/metadata/RelationMetadata";
export class CascadesNotAllowedError extends Error {
name = "CascadesNotAllowedError";
constructor(type: "insert"|"update"|"remove", metadata: EntityMetadata, relation: RelationMetadata) {
super();
const name = entityClassOrName instanceof Function ? (<any> entityClassOrName).name : entityClassOrName;
this.message = `No broadcaster for "${name}" was found. Looks like this entity is not registered in your connection?`;
}
}