* 1:n relations are fetched an reconstituted recursively
authorJochen Rau <j.rau@web.de>
Fri, 30 Jan 2009 12:16:34 +0000 (12:16 +0000)
committerJochen Rau <j.rau@web.de>
Fri, 30 Jan 2009 12:16:34 +0000 (12:16 +0000)
* reconstitution of objects works with rich contructors

typo3/sysext/extbase/Classes/DomainObject/TX_EXTMVC_DomainObject_AbstractDomainObject.php
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_Repository.php

index 7d66204..081488e 100644 (file)
@@ -21,6 +21,8 @@ declare(ENCODING = 'utf-8');
  * The TYPO3 project - inspiring people to share!                         *
  *                                                                        */
 
+require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
+
 /**
  * A generic Domain Object
  *
@@ -37,49 +39,68 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
        private $cleanProperties = NULL;
        
        /**
-        * An array properties configured as 1:n relations in $TCA.
+        * A configuration array of properties configured as 1:n relations in $TCA.
         *
         * @var array
         */
-       private $oneToManyProperties = NULL;
+       private $oneToManyRelations = array();
        
        /**
-        * An array properties configured as m:n relations in $TCA.
+        * A configuration array of properties configured as m:n relations in $TCA.
         *
         * @var array
         */
-       private $manyToManyProperties = NULL;
+       private $manyToManyRelations = array();
        
        private function initCleanProperties() {
                        $possibleTableName = strtolower(get_class($this));
                        t3lib_div::loadTCA($possibleTableName);
                        $tca = $GLOBALS['TCA'][$possibleTableName]['columns'];
                        foreach ($tca as $columnName => $columnConfiguration) {
+                               $propertyName = TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($columnName);
+                               if (property_exists($this, $propertyName)) {
+                                       $this->cleanProperties[$propertyName] = NULL;
+                               }
                                if (array_key_exists('foreign_table', $columnConfiguration['config'])) {
                                        // TODO take IRRE into account
                                        if (array_key_exists('MM', $columnConfiguration['config'])) {
-                                               $this->manyToManyProperties[] = $columnName;
+                                               $this->manyToManyRelations[$propertyName] = $columnConfiguration['config'];
                                        } else {
-                                               $this->oneToManyProperties[] = $columnName;
+                                               $this->oneToManyRelations[$propertyName] = array(
+                                                       'foreign_table' => $columnConfiguration['config']['foreign_table'],
+                                                       'foreign_field' => $columnConfiguration['config']['foreign_field'],
+                                                       'foreign_table_field' => $columnConfiguration['config']['foreign_table_field']
+                                                       );
                                        }
-                               } else {
-                                       $this->cleanProperties[$columnName] = NULL;
-                               }
-                               
+                               }                               
                        }
                        $this->cleanProperties['uid'] = NULL;
        }
-               
+       
+       public function getOneToManyRelations() {
+               return $this->oneToManyRelations;
+       }
+       
+       /**
+        * 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 
+        * parent::__wakeup() first!
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function __wakeup() {
+               foreach ($GLOBALS['EXTMVC']['reconstituteObject']['properties'] as $propertyName => $value) {
+                       $this->_reconstituteProperty($propertyName, $value);
+               }
+               $this->initCleanProperties();
+       }
+       
        public function _reconstituteProperty($propertyName, $value) {
-               $possibleAddMethodName = 'add' . ucfirst($propertyName);
-               if (method_exists($this, $possibleAddMethodName)) {
-                       $this->$possibleAddMethodName($value);
+               if (property_exists($this, $propertyName)) {
+                       $this->$propertyName = $value;
                } else {
-                       if (property_exists($this, $propertyName)) {
-                               $this->$propertyName = $value;
-                       } else {
-                               throw new TX_EXTMVC_Persistence_Exception_UnknownProperty('The property "' . $propertyName . '" doesn\'t exist in this object.', 1233270476);
-                       }
+                       // throw new TX_EXTMVC_Persistence_Exception_UnknownProperty('The property "' . $propertyName . '" doesn\'t exist in this object.', 1233270476);
                }
        }
        
@@ -91,7 +112,7 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        public function _memorizeCleanState() {
-               $this->initCleanProperties();
+               $cleanProperties = array();
                foreach ($this->cleanProperties as $propertyName => $propertyValue) {
                        $cleanProperties[$propertyName] = $this->$propertyName;
                }
@@ -105,18 +126,12 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
        public function _isDirty() {
-               $isDirty = FALSE;
-               $cleanProperties = is_array($this->cleanProperties) ? $this->cleanProperties : array();
-               if ($this->uid !== NULL && $this->uid != $cleanProperties['uid']) {
-                       throw new TX_EXTMVC_Persistence_Exception_TooDirty('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239);
-               }
-               foreach ($cleanProperties as $propertyName => $propertyValue) {
-                       if ($cleanProperties[$propertyName] !== $this->$propertyName) {
-                               $isDirty = TRUE;
-                       }
+               if (!is_array($this->cleanProperties)) throw new TX_EXTMVC_Persistence_Exception_CleanStateNotMemorized('The clean state of the object "' . get_class($this) . '" has not been memorized before asking _isDirty().', 1233309106);
+               if ($this->uid !== NULL && $this->uid != $this->cleanProperties['uid']) throw new TX_EXTMVC_Persistence_Exception_TooDirty('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239);
+               foreach ($this->cleanProperties as $propertyName => $propertyValue) {
+                       if ($this->$propertyName !== $propertyValue) return TRUE;
                }
-               return $isDirty;
-       }
-       
+               return FALSE;
+       }       
 }
 ?>
\ No newline at end of file
index ddde30a..11c3a9a 100644 (file)
@@ -208,7 +208,7 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
        }
        
        /**
-        * Fetches a reasult set by given SQL statement snippets
+        * Fetches a rows from the database by given SQL statement snippets
         *
         * @param string $where WHERE statement
         * @param string $groupBy GROUP BY statement
@@ -218,38 +218,91 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
         * @return void
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       private function fetchFromDatabase($where = '1=1', $groupBy = '', $orderBy = '', $limit = '', $tableName = NULL) {
+       private function fetchFromDatabase($where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL, $tableName = NULL, $fields = NULL) {
                $tableName = $tableName === NULL ? $this->tableName : $tableName;
-               $resultSet = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        '*', // TODO limit fetched fields
                        $tableName,
-                       $where . $this->cObj->enableFields($this->tableName) . $this->cObj->enableFields($this->tableName),
+                       $where . $this->cObj->enableFields($tableName) . $this->cObj->enableFields($tableName),
                        $groupBy,
                        $orderBy,
                        $limit
                        );
                // TODO language overlay; workspace overlay
-               return $resultSet ? $resultSet : array();
-       }
+               return $rows ? $rows : array();
+       }       
+       
+       /**
+        * Fetches a rows from the database by given SQL statement snippets
+        *
+        * @param string $where WHERE statement
+        * @param string $groupBy GROUP BY statement
+        * @param string $orderBy ORDER BY statement
+        * @param string $limit LIMIT statement
+        * @param string $tableName The table name
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       private function fetchRelatedFromDatabase($parentObject, $tableName, $parentField = 'parent_uid', $where = NULL, $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+               if ($where === NULL) {
+                       // FIXME static uid
+                       $where = $parentField . '=' . intval($parentObject->getUid());
+               } else {
+                       $where = ' AND ' . $parentField . '=' . intval($parentObject->getUid());
+               }
+               return $this->fetchFromDatabase($where, $groupBy, $orderBy, $limit, $tableName);
+       }       
        
        /**
         * Dispatches the reconstitution of a domain object to an appropriate method
         *
-        * @param string $resultSet The result set fetched from the database
+        * @param string $rows The rows array fetched from the database
         * @throws TX_EXTMVC_Persistence_Exception_UnsupportedMethod
         * @return array An array of reconstituted domain objects
         * @author Jochen Rau <jochen.rau@typoplanet.de>
         */
-       protected function reconstituteObjects($resultSet) {
-               $reconstituteMethodName = 'reconstitute' . array_pop(explode('_', $this->aggregateRootClassName));
-               if (!method_exists($this, $reconstituteMethodName)) throw new TX_EXTMVC_Persistence_Exception_UnsupportedMethod('The method "' . $methodName . '" is not supported by the repository.', 1233180480);
+       protected function reconstituteObjects($rows, $objectClassName = NULL) {
+               if ($objectClassName === NULL) $objectClassName = $this->aggregateRootClassName;
+               $reconstituteMethodName = 'reconstitute' . array_pop(explode('_', $objectClassName));
                $objects = array();
-               foreach ($resultSet as $row) {
-                       $objects[] = $this->$reconstituteMethodName($row);
+               if (method_exists($this, $reconstituteMethodName)) {
+                       foreach ($rows as $row) {
+                               $objects[] = $this->$reconstituteMethodName($row);                              
+                       }
+               } else {
+                       foreach ($rows as $row) {
+                               $object = $this->reconstituteObject($objectClassName, $row);
+                               foreach ($object->getOneToManyRelations() as $propertyName => $configuration) {
+                                       $relatedRows = $this->fetchRelatedFromDatabase($object, $configuration['foreign_table'], $configuration['foreign_field']);
+                                       // FIXME static objectClassName
+                                       // TODO check infinite recursion
+                                       $relatedObjects = $this->reconstituteObjects($relatedRows, 'TX_Blogexample_Domain_Post');
+                                       $object->_reconstituteProperty($propertyName, $relatedObjects);
+                               }
+                               $objects[] = $object;
+                               $this->session->registerReconstitutedObject($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>
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function reconstituteObject($objectClassName, 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($objectClassName) . ':"' . $objectClassName . '":0:{};');
+               unset($GLOBALS['EXTMVC']['reconstituteObject']);
+               return $object;
+       }
+       
        /**
         * Persists changes (added, removed or changed objects) to the database.
         *