[+TASK] Extbase (Persistence): Changed signature Typo3DbBackend::getRowByIdentifier...
authorJochen Rau <j.rau@web.de>
Wed, 25 Nov 2009 10:33:04 +0000 (10:33 +0000)
committerJochen Rau <j.rau@web.de>
Wed, 25 Nov 2009 10:33:04 +0000 (10:33 +0000)
[+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.

typo3/sysext/extbase/Classes/Dispatcher.php
typo3/sysext/extbase/Classes/MVC/Web/Response.php
typo3/sysext/extbase/Classes/Persistence/Backend.php
typo3/sysext/extbase/Classes/Persistence/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/Persistence/Storage/BackendInterface.php
typo3/sysext/extbase/Classes/Persistence/Storage/Typo3DbBackend.php

index cffde29..9fce7c2 100644 (file)
@@ -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();
        }
index 25c8a06..c3c13eb 100755 (executable)
@@ -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) {
index e25dfac..8bd923e 100644 (file)
@@ -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);
                }
        }
        
@@ -687,6 +689,26 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
        }
 
        /**
+        * 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
         *
         * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
index 4919956..3f83585 100644 (file)
@@ -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
index 839ba99..e39cfcc 100644 (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.
index 4a0178f..8fd3bd2 100644 (file)
@@ -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.