public function getPersistedId(ObjectEntity $entity, $temporaryId, $mustExist = TRUE) { // If it is a persisted id already... if (self::isPersistedId($temporaryId)) { // ...then return it. return $temporaryId; } // If it's in the entityTemporaryIdMap... if (array_key_exists($entity->getName(), $this->entityTemporaryIdMap)) { $temporaryIdMap = $this->entityTemporaryIdMap[$entity->getName()]; if (array_key_exists($temporaryId, $temporaryIdMap)) { // ...then return it. return $temporaryIdMap[$temporaryId]; } } // If a parent is available... if ($this->parent != null) { // ...then delegate the call. return $this->parent->getPersistedId($entity, $temporaryId, $mustExist); } // OK, so a persisted id is not available. Now what? if ($mustExist) { throw new Exception("Could not find the persisted equivalent of temporary id '{$temporaryId}' of entity '" . $entity->getName() . "'.", RestResponse::CLIENT_ERROR); } return NULL; }
public function fetchRelatedObjectRefsOfEntity(MySQLi $mySQLi, ObjectEntity $otherEntity, QueryContext $queryContext, Scope $scope, array &$fetchedObjectRefs) { // Find the corresponding relationship, ignoring link-relationships. $hasTerminatedObjects = FALSE; foreach ($this->objectEntity->getRelationships() as $relationship) { if ($relationship->getOppositeEntity($this->objectEntity) != $otherEntity) { continue; } // Select the id of the related object. $objectIdColumnName = $otherEntity->getObjectIdColumnName(); $query = new QueryRelatedEntity($this->objectEntity, $this->id, $relationship, $queryContext, $scope); $query->setPropertyNames(array($objectIdColumnName)); $queryString = $query->getQueryString(); $queryResult = $mySQLi->query($queryString); if (!$queryResult) { throw new Exception("Error fetching ObjectRefs of entity '" . $otherEntity->getName() . "', associated to '" . $this->objectEntity->getName() . "[{$this->id}]' - " . $mySQLi->error . "\n<!--\n{$queryString}\n-->"); } while ($dbObject = $queryResult->fetch_assoc()) { $objectRef = new ObjectRef($otherEntity, $dbObject[$objectIdColumnName]); if (!$queryContext->getParameterPublished() and isset($dbObject[QueryEntity::ID_TERMINATED])) { $hasTerminatedObjects = TRUE; } else { // Always use the objectRef if we're fetching published data or if it's not terminated. $fetchedObjectRefs[] = $objectRef; } } $queryResult->close(); } return $hasTerminatedObjects; }
/** * Recursively fetches all objects-to-be-deleted and puts them in a map with a list of id's per entity. * It also fetches links-to-be-deleted in another map with per link entity a list fkIds per fkColumnName. * * @param Schema $schema * @param ObjectEntity $oneEntity * @param <type> $id * @param array $objectsToBeDeleted [ * entityName => [ id, ... ] * ] * @param array $linksToBeDeleted [ * entityName => [ * fkColumnName => [ fkId, ... ] * ] * ] */ private function fetchTerminatedObjectsAndLinks(Schema $schema, ObjectEntity $oneEntity, $oneId, array &$objectsVisited, array &$objectsToBeDeleted, array &$linksToBeDeleted) { $mySQLi = $schema->getMySQLi(); $oneEntityName = $oneEntity->getName(); // Find any related object that is 'owned' by the current object. foreach ($oneEntity->getRelationships() as $relationship) { if ($relationship->getOwnerEntity() != $oneEntity) { // Do nothing if $oneEntity is not the owner of the other entity. continue; } // Get the 'other' entity of the relationship. $otherEntity = $relationship->getOppositeEntity($oneEntity); $otherEntityName = $otherEntity->getName(); if (!$otherEntity->isObjectEntity()) { // Deal with many-to-many relationships. $fkColumnNameOne = $relationship->getFkColumnName($oneEntity); // Prepare to add any fetched links to the map. if (!array_key_exists($otherEntityName, $linksToBeDeleted)) { $linksToBeDeleted[$otherEntityName] = array(); } $fksOfEntityToBeDeleted =& $linksToBeDeleted[$otherEntityName]; // Prepare to add the fkId. if (!array_key_exists($fkColumnNameOne, $fksOfEntityToBeDeleted)) { $fksOfEntityToBeDeleted[$fkColumnNameOne] = array(); } $fkIdsToBeDeleted =& $fksOfEntityToBeDeleted[$fkColumnNameOne]; $fkIdsToBeDeleted[] = $oneId; } else { // Deal with one-to-many relationships. $fetchedOtherIds = array(); // Prepare to add any fetched objects to the map. if (!array_key_exists($otherEntityName, $objectsToBeDeleted)) { $objectsToBeDeleted[$otherEntityName] = array(); } $otherIdsToBeDeleted =& $objectsToBeDeleted[$otherEntityName]; if ($relationship->getFkEntity() == $oneEntity) { // If the object at hand holds the foreign key, then fetch those foreign keys. $fkColumnName = $relationship->getFkColumnName($otherEntity); $oneObjectIdColumnName = $oneEntity->getObjectIdColumnName(); $queryString = "SELECT DISTINCT d.{$fkColumnName}"; if ($oneEntity->getStateIdColumnName() != NULL) { $queryString .= ", s.id_terminated"; } $queryString .= " FROM {$oneEntityName} d"; if ($oneEntity->getStateIdColumnName() != NULL) { $queryString .= ", " . DbConstants::TABLE_STATE . " s"; } $queryString .= " WHERE d.{$oneObjectIdColumnName} = {$oneId}"; if ($oneEntity->getStateIdColumnName() != NULL) { $queryString .= " AND s.id = d.{$oneObjectIdColumnName}"; } // Execute the query $queryResult = $mySQLi->query($queryString); if (!$queryResult) { throw new Exception("Error fetching foreign key '{$fkColumnName}' of {$oneEntityName}" . "[{$oneId}] - {$mySQLi->error}\n<!--\n{$queryString}\n-->"); } while ($queryData = $queryResult->fetch_assoc()) { $otherId = $queryData[$fkColumnName]; if ($otherId != NULL) { $fetchedOtherIds[] = $otherId; if ($queryData['id_terminated'] != NULL) { $otherIdsToBeDeleted[] = $otherId; } } } $queryResult->close(); } else { // The related objects hold the foreign keys, so fetch their ids. $fkColumnName = $relationship->getFkColumnName($oneEntity); $otherObjectIdColumnName = $otherEntity->getObjectIdColumnName(); $queryString = "SELECT DISTINCT d.{$otherObjectIdColumnName}"; if ($otherEntity->getStateIdColumnName() != NULL) { $queryString .= ", s.id_terminated"; } $queryString .= " FROM {$otherEntityName} d"; if ($otherEntity->getStateIdColumnName() != NULL) { $queryString .= ", " . DbConstants::TABLE_STATE . " s"; } $queryString .= " WHERE d.{$fkColumnName} = {$oneId}"; if ($otherEntity->getStateIdColumnName() != NULL) { $queryString .= " AND s.id = d." . $otherEntity->getStateIdColumnName(); } // Execute the query $queryResult = $mySQLi->query($queryString); if (!$queryResult) { throw new Exception("Error fetching terminated object ids of entity '{$otherEntityName}'" . " referring to {$oneEntityName}" . "[{$oneId}]" . " - {$mySQLi->error}\n<!--\n{$queryString}\n-->"); } while ($queryData = $queryResult->fetch_assoc()) { $otherId = $queryData[$otherObjectIdColumnName]; if ($otherId != NULL) { $fetchedOtherIds[] = $otherId; if ($queryData['id_terminated'] != NULL) { $otherIdsToBeDeleted[] = $otherId; } } } $queryResult->close(); } // If the other entity is not a link... if (count($fetchedOtherIds) > 0 && $relationship->getFkEntity()->isObjectEntity()) { // ...then continue recursing down the object hierarchy. // Keep track of all visited objects to prevent endless recursion. if (!array_key_exists($otherEntityName, $objectsVisited)) { $objectsVisited[$otherEntityName] = array(); } $otherIdsVisited =& $objectsVisited[$otherEntityName]; foreach ($fetchedOtherIds as $otherId) { if (!in_array($otherId, $otherIdsVisited)) { $otherIdsVisited[] = $otherId; $this->fetchTerminatedObjectsAndLinks($schema, $otherEntity, $otherId, $objectsVisited, $objectsToBeDeleted, $linksToBeDeleted); } } } } } }
private function getRelatedObjectId(ObjectEntity $foreignEntity) { $foreignId = NULL; foreach ($this->relatedObjects as $key => $relatedObject) { if ($foreignEntity == $relatedObject->entity) { if ($foreignId != NULL and $relatedObject->id != $foreignId) { throw new Exception("Error in relationship between '" . $this->entity->getName() . "' and '" . $foreignEntity->getName() . "'; expected one id, but got [{$foreignId}] and [{$relatedObject->id}].", RestResponse::CLIENT_ERROR); } $foreignId = $relatedObject->id; } } return $foreignId; }
private function fetchObjects(ObjectEntity $entity, $queryId, QueryContext $queryContext, Scope $defaultScope, $skipBinaries, DOMDocument $domDoc, DOMNode $xmlParent, array &$xmlElementsWithState, array &$allFetchedObjects = array()) { // Compose and execute the query. $mySQLi = $this->schema->getMySQLi(); $query = new QueryEntity($entity, $queryContext, $defaultScope, $queryId, $skipBinaries); $queryString = $query->getQueryString(); $queryResult = $mySQLi->query($queryString); if (!$queryResult) { throw new Exception("Error fetching objects of entity '" . $entity->getName() . "' - " . $mySQLi->error . "\n<!--\n{$queryString}\n-->"); } // Convert the query result into XML. while ($dbObject = $queryResult->fetch_assoc()) { $id = $dbObject[$entity->getObjectIdColumnName()]; $objectRef = new ObjectRef($entity, $id); // Beware of endless recursion! $scope = $this->detectEndlessRecursion($objectRef, $query->getScope(), $allFetchedObjects); // Create an XML node with the object id as an attribute and insert the node sorted by entity and id. $xmlElement = $domDoc->createElementNS($this->namespaceUri, $entity->getName()); $xmlElement->setAttribute(XmlConstants::ID, $id); $this->sortedInsertNode($xmlParent, $xmlElement); // Now determine what else to fetch, depending on the given scope. $hasTerminatedObjects = $this->fetchRelatedObjects($objectRef, $queryContext, $scope, $skipBinaries, $domDoc, $xmlElement, $xmlElementsWithState, $allFetchedObjects); // Add any object properties to the XML. $skipBinaryProperties = $skipBinaries || $scope->includes(Scope::TAG_PROPERTIES) == Scope::INCLUDES_REFS_ONLY; $publishedState = $this->addPropertiesToXML($dbObject, $entity, $skipBinaryProperties, $domDoc, $xmlElement, $xmlElementsWithState); if ($hasTerminatedObjects) { $publishedState = FALSE; } // Add a scope-attribute if it is not a reference, if the value is not empty and if it is not updatable. if ($xmlElement->childNodes->length > 0 and strlen($scope->getScopeValue()) > 0 and !$scope->isUpdatable()) { $xmlElement->setAttribute(XmlConstants::SCOPE, $scope->getScopeValue()); } // Notify the published-state to the parent node and its ancestors. $this->setPublishedStateAndPropagateToAncestors($xmlElement, $publishedState); } $queryResult->close(); }