import { Subject } from "../Subject" import { ObjectLiteral } from "../../common/ObjectLiteral" import { ObjectUtils } from "../../util/ObjectUtils" /** * Finds all cascade operations of the given subject and cascade operations of the found cascaded subjects, * e.g. builds a cascade tree and creates a subjects for them. */ export class CascadesSubjectBuilder { // --------------------------------------------------------------------- // Constructor // --------------------------------------------------------------------- constructor(protected allSubjects: Subject[]) {} // --------------------------------------------------------------------- // Public Methods // --------------------------------------------------------------------- /** * Builds a cascade subjects tree and pushes them in into the given array of subjects. */ build( subject: Subject, operationType: "save" | "remove" | "soft-remove" | "recover", ) { subject.metadata .extractRelationValuesFromEntity( subject.entity!, subject.metadata.relations, ) // todo: we can create EntityMetadata.cascadeRelations .forEach(([relation, relationEntity, relationEntityMetadata]) => { // we need only defined values and insert, update, soft-remove or recover cascades of the relation should be set if ( relationEntity === undefined || relationEntity === null || (!relation.isCascadeInsert && !relation.isCascadeUpdate && !relation.isCascadeSoftRemove && !relation.isCascadeRecover) ) return // if relation entity is just a relation id set (for example post.tag = 1) // then we don't really need to check cascades since there is no object to insert or update if (!ObjectUtils.isObject(relationEntity)) return // if we already has this entity in list of operated subjects then skip it to avoid recursion const alreadyExistRelationEntitySubject = this.findByPersistEntityLike( relationEntityMetadata.target, relationEntity, ) if (alreadyExistRelationEntitySubject) { if ( alreadyExistRelationEntitySubject.canBeInserted === false ) // if its not marked for insertion yet alreadyExistRelationEntitySubject.canBeInserted = relation.isCascadeInsert === true && operationType === "save" if ( alreadyExistRelationEntitySubject.canBeUpdated === false ) // if its not marked for update yet alreadyExistRelationEntitySubject.canBeUpdated = relation.isCascadeUpdate === true && operationType === "save" if ( alreadyExistRelationEntitySubject.canBeSoftRemoved === false ) // if its not marked for removal yet alreadyExistRelationEntitySubject.canBeSoftRemoved = relation.isCascadeSoftRemove === true && operationType === "soft-remove" if ( alreadyExistRelationEntitySubject.canBeRecovered === false ) // if its not marked for recovery yet alreadyExistRelationEntitySubject.canBeRecovered = relation.isCascadeRecover === true && operationType === "recover" return } // mark subject with what we can do with it // and add to the array of subjects to load only if there is no same entity there already const relationEntitySubject = new Subject({ metadata: relationEntityMetadata, parentSubject: subject, entity: relationEntity, canBeInserted: relation.isCascadeInsert === true && operationType === "save", canBeUpdated: relation.isCascadeUpdate === true && operationType === "save", canBeSoftRemoved: relation.isCascadeSoftRemove === true && operationType === "soft-remove", canBeRecovered: relation.isCascadeRecover === true && operationType === "recover", }) this.allSubjects.push(relationEntitySubject) // go recursively and find other entities we need to insert/update this.build(relationEntitySubject, operationType) }) } // --------------------------------------------------------------------- // Protected Methods // --------------------------------------------------------------------- /** * Finds subject where entity like given subject's entity. * Comparison made by entity id. */ protected findByPersistEntityLike( entityTarget: Function | string, entity: ObjectLiteral, ): Subject | undefined { return this.allSubjects.find((subject) => { if (!subject.entity) return false if (subject.entity === entity) return true return ( subject.metadata.target === entityTarget && subject.metadata.compareEntities( subject.entityWithFulfilledIds!, entity, ) ) }) } }