[+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.
$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();
}
*/
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) {
}
$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') {
$this->insertObject($propertyValue);
$queue[] = $propertyValue;
} else {
- $this->persistValueObject($propertyValue, $object, $propertyName);
+ $this->persistValueObject($propertyValue);
}
}
$row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
*
* @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);
}
/**
- * 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.
*
$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') {
$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) {
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);
}
}
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
*
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));
}
$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');
* @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;
}
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
* 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.
* 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);
}
* @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();
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.