public function deleteObjectTree(Schema $schema, ObjectEntity $entity, $id, Audit $audit) { $numTerminated = 0; // Get a set of ObjectRefs to objects that are 'owned' by the given entity and id. // Also determine the set of all related link entities. $ownedEntitiesWithFk = array(); $ownedEntitiesWithoutFk = array(); $ownerEntitiesWithState = array(); $uncheckedEntities = array(); foreach ($entity->getRelationships() as $relationship) { $otherEntity = $relationship->getOppositeEntity($entity); $otherEntityName = $otherEntity->getName(); // If this is a link relationship... if (!$relationship->getFkEntity()->isObjectEntity()) { // ...then delete any link object referring to the current (to be deleted) object. $propertyValues = array(); $propertyValues[$relationship->getFkColumnName($entity)] = $id; $this->terminateLinks($schema, $relationship->getFkEntity(), $propertyValues, $audit); } else { if ($relationship->getOwnerEntity() == $entity) { if ($relationship->getFkEntity() == $otherEntity) { $ownedEntitiesWithFk[$otherEntityName] = $otherEntity; } else { $ownedEntitiesWithoutFk[$otherEntityName] = $otherEntity; } } else { if ($otherEntity->getStateIdColumnName() != NULL) { $ownerEntitiesWithState[$otherEntityName] = $otherEntity; } else { $uncheckedEntities[$otherEntityName] = $otherEntity; } } } } // Now fetch the ObjectRefs. $objectRef = new ObjectRef($entity, $id); $scope = Scope::parseValue(Scope::VALUE_C_REF . Scope::VALUE_A_REF); $ownedObjectRefs = $objectRef->fetchAllRelatedObjectRefs($schema->getMySQLi(), array_merge($ownedEntitiesWithFk, $ownedEntitiesWithoutFk), $this->defaultQueryContext, $scope); // If the object supports the 'terminated' state... if ($entity->getStateIdColumnName() != NULL) { // ...then terminate it. $numTerminated += $this->terminateObject($schema, $entity, $id, $audit); if ($numTerminated > 0) { // Traverse the object tree and recursively delete children. foreach ($ownedObjectRefs as $ownedObjectRef) { $numTerminated += $this->deleteObjectTree($schema, $ownedObjectRef->getEntity(), $ownedObjectRef->getId(), $audit); } } } else { // The object does not support the 'terminated' state, so it must be deleted permanently. // Check if any 'owner-objects-with-state' refer to the given object, either now or in the past. if (count($ownerEntitiesWithState) > 0) { $queryContext = new QueryContext(array(RestUrlParams::AT => RestUrlParams::ALL_TIMES), NULL); $scope = Scope::parseValue(Scope::VALUE_O_REF); $ownerObjectRefs = $objectRef->fetchAllRelatedObjectRefs($schema->getMySQLi(), $ownerEntitiesWithState, $queryContext, $scope); if (count($ownerObjectRefs) > 0) { // Some terminated owner objects are still referring to this object, so we cannot delete it. return $numTerminated; } } // Split the set of $ownedObjectRefs in two: one set that keeps a foreign key and another that doesn't. $ownedObjectRefsWithFk = array(); $ownedObjectRefsWithoutFk = array(); foreach ($ownedObjectRefs as $ownedObjectRef) { if (array_search($ownedObjectRef->getEntity(), $ownedEntitiesWithFk) !== FALSE) { $ownedObjectRefsWithFk[] = $ownedObjectRef; } else { $ownedObjectRefsWithoutFk[] = $ownedObjectRef; } } // First get rid of all owned objects that keep a foreign key to self... $numTerminated += $this->deleteAndPurgeObjectTrees($schema, $entity, $id, $ownedObjectRefsWithFk, $audit); // ...then delete the object itself. // If deleting an _account, then make sure that any reference to it is 'patched'... $this->patchAccountRefsIfNecessary($schema, $entity, $id, $audit); // ...and then delete the object itself. $queryString = "DELETE d FROM " . $entity->getName() . " d" . " WHERE d." . $entity->getObjectIdColumnName() . " = {$id}"; $mySQLi = $schema->getMySQLi(); $queryResult = $mySQLi->query($queryString); if (!$queryResult) { throw new Exception("Error deleting objects of entity '" . $entity->getName() . "' - {$mySQLi->error}\n<!--\n{$queryString}\n-->"); } $numTerminated += $mySQLi->affected_rows; // Finally, after all foreign keys referring to them have been deleted, delete the remaining owned objects. $numTerminated += $this->deleteAndPurgeObjectTrees($schema, $entity, $id, $ownedObjectRefsWithoutFk, $audit); } return $numTerminated; }
private function getOwnedObjectRefs(Schema $schema, ObjectEntity $entity, $id, $isPublished) { // Find any related objects that are (still) published. // First create a set of entities that are 'owned' by the given one... $ownedEntities = array(); foreach ($entity->getRelationships() as $relationship) { if ($relationship->getOwnerEntity() == $entity) { $oppositeEntity = $relationship->getOppositeEntity($entity); if ($oppositeEntity->isObjectEntity()) { $ownedEntities[] = $oppositeEntity; } } } // ...and then fetch the published ObjectRefs. if (count($ownedEntities) == 0) { return NULL; } $objectRef = new ObjectRef($entity, $id); $params = array(RestUrlParams::PUBLISHED => $isPublished ? 'true' : 'false'); $queryContext = new QueryContext($params, NULL); $scope = Scope::parseValue(Scope::VALUE_C_REF . Scope::VALUE_A_REF . Scope::VALUE_O_REF); return $objectRef->fetchAllRelatedObjectRefs($schema->getMySQLi(), $ownedEntities, $queryContext, $scope); }