Commit 05e92209 authored by Jochen Rau's avatar Jochen Rau
Browse files

[+TASK] Extbase (Persistence): Changed signature...

[+TASK] Extbase (Persistence): Changed signature Typo3DbBackend::getRowByIdentifier($tableName, array $identifier).
[+BUGFIX] Extbase (MVC): Tx_Extbase_MVC_Web_Response::getHeaders() is now aware of the requested protocol version by evaluating SERVER_PROTOCOL. Defaults to HTTP/1.0. Thanks to Morton Jonuschat who reported that issue. Resolves #5247.
[~TASK] Extbase (Persistence): Extbase is now aware of property annotations like "@var Tx_Extbase_Persistence_ObjectStorage<Tx_MyExt_Domain_Model_Foo> ". This can still be overwritten by the TCA config option "foreign_class".
[+BUGFIX] Extbase (Persistence): Fixed a problem where all mm relations are written on every change.
[+BUGFIX] Extbase (Persistence): Fixed a problem where the parent field was not updated with the number of children.
parent a3f56a6f
......@@ -141,10 +141,15 @@ class Tx_Extbase_Dispatcher {
$this->timeTrackPull();
self::$reflectionService->shutdown();
if (substr($response->getStatus(), 0, 3) === '303') {
$response->sendHeaders();
exit;
}
if (count($response->getAdditionalHeaderData()) > 0) {
$GLOBALS['TSFE']->additionalHeaderData[$request->getControllerExtensionName()] = implode("\n", $response->getAdditionalHeaderData());
}
$response->sendHeaders();
$this->timeTrackPull();
return $response->getContent();
}
......
......@@ -169,7 +169,8 @@ class Tx_Extbase_MVC_Web_Response extends Tx_Extbase_MVC_Response {
*/
public function getHeaders() {
$preparedHeaders = array();
$statusHeader = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->statusMessage;
$protocolVersion = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
$statusHeader = $protocolVersion . ' ' . $this->statusCode . ' ' . $this->statusMessage;
$preparedHeaders[] = $statusHeader;
foreach ($this->headers as $name => $values) {
......
......@@ -351,8 +351,16 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
$columnMap = $dataMap->getColumnMap($propertyName);
$childClassName = $columnMap->getChildClassName();
$propertyMetaData = $classSchema->getProperty($propertyName);
$propertyType = $propertyMetaData['type'];
if (!empty($childClassName)) {
$propertyType = $childClassName;
} elseif (!empty($propertyMetaData['type'])) {
$propertyType = $propertyMetaData['type'];
} else {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Could not determine the class of the child objects.', 1251315965);
}
// FIXME enable property-type check
// $this->checkPropertyType($propertyType, $propertyValue);
if (($propertyValue !== NULL) && $propertyType === 'Tx_Extbase_Persistence_ObjectStorage') {
......@@ -371,7 +379,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->insertObject($propertyValue);
$queue[] = $propertyValue;
} else {
$this->persistValueObject($propertyValue, $object, $propertyName);
$this->persistValueObject($propertyValue);
}
}
$row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
......@@ -423,15 +431,10 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
*
* @return void
*/
protected function persistValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject = NULL, $parentPropertyName = NULL, $sortingPosition = 1) {
protected function persistValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object, $sortingPosition = 1) {
$result = $this->getUidOfAlreadyPersistedValueObject($object);
if ($result !== FALSE) {
$object->_setProperty('uid', (int)$result);
if (!is_null($parentObject) && is_string($parentPropertyName) && strlen($parentPropertyName) > 0) {
if($this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName)->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
$this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
}
}
} elseif ($object->_isNew()) {
$row = array();
$className = get_class($object);
......@@ -466,7 +469,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
/**
* Persists a relation. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
* Persists a an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
* gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
* deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
*
......@@ -488,8 +491,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$currentUids = t3lib_div::intExplode(',', $currentFieldValue);
}
}
$updateParent = FALSE;
$removedObjectUids = array();
foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
......@@ -498,9 +500,12 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
$removedObjectUids[] = $removedObject->getUid();
}
$updateParent = TRUE;
}
if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
$this->deleteAllRelationsFromRelationtable($parentObject, $propertyName);
}
$insertedObjectUids = array();
$sortingPosition = 1;
foreach ($objectStorage as $object) {
......@@ -508,23 +513,20 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
$this->insertObject($object);
} else {
$this->persistValueObject($object, $parentObject, $propertyName, $sortingPosition);
$this->persistValueObject($object, $sortingPosition);
}
$updateParent = TRUE;
$insertedObjectUids[] = $object->getUid();
}
$this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
$sortingPosition++;
}
if ($updateParent === TRUE) {
if ($columnMap->getParentKeyFieldName() === NULL) {
$newUids = array_diff($currentUids, $removedObjectUids);
$newUids = array_merge($newUids, $insertedObjectUids);
$row[$columnMap->getColumnName()] = implode(',', $newUids);
} else {
$row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
}
if ($columnMap->getParentKeyFieldName() === NULL) {
$newUids = array_diff($currentUids, $removedObjectUids);
$newUids = array_merge($newUids, $insertedObjectUids);
$row[$columnMap->getColumnName()] = implode(',', $newUids);
} else {
$row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
}
}
......@@ -686,6 +688,26 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
return $res;
}
/**
* Delete all mm-relations of a parent from a relation table
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
* @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
* @return void
*/
protected function deleteAllRelationsFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
$dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
$columnMap = $dataMap->getColumnMap($parentPropertyName);
$relationTableName = $columnMap->getRelationTableName();
$res = $this->storageBackend->removeRow(
$relationTableName,
array(
$columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
),
FALSE);
return $res;
}
/**
* Delete an mm-relation from a relation table
*
......
......@@ -258,15 +258,16 @@ class Tx_Extbase_Persistence_Mapper_DataMapper implements t3lib_Singleton {
protected function getPreparedQuery(Tx_Extbase_DomainObject_AbstractEntity $parentObject, $propertyName, $fieldValue = '') {
$columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
$queryFactory = t3lib_div::makeInstance('Tx_Extbase_Persistence_QueryFactory');
$query = $queryFactory->create($columnMap->getChildClassName());
// TODO: This is an ugly hack, just ignoring the storage page state from here. Actually, the query settings would have to be passed into the DataMapper, so we can respect
// enableFields and storage page settings.
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$parentKeyFieldName = $columnMap->getParentKeyFieldName();
$childSortByFieldName = $columnMap->getChildSortByFieldName();
if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
$query = $queryFactory->create($this->getType($parentObject, $propertyName));
$result = $query->matching($query->withUid(intval($fieldValue)));
} elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
$query = $queryFactory->create($this->getElementType($parentObject, $propertyName));
// TODO: This is an ugly hack, just ignoring the storage page state from here. Actually, the query settings would have to be passed into the DataMapper, so we can respect
// enableFields and storage page settings.
$query->getQuerySettings()->setRespectStoragePage(FALSE);
if (!empty($childSortByFieldName)) {
$query->setOrderings(array($childSortByFieldName => Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING));
}
......@@ -276,9 +277,13 @@ class Tx_Extbase_Persistence_Mapper_DataMapper implements t3lib_Singleton {
$result = $query->matching($query->equals('uid', t3lib_div::intExplode(',', $fieldValue)));
}
} elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
$query = $queryFactory->create($this->getElementType($parentObject, $propertyName));
// TODO: This is an ugly hack, just ignoring the storage page state from here. Actually, the query settings would have to be passed into the DataMapper, so we can respect
// enableFields and storage page settings.
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$relationTableName = $columnMap->getRelationTableName();
$left = $this->QOMFactory->selector(NULL, $relationTableName);
$childClassName = $columnMap->getChildClassName();
$childClassName = $this->getElementType($parentObject, $propertyName);
$childTableName = $columnMap->getChildTableName();
$right = $this->QOMFactory->selector($childClassName, $childTableName);
$joinCondition = $this->QOMFactory->equiJoinCondition($relationTableName, $columnMap->getChildKeyFieldName(), $childTableName, 'uid');
......@@ -307,11 +312,13 @@ class Tx_Extbase_Persistence_Mapper_DataMapper implements t3lib_Singleton {
* @return void
*/
public function mapResultToPropertyValue(Tx_Extbase_DomainObject_AbstractEntity $parentObject, $propertyName, $result) {
$propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
if ($result instanceof Tx_Extbase_Persistence_LoadingStrategyInterface) {
$propertyValue = $result;
} else {
if (in_array($propertyMetaData['type'], array('array', 'ArrayObject', 'Tx_Extbase_Persistence_ObjectStorage')) && strpos($propertyMetaData['elementType'], '_') !== FALSE) {
$propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
$columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
if (in_array($propertyMetaData['type'], array('array', 'ArrayObject', 'Tx_Extbase_Persistence_ObjectStorage'))) {
$elementType = $this->getElementType($parentObject, $propertyName);
$objects = array();
foreach ($result as $value) {
$objects[] = $value;
......@@ -432,6 +439,48 @@ class Tx_Extbase_Persistence_Mapper_DataMapper implements t3lib_Singleton {
}
return Tx_Extbase_Utility_Extension::convertCamelCaseToLowerCaseUnderscored($propertyName);
}
/**
* Returns the type of a child object.
*
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject The object instance this proxy is part of
* @param string $propertyName The name of the proxied property in it's parent
* @return string The class name of the child object
*/
protected function getType(Tx_Extbase_DomainObject_AbstractEntity $parentObject, $propertyName) {
$propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
$columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
$childClassName = $columnMap->getChildClassName();
if (!empty($childClassName)) {
$elementType = $childClassName;
} elseif (!empty($propertyMetaData['type'])) {
$elementType = $propertyMetaData['type'];
} else {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Could not determine the child object object type.', 1251315967);
}
return $elementType;
}
/**
* Returns the type of the elements inside an ObjectStorage or array.
*
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject The object instance this proxy is part of
* @param string $propertyName The name of the proxied property in it's parent
* @return string The class name of the elements inside an ObjectStorage
*/
protected function getElementType(Tx_Extbase_DomainObject_AbstractEntity $parentObject, $propertyName) {
$propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
$columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
$childClassName = $columnMap->getChildClassName();
if (!empty($childClassName)) {
$elementType = $childClassName;
} elseif (!empty($propertyMetaData['elementType'])) {
$elementType = $propertyMetaData['elementType'];
} else {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Could not determine the type of the contained objects.', 1251315966);
}
return $elementType;
}
}
?>
\ No newline at end of file
......@@ -58,11 +58,11 @@ interface Tx_Extbase_Persistence_Storage_BackendInterface {
* Deletes a row in the storage
*
* @param string $tableName The database table name
* @param array $identifyer An array of identifyer array('fieldname' => value). This array will be transformed to a WHERE clause
* @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
* @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
* @return void
*/
public function removeRow($tableName, array $identifyer, $isRelation = FALSE);
public function removeRow($tableName, array $identifier, $isRelation = FALSE);
/**
* Returns an array with rows matching the query.
......
......@@ -159,20 +159,13 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
* Deletes a row in the storage
*
* @param string $tableName The database table name
* @param array $identifyer An array of identifyer array('fieldname' => value). This array will be transformed to a WHERE clause
* @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
* @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
* @param boolean $isRelation TRUE if we are currently manipulating a relation table, FALSE by default
* @return void
*/
public function removeRow($tableName, array $identifyer, $isRelation = FALSE) {
$fieldNames = array_keys($identifyer);
$suffixedFieldNames = array();
foreach ($fieldNames as $fieldName) {
$suffixedFieldNames[] = $fieldName . '=?';
}
$parameters = array_values($identifyer);
$statement = 'DELETE FROM ' . $tableName;
$statement .= ' WHERE ' . implode(' AND ', $suffixedFieldNames);
$this->replacePlaceholders($statement, $parameters);
public function removeRow($tableName, array $identifier, $isRelation = FALSE) {
$statement = 'DELETE FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
$this->replacePlaceholders($statement, $identifier);
if (!$isRelation) {
$this->clearPageCache($tableName, $uid, $isRelation);
}
......@@ -188,10 +181,9 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
* @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap The Data Map
* @return array|FALSE
*/
public function getRowByIdentifier($identifier, Tx_Extbase_Persistence_Mapper_DataMap $dataMap) {
$tableName = $dataMap->getTableName();
$statement = 'SELECT * FROM ' . $tableName . ' WHERE uid=?';
$this->replacePlaceholders($statement, array($identifier));
public function getRowByIdentifier($tableName, array $identifier) {
$statement = 'SELECT * FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
$this->replacePlaceholders($statement, $identifier);
// debug($statement,-2);
$res = $this->databaseHandle->sql_query($statement);
$this->checkSqlErrors();
......@@ -202,6 +194,15 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
return FALSE;
}
}
protected function parseIdentifier(array $identifier) {
$fieldNames = array_keys($identifier);
$suffixedFieldNames = array();
foreach ($fieldNames as $fieldName) {
$suffixedFieldNames[] = $fieldName . '=?';
}
return implode(' AND ', $suffixedFieldNames);
}
/**
* Returns an array with tuples matching the query.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment