* renamed TcaMapper to ObjectRelationalMapper
authorJochen Rau <j.rau@web.de>
Tue, 17 Feb 2009 04:59:25 +0000 (04:59 +0000)
committerJochen Rau <j.rau@web.de>
Tue, 17 Feb 2009 04:59:25 +0000 (04:59 +0000)
* added call to template method initializeObject() (necessary for domain objects to restore settings while __construct() AND __wakeup())
* renamed initCleanProperties() to initializeCleanProperties()
* cleaned up _getProperties() in domain objects
* huge refactoring of the data mapping
  - introduced the meta-data mapping pattern
  - added a generic data map for every $TCA table
  - added a column map for every column in a table
  - Object Relational Mapper is now responsible only for mapping the objects to tables
* the Object Storage now implements ArrayAccess
* added toArray() method to the Object Storage
* added generic "findOneBy[PropertyName]()" method to the Repository

typo3/sysext/extbase/Classes/DomainObject/TX_EXTMVC_DomainObject_AbstractDomainObject.php
typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ColumnMap.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_DataMap.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php [deleted file]
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_ObjectStorage.php
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_Repository.php
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_Session.php
typo3/sysext/extbase/Documentation/todo.txt
typo3/sysext/extbase/Tests/TX_EXTMVC_Persistence_Session_testcase.php
typo3/sysext/extbase/class.tx_extmvc_dispatcher.php

index adc28bb..e2e1d3f 100644 (file)
@@ -21,7 +21,7 @@ declare(ENCODING = 'utf-8');
  * The TYPO3 project - inspiring people to share!                         *
  *                                                                        */
 
-require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper.php');
 
 /**
  * A generic Domain Object
@@ -37,12 +37,17 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
        protected $uid;
 
        /**
-        * An array of properties filled with database values of columns configured in $TCA.
+        * The generic constructor. If you want to implement your own __constructor() method in your Domain Object you have to call 
+        * $this->initializeObject() in the first line of your constructor.
         *
         * @var array
         */
        private $cleanProperties = NULL;
        
+       public function __construct() {
+               $this->initializeObject();
+       }
+               
        /**
         * This is the magic wakeup() method. It's invoked by the unserialize statement in the reconstitution process
         * of the object. If you want to implement your own __wakeup() method in your Domain Object you have to call 
@@ -55,7 +60,17 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
                foreach ($GLOBALS['EXTMVC']['reconstituteObject']['properties'] as $propertyName => $value) {
                        $this->_reconstituteProperty($propertyName, $value);
                }
-               $this->initCleanProperties();
+               $this->initializeObject();
+               $this->initializeCleanProperties();
+       }
+       
+       /**
+        * A template function to initialize an object
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function initializeObject() {
        }
        
        /**
@@ -92,7 +107,7 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        public function _memorizeCleanState() {
-               $this->initCleanProperties();
+               $this->initializeCleanProperties();
                $cleanProperties = array();
                foreach ($this->cleanProperties as $propertyName => $propertyValue) {
                        $cleanProperties[$propertyName] = $this->$propertyName;
@@ -116,21 +131,13 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
        }
 
        /**
-        * Returns a hash map of persitable properties and $values
+        * Returns a hash map of property names and property values
         *
-        * @return boolean
+        * @return array The properties
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        public function _getProperties() {
-               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper');
-               $properties = get_object_vars($this);
-               $persistableProperties = array();
-               foreach ($properties as $propertyName => $propertyValue) {
-                       if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
-                               $persistableProperties[$propertyName] = $propertyValue;
-                       }
-               }               
-               return $persistableProperties;
+               return get_object_vars($this);
        }
 
        /**
@@ -151,9 +158,9 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
                return $dirtyProperties;
        }
 
-       private function initCleanProperties() {
+       private function initializeCleanProperties() {
                $properties = get_object_vars($this);
-               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper');
+               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper');
                foreach ($properties as $propertyName => $propertyValue) {
                        if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
                                $this->cleanProperties[$propertyName] = NULL;
diff --git a/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ColumnMap.php b/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ColumnMap.php
new file mode 100644 (file)
index 0000000..2d9ef25
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+declare(ENCODING = 'utf-8');
+
+/*                                                                        *
+ * This script belongs to the FLOW3 framework.                            *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A column map to map a column configured in $TCA on a property of a domain object.
+ *
+ * @version $Id:$
+ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
+ */
+class TX_EXTMVC_Persistence_Mapper_ColumnMap {
+
+       /**
+        * Constants reflecting the type of relation
+        */
+       const RELATION_NONE = 0;
+       const RELATION_HAS_ONE = 1;
+       const RELATION_HAS_MANY = 2;
+       const RELATION_HAS_AND_BELONGS_TO_MANY = 3;
+
+       /**
+        * Constants reflecting the type of value
+        */
+       const TYPE_UNKNOWN = 0;
+       const TYPE_STRING = 1;
+       const TYPE_DATE = 2;
+       const TYPE_INTEGER = 3;
+       const TYPE_FLOAT = 4;
+       const TYPE_BOOLEAN = 5;
+
+       /**
+        * The property name corresponding to the table name
+        *
+        * @var string
+        **/
+       protected $propertyName;
+       
+       /**
+        * The column name
+        *
+        * @var string
+        **/
+       protected $columnName;
+       
+       /**
+        * The type of relation
+        *
+        * @var int
+        **/
+       protected $typeOfRelation;
+       
+       /**
+        * The type of value
+        *
+        * @var int
+        **/
+       protected $typeOfValue;
+       
+       /**
+        * The name of the child's class
+        *
+        * @var string
+        **/
+       protected $childClassName;
+       
+       /**
+        * The name of the child's table
+        *
+        * @var string
+        **/
+       protected $childTableName;
+       
+       /**
+        * The where clause to narrow down the selected child records
+        *
+        * @var string
+        **/
+       protected $childTableWhere;
+       
+       /**
+        * The name of the field the results from the child's table are sorted by 
+        *
+        * @var string
+        **/
+       protected $childSortByFieldName;
+
+       /**
+        * The name of the relation table
+        *
+        * @var string
+        **/
+       protected $relationTableName;
+       
+       /**
+        * The name of the field holding the parents key
+        *
+        * @var string
+        **/
+       protected $parentKeyFieldName;
+       
+       /**
+        * The name of the field holding the name of the table of the parent's records
+        *
+        * @var string
+        **/
+       protected $parentTableFieldName;
+
+       public function __construct($columnName) {
+               $this->setColumnName($columnName);
+               $this->setPropertyName(TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($columnName));
+       }
+
+       public function setTypeOfRelation($typeOfRelation) {
+               switch ($typeOfRelation) {
+                       case self::TYPE_UNKNOWN;
+                       case self::TYPE_STRING;
+                       case self::TYPE_DATE;
+                       case self::TYPE_INTEGER;
+                       case self::TYPE_FLOAT;
+                       case self::TYPE_BOOLEAN;
+                               $this->typeOfRelation = $typeOfRelation;
+                               break;
+                       default:
+                               $this->typeOfRelation = NULL;
+                               break;
+               }
+       }
+
+       public function getTypeOfRelation() {
+               return $this->typeOfRelation;
+       }
+       
+       public function setTypeOfValue($typeOfValue) {
+               switch ($typeOfValue) {
+                       case self::RELATION_NONE;
+                       case self::RELATION_HAS_ONE;
+                       case self::RELATION_HAS_MANY;
+                       case self::RELATION_HAS_AND_BELONGS_TO_MANY;
+                               $this->typeOfValue = $typeOfValue;
+                               break;
+                       default:
+                               $this->typeOfValue = NULL;
+                               break;
+               }
+       }
+
+       public function getTypeOfValue() {
+               return $this->typeOfValue;
+       }
+               
+       public function setPropertyName($propertyName) {
+               $this->propertyName = $propertyName;
+       }
+
+       public function getPropertyName() {
+               return $this->propertyName;
+       }
+
+       public function setColumnName($columnName) {
+               $this->columnName = $columnName;
+       }
+
+       public function getColumnName() {
+               return $this->columnName;
+       }
+       
+       public function setChildClassName($childClassName) {
+               $this->childClassName = $childClassName;
+       }
+
+       public function getChildClassName() {
+               return $this->childClassName;
+       }
+
+       public function setChildTableName($childTableName) {
+               $this->childTableName = $childTableName;
+       }
+
+       public function getChildTableName() {
+               return $this->childTableName;
+       }
+
+       public function setChildTableWhere($childTableWhere) {
+               $this->childTableWhere = $childTableWhere;
+       }
+
+       public function getChildTableWhere() {
+               return $this->childTableWhere;
+       }
+
+       public function setChildSortByFieldName($childSortByFieldName) {
+               $this->childSortByFieldName = $childSortByFieldName;
+       }
+
+       public function getChildSortByFieldName() {
+               return $this->childSortByFieldName;
+       }
+       
+       public function setRelationTableName($relationTableName) {
+               $this->relationTableName = $relationTableName;
+       }
+
+       public function getRelationTableName() {
+               return $this->relationTableName;
+       }
+
+       public function setParentKeyFieldName($parentKeyFieldName) {
+               $this->parentKeyFieldName = $parentKeyFieldName;
+       }
+
+       public function getParentKeyFieldName() {
+               return $this->parentKeyFieldName;
+       }
+
+       public function setParentTableFieldName($parentTableFieldName) {
+               $this->parentTableFieldName = $parentTableFieldName;
+       }
+
+       public function getParentTableFieldName() {
+               return $this->parentTableFieldName;
+       }
+       
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_DataMap.php b/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_DataMap.php
new file mode 100644 (file)
index 0000000..46d41c8
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+declare(ENCODING = 'utf-8');
+
+/*                                                                        *
+ * This script belongs to the FLOW3 framework.                            *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ColumnMap.php');
+
+/**
+ * A data map to map a single table configured in $TCA on a domain object.
+ *
+ * @version $Id:$
+ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
+ */
+class TX_EXTMVC_Persistence_Mapper_DataMap {
+
+       /**
+        * The domain class name
+        *
+        * @var string
+        **/
+       protected $className;
+       
+       /**
+        * The table name corresponding to the domain class configured in $TCA
+        *
+        * @var string
+        **/
+       protected $tableName;
+       
+       /**
+        * An array of column maps configured in $TCA
+        *
+        * @var array
+        **/
+       protected $columnMaps;
+       
+       public function __construct($className) {
+               $this->setClassName($className);
+               $this->setTableName(strtolower($this->className));
+               t3lib_div::loadTCA($this->getTableName());
+       }
+       
+       public function setClassName($className) {
+               $this->className = $className;
+       }
+
+       public function getClassName() {
+               return $this->className;
+       }
+
+       public function setTableName($tableName) {
+               $this->tableName = $tableName;
+       }
+
+       public function getTableName() {
+               return $this->tableName;
+       }
+       
+       public function initialize() {
+               $columns = $GLOBALS['TCA'][$this->getTableName()]['columns'];
+               if (is_array($columns)) {
+                       $this->addColumn('uid', TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_INTEGER);
+                       $this->addColumn('pid', TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_INTEGER);
+                       $this->addColumn('tstamp', TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_DATE);
+                       $this->addColumn('crdate', TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_DATE);
+                       $this->addColumn('cruser_id', TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_DATE);
+                       if ($this->getDeletedColumnName() !== NULL) {
+                               $this->addColumn($this->getDeletedColumnName(), TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_BOOLEAN);
+                       }
+                       if ($this->getHiddenColumnName() !== NULL) {
+                               $this->addColumn($this->getHiddenColumnName(), TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_BOOLEAN);
+                       }
+
+                       $columnMapClassName = t3lib_div::makeInstanceClassName('TX_EXTMVC_Persistence_Mapper_ColumnMap');                       
+                       foreach ($columns as $columnName => $columnConfiguration) {
+                               $columnMap = new $columnMapClassName($columnName, $this);
+                               if (strpos($columnConfiguration['config']['eval'], 'date') !== FALSE
+                                       || strpos($columnConfiguration['config']['eval'], 'datetime') !== FALSE) {
+                                       $columnMap->setTypeOfValue(TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_DATE);
+                               } elseif ($columnConfiguration['config']['type'] === 'check' && empty($columnConfiguration['config']['items'])) {
+                                       $columnMap->setTypeOfValue(TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_BOOLEAN);
+                               } elseif (strpos($columnConfiguration['config']['eval'], 'int') !== FALSE) {
+                                       $columnMap->setTypeOfValue(TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_INTEGER);
+                               } elseif (strpos($columnConfiguration['config']['eval'], 'double2') !== FALSE) {
+                                       $columnMap->setTypeOfValue(TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_FLOAT);
+                               } else {
+                                       $columnMap->setTypeOfValue(TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_STRING);
+                               }
+                               
+                               // TODO support for IRRE
+                               // TODO support for MM_insert_fields and MM_match_fields
+                               if (array_key_exists('foreign_table', $columnConfiguration['config']) && !array_key_exists('MM', $columnConfiguration['config'])) {
+                                       $columnMap->setTypeOfRelation(TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY);
+                                       $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
+                                       $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
+                                       $columnMap->setChildTableWhere($columnConfiguration['config']['foreign_table_where']);
+                                       $columnMap->setChildSortbyFieldName($columnConfiguration['config']['foreign_sortby']);
+                                       $columnMap->setParentKeyFieldName($columnConfiguration['config']['foreign_field']);
+                                       $columnMap->setParentTableFieldName($columnConfiguration['config']['foreign_table_field']);                                     
+                               } elseif (array_key_exists('MM', $columnConfiguration['config'])) {
+                                       $columnMap->setTypeOfRelation(TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY);
+                                       $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
+                                       $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
+                                       $columnMap->setRelationTableName($columnConfiguration['config']['MM']);
+                               } else {
+                                       $columnMap->setTypeOfRelation(TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_NONE);                                   
+                               }
+                               $this->addColumnMap($columnMap);
+                       }
+               } else {
+                       // TODO Throw exception 
+               }
+       }
+
+       public function setColumnMaps(array $columnMaps) {
+               $this->columnMaps = $columnMaps;
+       }
+
+       public function addColumnMap(TX_EXTMVC_Persistence_Mapper_ColumnMap $columnMap) {
+               $this->columnMaps[$columnMap->getPropertyName()] = $columnMap;
+       }
+       
+       public function addColumn($columnName, $typeOfValue = TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_STRING, $typeOfRelation = TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_NONE) {
+               $columnMapClassName = t3lib_div::makeInstanceClassName('TX_EXTMVC_Persistence_Mapper_ColumnMap');
+               $columnMap = new $columnMapClassName($columnName);
+               $columnMap->setTypeOfValue($typeOfValue);
+               $columnMap->setTypeOfRelation($typeOfRelation);
+               $this->addColumnMap($columnMap);
+               return $this;
+       }
+
+       public function getColumnMaps() {
+               return $this->columnMaps;
+       }
+       
+       public function getColumnMap($propertyName) {
+               return $this->columnMaps[$propertyName];
+       }
+       
+       public function getColumnList() {
+               $columnList = '';
+               foreach ($this->columnMaps as $columnMap) {
+                       if ($columnList !== '') {
+                               $columnList .= ',';
+                       }
+                       $columnList .= $columnMap->getColumnName();
+               }
+               return $columnList;
+       }
+       
+       /**
+        * Returns TRUE if the property is persistable (configured in $TCA)
+        *
+        * @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($propertyName) {
+               if (array_key_exists($propertyName, $this->columnMaps)) return TRUE;
+               return FALSE;
+       }
+       
+       /**
+        * Returns the name of a column indicating the 'deleted' state of the row
+        *
+        * @return string The class name
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */     
+       protected function getDeletedColumnName() {
+               return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['delete'];
+       }
+       
+       /**
+        * Returns the name of a column indicating the 'hidden' state of the row
+        *
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */     
+       protected function getHiddenColumnName() {;
+               return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['disabled'];
+       }
+       
+       /**
+        * 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>
+        */             
+       public function convertFieldValueToPropertyValue($propertyName, $fieldValue) {
+               $columnMap = $this->getColumnMap($propertyName);
+               if ($columnMap->getTypeOfValue() === TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_DATE) {
+                       $convertedValue = new DateTime(strftime('%Y-%m-%d %H:%M', $fieldValue), new DateTimeZone('UTC'));
+               } elseif ($columnMap->getTypeOfValue() === TX_EXTMVC_Persistence_Mapper_ColumnMap::TYPE_BOOLEAN) {
+                       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>
+        */             
+       public 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
diff --git a/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper.php b/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper.php
new file mode 100644 (file)
index 0000000..9044e3d
--- /dev/null
@@ -0,0 +1,452 @@
+<?php
+declare(ENCODING = 'utf-8');
+
+/*                                                                        *
+ * This script belongs to the FLOW3 framework.                            *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+require_once(PATH_t3lib . 'interfaces/interface.t3lib_singleton.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/TX_EXTMVC_Persistence_ObjectStorage.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_DataMap.php');
+
+/**
+ * A mapper to map database tables configured in $TCA on domain objects.
+ *
+ * @version $Id:$
+ * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
+ */
+class TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Singleton {
+
+       /**
+        * The content object
+        *
+        * @var tslib_cObj
+        **/
+       protected $cObj;
+               
+       /**
+        * The persistence session
+        *
+        * @var TX_EXTMVC_Persistence_Session
+        **/
+       protected $session;
+               
+       /**
+        * Constructs a new mapper
+        *
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function __construct() {
+               $this->cObj = t3lib_div::makeInstance('tslib_cObj');
+               $this->session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session');
+               $GLOBALS['TSFE']->includeTCA();
+       }
+       
+       /**
+        * Finds objects matching property="xyz"
+        *
+        * @param string $propertyName The name of the property (will be chekced by a white list)
+        * @param string $arguments The WHERE statement
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function findWhere($className, $where = '1=1') {
+               $dataMap = $this->getDataMap($className);
+               $rows = $this->fetch($dataMap, $where);
+               $objects = $this->reconstituteObjects($dataMap, $rows);
+               return $objects;
+       }
+       
+       /**
+        * Fetches rows from the database by given SQL statement snippets
+        *
+        * @param string $from FROM statement
+        * @param string $where WHERE statement
+        * @param string $groupBy GROUP BY statement
+        * @param string $orderBy ORDER BY statement
+        * @param string $limit LIMIT statement
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function fetch($dataMap, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+                       '*', // TODO limit fetched fields
+                       $dataMap->getTableName(),
+                       $where . $this->cObj->enableFields($dataMap->getTableName()) . $this->cObj->enableFields($dataMap->getTableName()),
+                       $groupBy,
+                       $orderBy,
+                       $limit
+                       );
+               // TODO language overlay; workspace overlay
+               return $rows ? $rows : array();
+       }       
+               
+       /**
+        * Fetches a rows from the database by given SQL statement snippets taking a relation table into account
+        *
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function fetchWithRelationTable($parentObject, $columnMap, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+                       $columnMap->getChildTableName() . '.*, ' . $columnMap->getRelationTableName() . '.*',
+                       $columnMap->getChildTableName() . ' LEFT JOIN ' . $columnMap->getRelationTableName() . ' ON (' . $columnMap->getChildTableName() . '.uid=' . $columnMap->getRelationTableName() . '.uid_foreign)',
+                       $where . ' AND ' . $columnMap->getRelationTableName() . '.uid_local=' . intval($parentObject->getUid()) . $this->cObj->enableFields($columnMap->getChildTableName()) . $this->cObj->enableFields($columnMap->getChildTableName()),
+                       $groupBy,
+                       $orderBy,
+                       $limit
+                       );
+               // TODO language overlay; workspace overlay
+               return $rows ? $rows : array();         
+       }
+       
+       /**
+        * reconstitutes domain objects from $rows (array)
+        *
+        * @param TX_EXTMVC_Persistence_Mapper_DataMap $dataMap The data map corresponding to the domain object
+        * @param array $rows The rows array fetched from the database
+        * @return array An array of reconstituted domain objects
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function reconstituteObjects($dataMap, array $rows) {
+               $objects = array();
+               foreach ($rows as $row) {
+                       $properties = array();
+                       foreach ($dataMap->getColumnMaps() as $columnMap) {
+                               $properties[$columnMap->getPropertyName()] = $dataMap->convertFieldValueToPropertyValue($columnMap->getPropertyName(), $row[$columnMap->getColumnName()]);
+                       }
+                       $object = $this->reconstituteObject($dataMap->getClassName(), $properties);
+                       foreach ($dataMap->getColumnMaps() as $columnMap) {
+                               if ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+                                       $where .= $columnMap->getParentKeyFieldName() . '=' . intval($object->getUid());
+                                       $relatedDataMap = $this->getDataMap($columnMap->getChildClassName());
+                                       $relatedRows = $this->fetch($relatedDataMap, $where);
+                                       $relatedObjects = $this->reconstituteObjects($relatedDataMap, $relatedRows, $depth);
+                                       $object->_reconstituteProperty($columnMap->getPropertyName(), $relatedObjects);
+                               } elseif ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                                       $relatedDataMap = $this->getDataMap($columnMap->getChildClassName());
+                                       $relatedRows = $this->fetchWithRelationTable($object, $columnMap);
+                                       $relatedObjects = $this->reconstituteObjects($relatedDataMap, $relatedRows, $depth);
+                                       $object->_reconstituteProperty($columnMap->getPropertyName(), $relatedObjects);
+                               }
+                       }
+                       $this->session->registerReconstitutedObject($object);                   
+                       $objects[] = $object;
+               }
+               return $objects;
+       }
+       
+       /**
+        * Reconstitutes the specified object and fills it with the given properties.
+        *
+        * @param string $objectName Name of the object to reconstitute
+        * @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>
+        */
+       protected function reconstituteObject($className, array $properties = array()) {
+               // those objects will be fetched from within the __wakeup() method of the object...
+               $GLOBALS['EXTMVC']['reconstituteObject']['properties'] = $properties;
+               $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
+               unset($GLOBALS['EXTMVC']['reconstituteObject']);
+               return $object;
+       }
+       
+       /**
+        * Persists all objects of a persistence session
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function persistAll() {
+               // 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 Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function insertObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL) {
+               $queuedRelations = array();
+               $row = array();
+               $properties = $object->_getProperties();
+               $dataMap = $this->getDataMap(get_class($object));
+               foreach ($dataMap->getColumnMaps() as $columnMap) {
+                       $propertyName = $columnMap->getPropertyName();
+                       $columnName = $columnMap->getColumnName();
+                       if ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                               $row[$columnName] = count($properties[$propertyName]);
+                       } elseif ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                               $row[$columnName] = count($properties[$propertyName]);
+                       } else {
+                               if ($properties[$propertyName] !== NULL) {
+                                       $row[$columnName] = $dataMap->convertPropertyValueToFieldValue($properties[$propertyName]);
+                               }
+                       }
+               }
+               
+               if ($parentObject instanceof TX_EXTMVC_DomainObject_AbstractDomainObject && $parentPropertyName !== NULL) {
+                       $parentDataMap = $this->getDataMap(get_class($parentObject));
+                       $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
+                       $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
+                       if ($parentKeyFieldName !== NULL) {
+                               $row[$parentKeyFieldName] = $parentObject->getUid();
+                       }
+                       $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
+                       if ($parentTableFieldName !== NULL) {
+                               $row[$parentTableFieldName] = $parentDataMap->getTableName();
+                       }
+               }
+
+               unset($row['uid']);
+               $row['pid'] = !empty($this->cObj->data['pages']) ? $this->cObj->data['pages'] : $GLOBALS['TSFE']->id;
+               $row['tstamp'] = time();
+               
+               $tableName = $dataMap->getTableName();
+               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+                       $tableName,
+                       $row
+                       );
+               $object->_reconstituteProperty('uid', $GLOBALS['TYPO3_DB']->sql_insert_id());
+               
+               foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                       foreach ($relatedObjects as $relatedObject) {
+                               if (!$this->session->isReconstitutedObject($relatedObject)) {
+                                       $this->insertObject($relatedObject, $object, $propertyName);
+                                       if ($dataMap->getColumnMap($propertyName)->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                                               $this->insertRelation($relatedObject, $object, $propertyName);
+                                       } elseif ($this->session->isReconstitutedObject($relatedObject) && $relatedObject->_isDirty()) {
+                                               $this->updateObject($relatedObject, $object, $propertyName);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * 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 $relatedObject, TX_EXTMVC_DomainObject_AbstractDomainObject $parentObject, $parentPropertyName) {
+               $dataMap = $this->getDataMap(get_class($parentObject));
+               $rowToInsert = array(
+                       'uid_local' => $parentObject->getUid(),
+                       'uid_foreign' => $relatedObject->getUid(),
+                       'tablenames' => $dataMap->getTableName(),
+                       'sorting' => 9999 // TODO sorting of mm table items
+                       );
+               $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
+               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+                       $tableName,
+                       $rowToInsert
+                       );
+       }
+       
+       /**
+        * Updates a modified object in the database
+        *
+        * @return void
+        * @author Karsten Dambekalns <karsten@typo3.org>
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function updateObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL) {
+               $queuedRelations = array();
+               $row = array();
+               $properties = $object->_getDirtyProperties();
+               $dataMap = $this->getDataMap(get_class($object));
+               foreach ($dataMap->getColumnMaps() as $columnMap) {
+                       $propertyName = $columnMap->getPropertyName();
+                       $columnName = $columnMap->getColumnName();
+                       if ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                               $row[$columnName] = count($properties[$propertyName]);
+                       } elseif ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                               $row[$columnName] = count($properties[$propertyName]);
+                       } else {
+                               if ($properties[$propertyName] !== NULL) {
+                                       $row[$columnName] = $dataMap->convertPropertyValueToFieldValue($properties[$propertyName]);
+                               }
+                       }
+               }
+               
+               unset($row['uid']);
+               $row['crdate'] = time();
+               if (!empty($GLOBALS['TSFE']->fe_user->user['uid'])) {
+                       $row['cruser_id'] = $GLOBALS['TSFE']->fe_user->user['uid'];
+               }
+               if ($parentObject instanceof TX_EXTMVC_DomainObject_AbstractDomainObject && $parentPropertyName !== NULL) {
+                       $parentDataMap = $this->getDataMap(get_class($parentObject));
+                       $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
+                       $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
+                       if ($parentKeyFieldName !== NULL) {
+                               $row[$parentKeyFieldName] = $parentObject->getUid();
+                       }
+                       $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
+                       if ($parentTableFieldName !== NULL) {
+                               $row[$parentTableFieldName] = $parentDataMap->getTableName();
+                       }
+               }
+               $tableName = $dataMap->getTableName();
+               $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                       $tableName,
+                       'uid=' . $object->getUid(),
+                       $row
+                       );
+
+               foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                       foreach ($relatedObjects as $relatedObject) {
+                               if (!$this->session->isReconstitutedObject($relatedObject)) {
+                                       $this->insertObject($relatedObject, $object, $propertyName);
+                                       if ($dataMap->getColumnMap($propertyName)->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                                               $this->insertRelation($relatedObject, $object, $propertyName);
+                                       }
+                               } elseif ($this->session->isReconstitutedObject($relatedObject) && $relatedObject->_isDirty()) {
+                                       $this->updateObject($relatedObject, $object, $propertyName);
+                               }
+                       }
+               }               
+       }
+
+       /**
+        * 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 deleteObject(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $parentObject = NULL, $parentPropertyName = NULL, $recursionMode = FALSE) {
+               $queuedRelations = array();
+               $properties = $object->_getDirtyProperties();
+               $dataMap = $this->getDataMap(get_class($object));
+               foreach ($dataMap->getColumnMaps() as $columnMap) {
+                       $propertyName = $columnMap->getPropertyName();
+                       $columnName = $columnMap->getColumnName();
+                       if ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                       } elseif ($columnMap->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                               $queuedRelations[$propertyName] = $properties[$propertyName];
+                       }
+               }
+               
+               $tableName = $dataMap->getTableName();
+               $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $tableName,
+                       'uid=' . $object->getUid()
+                       );
+
+               if ($recursionMode === TRUE) {
+                       foreach ($queuedRelations as $propertyName => $relatedObjects) {
+                               foreach ($relatedObjects as $relatedObject) {
+                                       $this->deleteObject($relatedObject, $object, $propertyName);
+                                       if ($dataMap->getColumnMap($propertyName)->getTypeOfRelation() === TX_EXTMVC_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+                                               $this->deleteRelation($relatedObject, $object, $propertyName);
+                                       }
+                               }
+                       }               
+               }
+               
+       }
+       
+       /**
+        * 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()
+                       );
+       }
+       
+       /**
+        * Delegates the call to the Data Map.
+        * Returns TRUE if the property is persistable (configured in $TCA)
+        *
+        * @param string $className The property 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) {
+               $dataMapClassName = t3lib_div::makeInstanceClassName('TX_EXTMVC_Persistence_Mapper_DataMap');
+               $dataMap = new $dataMapClassName($className);
+               $dataMap->initialize();
+               return $dataMap->isPersistableProperty($propertyName);
+       }
+       
+       /**
+        * Returns a data map for a given class name
+        *
+        * @return TX_EXTMVC_Persistence_Mapper_DataMap The data map
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function getDataMap($className) {
+               $dataMapClassName = t3lib_div::makeInstanceClassName('TX_EXTMVC_Persistence_Mapper_DataMap');
+               // TODO Cache data maps
+               $dataMap = new $dataMapClassName($className);
+               $dataMap->initialize();
+               return $dataMap;
+       }
+       
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php b/typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php
deleted file mode 100644 (file)
index 0a7f607..0000000
+++ /dev/null
@@ -1,650 +0,0 @@
-<?php
-declare(ENCODING = 'utf-8');
-
-/*                                                                        *
- * This script belongs to the FLOW3 framework.                            *
- *                                                                        *
- * It is free software; you can redistribute it and/or modify it under    *
- * the terms of the GNU Lesser General Public License as published by the *
- * Free Software Foundation, either version 3 of the License, or (at your *
- * option) any later version.                                             *
- *                                                                        *
- * This script is distributed in the hope that it will be useful, but     *
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
- * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
- * General Public License for more details.                               *
- *                                                                        *
- * You should have received a copy of the GNU Lesser General Public       *
- * License along with the script.                                         *
- * If not, see http://www.gnu.org/licenses/lgpl.html                      *
- *                                                                        *
- * The TYPO3 project - inspiring people to share!                         *
- *                                                                        */
-
-require_once(PATH_t3lib . 'interfaces/interface.t3lib_singleton.php');
-require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
-
-/**
- * A mapper to map database tables configured in $TCA on domain objects.
- *
- * @version $Id:$
- * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
- */
-class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
-
-       /**
-        * The content object
-        *
-        * @var tslib_cObj
-        **/
-       protected $cObj;
-               
-       /**
-        * The persistence session
-        *
-        * @var TX_EXTMVC_Persistence_Session
-        **/
-       protected $session;
-               
-       /**
-        * A hash of table configurations from $TCA
-        *
-        * @var array
-        **/
-       protected $tableConfigurations;
-               
-       /**
-        * Constructs a new mapper
-        *
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       public function __construct() {
-               $this->cObj = t3lib_div::makeInstance('tslib_cObj');
-               $this->session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session');
-               $GLOBALS['TSFE']->includeTCA();
-       }
-       
-       /**
-        * Finds objects matching property="xyz"
-        *
-        * @param string $propertyName The name of the property (will be chekced by a white list)
-        * @param string $arguments The WHERE statement
-        * @return void
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       public function findWhere($className, $where = '1=1') {
-               return $this->reconstituteObjects($className, $this->fetch($className, $where));
-       }
-       
-       /**
-        * Fetches a rows from the database by given SQL statement snippets
-        *
-        * @param string $from FROM statement
-        * @param string $where WHERE statement
-        * @param string $groupBy GROUP BY statement
-        * @param string $orderBy ORDER BY statement
-        * @param string $limit LIMIT statement
-        * @return void
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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,
-                       $where . $this->cObj->enableFields($tableName) . $this->cObj->enableFields($tableName),
-                       $groupBy,
-                       $orderBy,
-                       $limit
-                       );
-               // TODO language overlay; workspace overlay
-               return $rows ? $rows : array();
-       }       
-       
-       /**
-        * Fetches a rows from the database by given SQL statement snippets
-        *
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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);
-       }       
-       
-       /**
-        * Fetches a rows from the database by given SQL statement snippets
-        *
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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)',
-                       $where . ' AND ' . $relationTableName . '.uid_local=' . intval($parentObject->getUid()) . $this->cObj->enableFields($foreignTableName) . $this->cObj->enableFields($foreignTableName),
-                       $groupBy,
-                       $orderBy,
-                       $limit
-                       );
-               // TODO language overlay; workspace overlay
-               return $rows ? $rows : array();         
-       }
-       
-       /**
-        * Dispatches the reconstitution of a domain object to an appropriate method
-        *
-        * @param array $rows The rows array fetched from the database
-        * @throws TX_EXTMVC_Persistence_Exception_RecursionTooDeep
-        * @return array An array of reconstituted domain objects
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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) {
-                       $propertiesToReconstitute = array();
-                       foreach ($row as $fieldName => $fieldValue) {
-                               $propertyName = TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($fieldName);
-                               $propertiesToReconstitute[$propertyName] = $this->convertFieldValueToPropertyValue($className, $propertyName, $fieldValue);
-                       }
-                       $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;
-       }
-       
-       /**
-        * Reconstitutes the specified object and fills it with the given properties.
-        *
-        * @param string $objectName Name of the object to reconstitute
-        * @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>
-        */
-       protected function reconstituteObject($className, array $properties = array()) {
-               // those objects will be fetched from within the __wakeup() method of the object...
-               $GLOBALS['EXTMVC']['reconstituteObject']['properties'] = $properties;
-               $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
-               unset($GLOBALS['EXTMVC']['reconstituteObject']);
-               return $object;
-       }
-       
-       /**
-        * Persists all objects of a persistence session
-        *
-        * @param string $session The persistence session
-        * @return void
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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'] = !empty($this->cObj->data['pages']) ? $this->cObj->data['pages'] : $GLOBALS['TSFE']->id;
-               $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(
-                       $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 a modified object in the database
-        *
-        * @return void
-        * @author Karsten Dambekalns <karsten@typo3.org>
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       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, 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 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);
-                                       }
-                                       
-                               }
-                       }
-               }
-               
-       }
-       
-       /**
-        * 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
-                       );
-       }
-       
-       /**
-        * 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 table
-        *
-        * @param string $tableName The table name 
-        * @return array The column configurations from $TCA
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       protected function getColumns($tableName) {
-               if (empty($this->tableConfigurations[$tableName])) {
-                       t3lib_div::loadTCA($tableName);
-                       $this->tableConfigurations[$tableName] = $GLOBALS['TCA'][$tableName];
-               }
-               return $this->tableConfigurations[$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);
-       }
-
-       /**
-        * 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'];
-       }
-       
-       /**
-        * 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;
-       }
-       
-       /**
-        * 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
index 446d11b..c96577e 100644 (file)
@@ -28,15 +28,15 @@ declare(ENCODING = 'utf-8');
  * @version $Id:$
  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
  */
-class TX_EXTMVC_Persistence_ObjectStorage implements Iterator, Countable {
-       
+class TX_EXTMVC_Persistence_ObjectStorage implements Iterator, Countable, ArrayAccess {
+
        /**
-        * The array holding references to the stored objects.
-        *
-        * @var string
-        **/
+               * The array holding references to the stored objects.
+               *
+               * @var string
+               **/
        private $storage = array();
-               
+
        public function rewind() {
                reset($this->storage);
        }
@@ -61,6 +61,24 @@ class TX_EXTMVC_Persistence_ObjectStorage implements Iterator, Countable {
                return count($this->storage);
        }
 
+       public function offsetSet($offset, $obj) {
+               if (is_object($obj) && !$this->contains($obj)) {
+                       $this->storage[$offset] = $obj;
+               }
+       }
+
+       public function offsetExists($offset) {
+               return isset($this->storage[$offset]);
+       }
+
+       public function offsetUnset($offset) {
+               unset($this->storage[$offset]);
+       }
+
+       public function offsetGet($offset) {
+               return isset($this->storage[$offset]) ? $this->storage[$offset] : NULL;
+       }
+
        /**
         * Does the Storage contains the given object
         *
@@ -114,6 +132,16 @@ class TX_EXTMVC_Persistence_ObjectStorage implements Iterator, Countable {
        public function removeAll() {
                $this->storage = array();
        }
+       
+       /**
+        * Returns this object storage as an array
+        *
+        * @return array The object storage
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function toArray() {
+               return $this->storage;
+       }
 
 }
 
index 3b0bd43..9eeaaee 100644 (file)
@@ -83,11 +83,11 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
                if (substr($repositoryClassName, -10) == 'Repository' && substr($repositoryClassName, -11, 1) != '_') {
                        $this->aggregateRootClassName = substr($repositoryClassName, 0, -10);
                }
-               $this->dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper'); // singleton
+               $this->dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper'); // singleton
                $this->session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session'); // singleton
                $this->session->registerAggregateRootClassName($this->aggregateRootClassName);
-               // TODO auto resolve findBy properties
-               $this->allowedFindByProperties = array('name');
+               // FIXIT auto resolve findBy properties
+               $this->allowedFindByProperties = array('name', 'blog');
        }
        
        /**
@@ -155,6 +155,16 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
                        if (in_array($propertyName, $this->allowedFindByProperties)) {
                                return $this->findByProperty($propertyName, $arguments[0]);
                        }
+               } elseif (substr($methodName, 0, 9) === 'findOneBy') {
+                       $propertyName = TX_EXTMVC_Utility_Strings::lowercaseFirst(substr($methodName,9));
+                       if (in_array($propertyName, $this->allowedFindByProperties)) {
+                               $result = $this->findByProperty($propertyName, $arguments[0]);
+                               if (empty($result)) {
+                                       return FALSE;
+                               } else {
+                                       return $result[0];
+                               }
+                       }
                }
                throw new TX_EXTMVC_Persistence_Exception_UnsupportedMethod('The method "' . $methodName . '" is not supported by the repository.', 1233180480);
        }
@@ -166,6 +176,7 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        public function findAll() {
+               // TODO implement support for SQL LIMIT
                return $this->dataMapper->findWhere($this->aggregateRootClassName);
        }
        
@@ -174,11 +185,16 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
         *
         * @param string $propertyName The name of the property (will be checked by a white list)
         * @param string $arguments The arguments of the magic findBy method
-        * @return void
+        * @return array The result
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        private function findByProperty($propertyName, $value) {
-               $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value, 'foo');
+               // TODO implement support for SQL LIMIT
+               if ($value instanceof TX_EXTMVC_DomainObject_AbstractDomainObject) {
+                       $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value->getUid(), 'foo');
+               } else {
+                       $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value, 'foo');
+               }
                return $this->dataMapper->findWhere($this->aggregateRootClassName, $where);
        }
                
index e85c38e..79c0900 100644 (file)
@@ -307,8 +307,8 @@ class TX_EXTMVC_Persistence_Session implements t3lib_singleton {
         * @author Karsten Dambekalns <karsten@typo3.org>
         */
        public function commit() {
-               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper'); // singleton;
-               $dataMapper->persistAll($this);
+               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper'); // singleton;
+               $dataMapper->persistAll();
        }
                
 }
index e3c337b..d58f809 100644 (file)
@@ -6,5 +6,4 @@ The SQL statements are issued in the following order
     * all collection element deletions, updates and insertions
     * all collection insertions
     * all entity deletions, in the same order the corresponding objects were removed using $repository->remove($object)
-       (Exception: entity instances using application-assigned identifiers are inserted when they are saved.)
 
index 51995d7..3a69cee 100644 (file)
@@ -113,7 +113,7 @@ class TX_EXTMVC_Persistence_Session_testcase extends tx_phpunit_testcase {
                                                // TODO is 'foreign_class' in $TCA the best way?
                                                'foreign_class' => 'TX_Blogexample_Domain_Post',
                                                'foreign_table' => 'tx_blogexample_domain_post',
-                                               'foreign_field' => 'blog_uid',
+                                               'foreign_field' => 'blog',
                                                'foreign_table_field' => 'blog_table',
                                                'appearance' => array(
                                                        'newRecordLinkPosition' => 'bottom',
index b5c4160..ae47ac8 100644 (file)
@@ -31,7 +31,7 @@ require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Controller/TX_EXTMVC_Con
 require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Controller/TX_EXTMVC_Controller_ActionController.php');
 require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/View/TX_EXTMVC_View_AbstractView.php');
 require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/TX_EXTMVC_Persistence_Session.php');
-require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php');
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_ObjectRelationalMapper.php');
 
 // FIXME
 // require_once(t3lib_extMgm::extPath('blogexample') . 'Classes/Controller/TX_Blogexample_Controller_PostsController.php');
@@ -107,7 +107,7 @@ class TX_EXTMVC_Dispatcher {
                try {
                        $controller->processRequest($request, $response);                       
                } catch (TX_EXTMVC_Exception_StopAction $ignoredException) {
-               }
+               }               
                $session->commit();
                $session->clear();