* implemented recursive deletion of objects (deletes the object its 1:n related...
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Mapper / TX_EXTMVC_Persistence_Mapper_TcaMapper.php
index 17da9ae..1085bad 100644 (file)
@@ -39,6 +39,13 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         **/
        protected $cObj;
                
+       /**
+        * The persistence session
+        *
+        * @var 
+        **/
+       protected $session;
+               
        /**
         * Constructs a new mapper
         *
@@ -46,31 +53,20 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         */
        public function __construct() {
                $this->cObj = t3lib_div::makeInstance('tslib_cObj');
+               $this->session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session');
                $GLOBALS['TSFE']->includeTCA();
        }
-               
-       /**
-        * Returns all objects of the given class name
-        *
-        * @return array An array of objects, empty if no objects found
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       public function loadAll($className) {
-               return $this->reconstituteObjects($this->fetch($this->getTableName($className)));
-       }
        
        /**
-        * Finds objects matching 'property=xyz'
+        * Finds objects matching property="xyz"
         *
         * @param string $propertyName The name of the property (will be chekced by a white list)
-        * @param string $arguments The arguments of the magic findBy method
+        * @param string $arguments The WHERE statement
         * @return void
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       public function loadWhere($className, $propertyName, $arguments) {
-               $tableName = $this->getTableName($className);
-               $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($arguments[0], $tableName);
-               return $this->reconstituteObjects($className, $this->fetch($tableName, $where));
+       public function findWhere($className, $where = '1=1') {
+               return $this->reconstituteObjects($className, $this->fetch($className, $where));
        }
        
        /**
@@ -84,7 +80,8 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         * @return void
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       private function fetch($tableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+       public function fetch($className, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+               $tableName = $this->getTableName($className);
                $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        '*', // TODO limit fetched fields
                        $tableName,
@@ -102,7 +99,7 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         *
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       private function fetchOneToMany($parentObject, $parentField, $tableName, $where = '', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+       public function fetchOneToMany($parentObject, $parentField, $tableName, $where = '', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
                $where .= ' ' . $parentField . '=' . intval($parentObject->getUid());
                return $this->fetch($tableName, $where, $groupBy, $orderBy, $limit);
        }       
@@ -112,7 +109,7 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         *
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       private function fetchManyToMany($parentObject, $foreignTableName, $relationTableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+       public function fetchManyToMany($parentObject, $foreignTableName, $relationTableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
                $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        $foreignTableName . '.*, ' . $relationTableName . '.*',
                        $foreignTableName . ' LEFT JOIN ' . $relationTableName . ' ON (' . $foreignTableName . '.uid=' . $relationTableName . '.uid_foreign)',
@@ -133,20 +130,28 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         * @return array An array of reconstituted domain objects
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       protected function reconstituteObjects($className, array $rows, $depth = 0) {
-               if ($depth > 10) throw new TX_EXTMVC_Persistence_Exception_RecursionTooDeep('The maximum depth of ' . $depth . ' recursions was reached.', 1233352348);
+       protected function reconstituteObjects($className, array $rows) {
+               // TODO if ($depth > 10) throw new TX_EXTMVC_Persistence_Exception_RecursionTooDeep('The maximum depth of ' . $depth . ' recursions was reached.', 1233352348); 
                foreach ($rows as $row) {
-                       $object = $this->reconstituteObject($className, $row);
-                       foreach ($object->getOneToManyRelations() as $propertyName => $tcaColumnConfiguration) {
-                               $relatedRows = $this->fetchOneToMany($object, $tcaColumnConfiguration['foreign_field'], $tcaColumnConfiguration['foreign_table']);
-                               $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
-                               $object->_reconstituteProperty($propertyName, $relatedObjects);
+                       $propertiesToReconstitute = array();
+                       foreach ($row as $fieldName => $fieldValue) {
+                               $propertyName = TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($fieldName);
+                               $propertiesToReconstitute[$propertyName] = $this->convertFieldValueToPropertyValue($className, $propertyName, $fieldValue);
                        }
-                       foreach ($object->getManyToManyRelations() as $propertyName => $tcaColumnConfiguration) {
-                               $relatedRows = $this->fetchManyToMany($object, $tcaColumnConfiguration['foreign_table'], $tcaColumnConfiguration['MM']);
-                               $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
-                               $object->_reconstituteProperty($propertyName, $relatedObjects);
+                       $object = $this->reconstituteObject($className, $propertiesToReconstitute);
+                       $properties = $object->_getProperties();
+                       foreach ($properties as $propertyName => $propertyValue) {
+                               if ($this->isOneToManyRelation($className, $propertyName)) {
+                                       $relatedRows = $this->fetchOneToMany($object, $this->getForeignUidField($className, $propertyName), $this->getForeignTableName($className, $propertyName));
+                                       $relatedObjects = $this->reconstituteObjects($this->getForeignClass($className, $propertyName), $relatedRows, $depth);
+                                       $object->_reconstituteProperty($propertyName, $relatedObjects);
+                               } elseif ($this->isManyToManyRelation($className, $propertyName)) {
+                                       $relatedRows = $this->fetchManyToMany($object, $this->getForeignTableName($className, $propertyName), $this->getRelationTableName($className, $propertyName));
+                                       $relatedObjects = $this->reconstituteObjects($this->getForeignClass($className, $propertyName), $relatedRows, $depth);
+                                       $object->_reconstituteProperty($propertyName, $relatedObjects);
+                               }
                        }
+                       $this->session->registerReconstitutedObject($object);
                        $objects[] = $object;
                }
                return $objects;
@@ -159,7 +164,6 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
         * @param array $properties The names of properties and their values which should be set during the reconstitution
         * @return object The reconstituted object
         * @author Robert Lemke <robert@typo3.org>
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        protected function reconstituteObject($className, array $properties = array()) {
                // those objects will be fetched from within the __wakeup() method of the object...
@@ -167,94 +171,471 @@ class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
                $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
                unset($GLOBALS['EXTMVC']['reconstituteObject']);
                return $object;
-       }       
-
+       }
+       
        /**
-        * Inserts an object in the database.
+        * Persists all objects of a persistence session
         *
+        * @param string $session The persistence session
         * @return void
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       public function insert(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
-               $row = array(
-                       'pid' => 0, // FIXME
-                       'tstamp' => time(),
-                       'crdate' => time(),
-                       // FIXME static fields
-                       'name' => $object->getName(),
-                       'description' => $object->getDescription()
-                       // 'logo' => $object->getLogo(),
-                       );
+       public function persistAll($session) {
+               $this->session = $session;
+               
+               // first, persit all aggregate root objects
+               $aggregateRootClassNames = $this->session->getAggregateRootClassNames();
+               foreach ($aggregateRootClassNames as $className) {
+                       $this->persistObjects($className);
+               }
+               
+               // persist all remaining objects
+               $this->persistObjects();
+       }
+       
+       /**
+        * Persists all objects of a persitance session that are of a given class. If there
+        * is no class specified, it persits all objects of a session.
+        *
+        * @param string $className Name of the class of the objects to be persisted
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function persistObjects($className = NULL) {
+               foreach ($this->session->getAddedObjects($className) as $object) {
+                       $this->insertObject($object);
+                       $this->session->unregisterAddedObject($object);
+               }
+               foreach ($this->session->getDirtyObjects($className) as $object) {
+                       $this->updateObject($object);
+                       $this->session->unregisterObject($object); // TODO is this necessary?
+                       $this->session->registerReconstitutedObject($object);
+               }
+               foreach ($this->session->getRemovedObjects($className) as $object) {
+                       $this->deleteObject($object);
+                       $this->session->unregisterRemovedObject($object);
+               }       
+       }
+       
+       /**
+        * Inserts an object to the database.
+        *
+        * @return void
+        * @author Karsten Dambekalns <karsten@typo3.org>
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function insertObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL) {
+               $queuedRelations = array();
+               $rowToInsert = array();
+               $properties = $object->_getProperties();
+               foreach ($properties as $propertyName => $propertyValue) {
+                       if ($this->isOneToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                               $rowToInsert[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = count($properties[$propertyName]);
+                       } elseif ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                               $rowToInsert[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = count($properties[$propertyName]);
+                       } else {
+                               $rowToInsert[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = $this->convertPropertyValueToFieldValue($propertyValue);
+                       }
+               }
+               
+               $rowToInsert['pid'] = 0; // FIXME
+               $rowToInsert['tstamp'] = time();
+               if ($parentObject !== NULL && $parentPropertyName !== NULL) {
+                       $foreignUidfield = $this->getForeignUidField(get_class($parentObject), $parentPropertyName);
+                       if ($foreignUidfield !== NULL) {
+                               $rowToInsert[$foreignUidfield] = $parentObject->getUid();
+                       }
+                       $foreignTablefield = $this->getForeignTableField(get_class($parentObject), $parentPropertyName);
+                       if ($foreignTablefield !== NULL) {
+                               $rowToInsert[$foreignTablefield] = $this->getTableName(get_class($parentObject));
+                       }
+               }
+               $tableName = $this->getTableName(get_class($object));
                $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
-                       $this->getTableName($this->getClassName($object)),
-                       $row
+                       $tableName,
+                       $rowToInsert
                        );
+
+               $object->_reconstituteProperty('uid', $GLOBALS['TYPO3_DB']->sql_insert_id());
+               // var_dump($object);
+               
+               foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                       foreach ($relatedObjects as $relatedObject) {
+                               if (!$this->session->isReconstitutedObject($relatedObject)) {
+                                       $this->insertObject($relatedObject, $object, $propertyName);
+                                       if ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                                               $this->insertRelation($object, $propertyName, $relatedObject);
+                                       }
+                               }
+                       }
+               }
+               
        }
        
        /**
-        * Updates an object
+        * Updates a modified object in the database
         *
         * @return void
+        * @author Karsten Dambekalns <karsten@typo3.org>
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       public function update(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
+       public function updateObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL) {
+               $queuedRelations = array();
+               $fieldsToUpdate = array();
+               $properties = $object->_getDirtyProperties();
+               foreach ($properties as $propertyName => $propertyValue) {
+                       if ($this->isOneToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                               $fieldsToUpdate[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = count($properties[$propertyName]);
+                       } elseif ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                               $fieldsToUpdate[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = count($properties[$propertyName]);
+                       } else {
+                               $fieldsToUpdate[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)] = $this->convertPropertyValueToFieldValue($propertyValue);
+                       }
+               }
+               
+               $fieldsToUpdate['crdate'] = time();
+               if (!empty($GLOBALS['TSFE']->fe_user->user['uid'])) {
+                       $fieldsToUpdate['cuser_id'] = $GLOBALS['TSFE']->fe_user->user['uid'];
+               }
+               if ($parentObject !== NULL && $parentPropertyName !== NULL) {
+                       $foreignUidfield = $this->getForeignUidField(get_class($parentObject), $parentPropertyName);
+                       if ($foreignUidfield !== NULL) {
+                               $fieldsToUpdate[$foreignUidfield] = $parentObject->getUid();
+                       }
+                       $foreignTablefield = $this->getForeignTableField(get_class($parentObject), $parentPropertyName);
+                       if ($foreignTablefield !== NULL) {
+                               $fieldsToUpdate[$foreignTablefield] = $this->getTableName(get_class($parentObject));
+                       }
+               }
+               $tableName = $this->getTableName(get_class($object));
+               $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                       $tableName,
+                       'uid=' . $object->getUid(),
+                       $fieldsToUpdate
+                       );
+
+               // var_dump($object);
 
+               foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                       foreach ($relatedObjects as $relatedObject) {
+                               if (!$this->session->isReconstitutedObject($relatedObject)) {
+                                       $this->insertObject($relatedObject, $object, $propertyName);
+                                       if ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                                               $this->insertRelation($object, $propertyName, $relatedObject);
+                                       }
+                               }
+                       }
+               }
+               
        }
-       
+
        /**
-        * Deletes an object
+        * Deletes an object, it's 1:n related objects, and the m:n relations in relation tables (but not the m:n related objects!)
         *
         * @return void
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       public function delete(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $onlyMarkAsDeleted = TRUE) {
-               $tableName = $this->getTableName($this->getClassName($object));
-               if ($onlyMarkAsDeleted) {
-                       $deletedColumnName = $this->getDeletedColumnName($tableName);
-                       if (empty($deletedColumnName)) throw new Exception('Could not mark object as deleted in table "' . $tableName . '"');
-               $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
-                               $this->getTableName($object),
-                               'uid = ' . intval($object->getUid()),
-                               array($deletedColumnName => 1)
-                               );
-               } else {
-                       // TODO remove associated objects
-                       
-                       $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
-                               $this->getTableName($object),
-                               'uid=' . intval($object->getUid())
-                               );
+       public function deleteObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL) {
+               $queuedRelations = array();
+               $properties = $object->_getProperties();
+               foreach ($properties as $propertyName => $propertyValue) {
+                       if ($this->isOneToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                       } elseif ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                               $queuedRelations = t3lib_div::array_merge_recursive_overrule($queuedRelations, array($propertyName => $propertyValue));
+                       }
+               }
+               
+               $tableName = $this->getTableName(get_class($object));
+               $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $tableName,
+                       'uid=' . $object->getUid()
+                       );
+
+               foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                       foreach ($relatedObjects as $relatedObject) {
+                               if ($this->session->isReconstitutedObject($relatedObject)) {
+                                       if ($this->isOneToManyRelation(get_class($object), $propertyName)) {
+                                               $this->deleteObject($relatedObject, $object, $propertyName);
+                                       } elseif ($this->isManyToManyRelation(get_class($object), $propertyName)) {
+                                               $this->deleteRelations($object, $propertyName, $relatedObject);
+                                       }
+                                       
+                               }
+                       }
                }
+               
        }
        
-       public function getColumns(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
-               t3lib_div::loadTCA($this->getTableName($this->getClassName($object)));
-               return $GLOBALS['TCA'][$this->getTableName($this->getClassName($object))]['columns'];
+       /**
+        * Inserts relation to a relation table
+        *
+        * @param TX_EXTMVC_DomainObject_AbstractDomainObject $parentObject The parent object
+        * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in 
+        * @param TX_EXTMVC_DomainObject_AbstractDomainObject $relatedObject The related object
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function insertRelation(TX_EXTMVC_DomainObject_AbstractDomainObject $parentObject, $parentPropertyName, TX_EXTMVC_DomainObject_AbstractDomainObject $relatedObject) {
+               $rowToInsert = array(
+                       'uid_local' => $parentObject->getUid(),
+                       'uid_foreign' => $relatedObject->getUid(),
+                       'tablenames' => $this->getTableName(get_class($parentObject)),
+                       'sorting' => 9999 // TODO sorting of mm table items
+                       );
+               $tableName = $this->getRelationTableName(get_class($parentObject), $parentPropertyName);
+               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+                       $tableName,
+                       $rowToInsert
+                       );
        }
-               
-       protected function getClassName(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
-               return get_class($object);
+       
+       /**
+        * Inserts relation to a relation table
+        *
+        * @param TX_EXTMVC_DomainObject_AbstractDomainObject $parentObject The parent object
+        * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in 
+        * @param TX_EXTMVC_DomainObject_AbstractDomainObject $relatedObject The related object
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function deleteRelations(TX_EXTMVC_DomainObject_AbstractDomainObject $parentObject, $parentPropertyName, TX_EXTMVC_DomainObject_AbstractDomainObject $relatedObject) {
+               $tableName = $this->getRelationTableName(get_class($parentObject), $parentPropertyName);
+               $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $tableName,
+                       'uid_local=' . $parentObject->getUid()
+                       );
        }
        
+       /**
+        * Returns all columns configured in $TCA for a given class
+        *
+        * @param string $className The class name 
+        * @return array The column configurations from $TCA
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function getColumns($className) {
+               $tableName = $this->getTableName($className);
+               t3lib_div::loadTCA($tableName);
+               return $GLOBALS['TCA'][$tableName]['columns'];
+       }
+       
+       /**
+        * Returns a table name for a given class
+        *
+        * @param string $className The class name
+        * @return string The table name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
        protected function getTableName($className) {
+               // TODO implement table name aliases
                return strtolower($className);
        }
-       
-       protected function getDeletedColumnName($tableName) {
+
+       /**
+        * Returns the name of a column indicating the 'deleted' state of the row
+        *
+        * @param string $className The class name
+        * @return string The class name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */     
+       protected function getDeletedColumnName($className) {
+               $this->getTableName($className);
                return $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
        }
        
-       protected function getHiddenColumnName($tableName) {
-               return $GLOBALS['TCA'][$tableNAme]['ctrl']['enablecolumns']['disabled'];
+       /**
+        * Returns the name of a column indicating the 'hidden' state of the row
+        *
+        * @param string $className The class name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */     
+       protected function getHiddenColumnName($className) {;
+               $this->getTableName($className);
+               return $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns']['disabled'];
+       }
+
+       /**
+        * Returns TRUE if the given property corresponds to one to many relation in the database
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return boolean TRUE if the given property corresponds to one to many relation in the database
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function isOneToManyRelation($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               $columnConfiguration = $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config'];
+               if (array_key_exists('foreign_table', $columnConfiguration) && !array_key_exists('MM', $columnConfiguration)) return TRUE;
+               return FALSE;
        }
        
-       public function isPersistable(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $propertyName) {
-               $columns = $this->getColumns($object);
-               foreach ($columns as $columnName => $columnConfiguration) {
-                       if (TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($columnName) == $propertyName) return TRUE;
-               }
+       /**
+        * Returns TRUE if the given property corresponds to many to many relation in the database
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return boolean TRUE if the given property corresponds to many to many relation in the database
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function isManyToManyRelation($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               $columnConfiguration = $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config'];
+               if (array_key_exists('foreign_table', $columnConfiguration) && array_key_exists('MM', $columnConfiguration)) return TRUE;
                return FALSE;
        }
        
+       /**
+        * Returns the foreign class name for a given parent class and property
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return string The foreign class name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function getForeignClass($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['foreign_class'];
+       }
+       
+       /**
+        * Returns the foreign table name for a given parent class and property
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return string The foreign table name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function getForeignTableName($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['foreign_table'];
+       }
+       
+       /**
+        * Returns the foreign uid field name for a given parent class and property
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return string The foreign uid field name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function getForeignUidField($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['foreign_field'];
+       }
+       
+       /**
+        * Returns the foreign table field name for a given parent class and property
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return string The foreign table field name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function getForeignTableField($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['foreign_table_field'];
+       }
+       
+       /**
+        * Returns the relation table name for a given parent class and property
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return string The relation table name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function getRelationTableName($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['MM'];
+       }
+               
+       /**
+        * Returns TRUE if the property of a given class is of type date (as configured in $TCA)
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return boolean TRUE if the property of a given class is of type date (as configured in $TCA)
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function isOfTypeDate($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return strpos($columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['eval'], 'date') !== FALSE
+                       || strpos($columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['eval'], 'datetime') !== FALSE;
+       }
+       
+       /**
+        * Returns TRUE if the property of a given class is of type boolean (as configured in $TCA)
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return boolean TRUE if the property of a given class is of type boolean (as configured in $TCA)
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function isOfTypeBoolean($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               return $columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['type'] === 'check' 
+                       && empty($columns[TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName)]['config']['items']);
+       }
+       
+       /**
+        * Returns TRUE if the property is persistable (configured in $TCA)
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @return boolean TRUE if the property is persistable (configured in $TCA)
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       public function isPersistableProperty($className, $propertyName) {
+               $columns = $this->getColumns($this->getTableName($className));
+               if (array_key_exists(TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($propertyName), $columns)) return TRUE;
+               return FALSE;
+       }
+       
+       /**
+        * Converts a value from a database field type to a property type
+        *
+        * @param string $className The class name
+        * @param string $propertyName The property name
+        * @param mixed $fieldValue The field value
+        * @return mixed The converted value
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function convertFieldValueToPropertyValue($className, $propertyName, $fieldValue) {
+               if ($this->isOfTypeDate($className, $propertyName)) {
+                       $convertedValue = new DateTime(strftime('%Y-%m-%d %H:%M', $fieldValue), new DateTimeZone('UTC'));
+               } elseif ($this->isOfTypeBoolean($className, $propertyName)) {
+                       if ($fieldValue === '0') {
+                               $convertedValue = FALSE;
+                       } else {
+                               $convertedValue = TRUE;
+                       }
+               } else {
+                       $convertedValue = $fieldValue;
+               }
+               return $convertedValue;
+       }
+       
+       /**
+        * Converts a value from a property type to a database field type
+        *
+        * @param mixed $propertyValue The property value
+        * @return mixed The converted value
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */             
+       protected function convertPropertyValueToFieldValue($propertyValue) {
+               if ($propertyValue instanceof DateTime) {
+                       $convertedValue = $propertyValue->format('U');
+               } elseif (is_bool($propertyValue)) {
+                       $convertedValue = $propertyValue ? 1 : 0;
+               } else {
+                       $convertedValue = $propertyValue;
+               }
+               return $convertedValue;
+       }
+       
 }
 ?>
\ No newline at end of file