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);
 }