* introduced a tca mapper
authorJochen Rau <j.rau@web.de>
Mon, 2 Feb 2009 16:46:30 +0000 (16:46 +0000)
committerJochen Rau <j.rau@web.de>
Mon, 2 Feb 2009 16:46:30 +0000 (16:46 +0000)
typo3/sysext/extbase/Classes/DomainObject/TX_EXTMVC_DomainObject_AbstractDomainObject.php
typo3/sysext/extbase/Classes/Persistence/Mapper/TX_EXTMVC_Persistence_Mapper_TcaMapper.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_Repository.php
typo3/sysext/extbase/Classes/Persistence/TX_EXTMVC_Persistence_Session.php
typo3/sysext/extbase/class.tx_extmvc_dispatcher.php

index 0cac22b..2263d35 100644 (file)
@@ -22,6 +22,7 @@ declare(ENCODING = 'utf-8');
  *                                                                        */
 
 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_TcaMapper.php');
 
 /**
  * A generic Domain Object
@@ -53,34 +54,14 @@ abstract class TX_EXTMVC_DomainObject_AbstractDomainObject {
        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->manyToManyRelations[$propertyName] = array(
-                                                       'foreign_class' => $columnConfiguration['config']['foreign_class'],
-                                                       'foreign_table' => $columnConfiguration['config']['foreign_table'],
-                                                       'MM' => $columnConfiguration['config']['MM']
-                                                       );
-                                       } else {
-                                               // TODO implement a $TCA object 
-                                               $this->oneToManyRelations[$propertyName] = array(
-                                                       'foreign_class' => $columnConfiguration['config']['foreign_class'],
-                                                       'foreign_table' => $columnConfiguration['config']['foreign_table'],
-                                                       'foreign_field' => $columnConfiguration['config']['foreign_field'],
-                                                       'foreign_table_field' => $columnConfiguration['config']['foreign_table_field']
-                                                       );
-                                       }
-                               }                               
+               $properties = get_object_vars($this);
+               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper');
+               foreach ($properties as $propertyName => $propertyValue) {
+                       if ($dataMapper->isPersistable($this, $propertyName)) {
+                               $this->cleanProperties[$propertyName] = NULL;
                        }
-                       $this->cleanProperties['uid'] = NULL;
+               }
+               $this->cleanProperties['uid'] = NULL;
        }
        
        public function getOneToManyRelations() {
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
new file mode 100644 (file)
index 0000000..17da9ae
--- /dev/null
@@ -0,0 +1,260 @@
+<?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 onto 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;
+               
+       /**
+        * Constructs a new mapper
+        *
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function __construct() {
+               $this->cObj = t3lib_div::makeInstance('tslib_cObj');
+               $GLOBALS['TSFE']->includeTCA();
+       }
+               
+       /**
+        * Returns all objects of the given class name
+        *
+        * @return array An array of objects, empty if no objects found
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function loadAll($className) {
+               return $this->reconstituteObjects($this->fetch($this->getTableName($className)));
+       }
+       
+       /**
+        * Finds objects matching 'property=xyz'
+        *
+        * @param string $propertyName The name of the property (will be chekced by a white list)
+        * @param string $arguments The arguments of the magic findBy method
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function loadWhere($className, $propertyName, $arguments) {
+               $tableName = $this->getTableName($className);
+               $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($arguments[0], $tableName);
+               return $this->reconstituteObjects($className, $this->fetch($tableName, $where));
+       }
+       
+       /**
+        * 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>
+        */
+       private function fetch($tableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
+               $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>
+        */
+       private 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>
+        */
+       private 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, $depth = 0) {
+               if ($depth > 10) throw new TX_EXTMVC_Persistence_Exception_RecursionTooDeep('The maximum depth of ' . $depth . ' recursions was reached.', 1233352348);
+               foreach ($rows as $row) {
+                       $object = $this->reconstituteObject($className, $row);
+                       foreach ($object->getOneToManyRelations() as $propertyName => $tcaColumnConfiguration) {
+                               $relatedRows = $this->fetchOneToMany($object, $tcaColumnConfiguration['foreign_field'], $tcaColumnConfiguration['foreign_table']);
+                               $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
+                               $object->_reconstituteProperty($propertyName, $relatedObjects);
+                       }
+                       foreach ($object->getManyToManyRelations() as $propertyName => $tcaColumnConfiguration) {
+                               $relatedRows = $this->fetchManyToMany($object, $tcaColumnConfiguration['foreign_table'], $tcaColumnConfiguration['MM']);
+                               $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
+                               $object->_reconstituteProperty($propertyName, $relatedObjects);
+                       }
+                       $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>
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function reconstituteObject($className, array $properties = array()) {
+               // those objects will be fetched from within the __wakeup() method of the object...
+               $GLOBALS['EXTMVC']['reconstituteObject']['properties'] = $properties;
+               $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
+               unset($GLOBALS['EXTMVC']['reconstituteObject']);
+               return $object;
+       }       
+
+       /**
+        * Inserts an object in the database.
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function insert(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
+               $row = array(
+                       'pid' => 0, // FIXME
+                       'tstamp' => time(),
+                       'crdate' => time(),
+                       // FIXME static fields
+                       'name' => $object->getName(),
+                       'description' => $object->getDescription()
+                       // 'logo' => $object->getLogo(),
+                       );
+               $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
+                       $this->getTableName($this->getClassName($object)),
+                       $row
+                       );
+       }
+       
+       /**
+        * Updates an object
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function update(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
+
+       }
+       
+       /**
+        * Deletes an object
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       public function delete(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $onlyMarkAsDeleted = TRUE) {
+               $tableName = $this->getTableName($this->getClassName($object));
+               if ($onlyMarkAsDeleted) {
+                       $deletedColumnName = $this->getDeletedColumnName($tableName);
+                       if (empty($deletedColumnName)) throw new Exception('Could not mark object as deleted in table "' . $tableName . '"');
+               $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                               $this->getTableName($object),
+                               'uid = ' . intval($object->getUid()),
+                               array($deletedColumnName => 1)
+                               );
+               } else {
+                       // TODO remove associated objects
+                       
+                       $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                               $this->getTableName($object),
+                               'uid=' . intval($object->getUid())
+                               );
+               }
+       }
+       
+       public function getColumns(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
+               t3lib_div::loadTCA($this->getTableName($this->getClassName($object)));
+               return $GLOBALS['TCA'][$this->getTableName($this->getClassName($object))]['columns'];
+       }
+               
+       protected function getClassName(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
+               return get_class($object);
+       }
+       
+       protected function getTableName($className) {
+               return strtolower($className);
+       }
+       
+       protected function getDeletedColumnName($tableName) {
+               return $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
+       }
+       
+       protected function getHiddenColumnName($tableName) {
+               return $GLOBALS['TCA'][$tableNAme]['ctrl']['enablecolumns']['disabled'];
+       }
+       
+       public function isPersistable(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $propertyName) {
+               $columns = $this->getColumns($object);
+               foreach ($columns as $columnName => $columnConfiguration) {
+                       if (TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($columnName) == $propertyName) return TRUE;
+               }
+               return FALSE;
+       }
+       
+}
+?>
\ No newline at end of file
index 34b3771..cb90982 100644 (file)
@@ -87,6 +87,7 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
                $this->objects = new TX_EXTMVC_Persistence_ObjectStorage();
                $this->cObj = t3lib_div::makeInstance('tslib_cObj');
                $repositoryClassName = get_class($this);
+               $this->dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper');
                // the session object is a singleton
                $this->session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session');
                $this->session->registerRepository($repositoryClassName);
@@ -334,11 +335,12 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
         */
        protected function deleteRemoved() {
                $removedObjects = $this->session->getRemovedObjects($this->getAggregateRootClassName());
+
                // FIXME remove debug code
                // debug($removedObjects, 'removed objects');
 
                foreach ($removedObjects as $object) {
-                       $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery($this->getTableName(), 'uid=' . $object->getUid());
+                       $this->dataMapper->delete($object);
                }
        }
        
@@ -355,19 +357,24 @@ class TX_EXTMVC_Persistence_Repository implements TX_EXTMVC_Persistence_Reposito
                // debug($addedObjects, 'added objects');
                
                foreach ($addedObjects as $object) {
-                       $row = array(
-                               'pid' => 0, // FIXME
-                               'tstamp' => time(),
-                               'crdate' => time(),
-                               // FIXME static fields
-                               'name' => $object->getName(),
-                               'description' => $object->getDescription()
-                               // 'logo' => $object->getLogo(),
-                               );
-                       $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
-                               $this->getTableName(),
-                               $row
-                               );
+                       $this->dataMapper->insert($object);
+               }
+       }
+       
+       /**
+        * Updates all dirty objects.
+        *
+        * @return void
+        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        */
+       protected function updateDirty() {
+               $dirtyObjects = $this->session->getDirtyObjects($this->getAggregateRootClassName());
+
+               // FIXME remove debug code
+               // debug($dirtyObjects, 'dirty objects');
+
+               foreach ($dirtyObjects as $object) {
+                       $this->dataMapper->update($dirtyObjects);
                }
        }
        
index f18c4d1..4f46f64 100644 (file)
@@ -224,7 +224,7 @@ class TX_EXTMVC_Persistence_Session implements t3lib_singleton {
        }
        
        public function commit() {
-               foreach ($this->repositoryClassNames as $repositoryClassName) {
+               foreach ($this->getRepositoryClassNames() as $repositoryClassName) {
                        $repository = t3lib_div::makeInstance($repositoryClassName);
                        $repository->persistAll();
                }
index 2298efe..9fe8444 100644 (file)
@@ -33,6 +33,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');
 
 // FIXME
 require_once(t3lib_extMgm::extPath('blogexample') . 'Classes/Controller/TX_Blogexample_Controller_PostsController.php');
@@ -109,6 +110,7 @@ class TX_EXTMVC_Dispatcher {
                // $settings = $this->configurationManager->getSettings($extensionKey);
                // $controller->injectSettings($this->configurationManager->getSettings($request->getControllerExtensionKey()));
                $session = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Session');
+               $dataMapper = t3lib_div::makeInstance('TX_EXTMVC_Persistence_Mapper_TcaMapper');
                $controller->processRequest($request, $response);
                $session->commit();
                $session->clear();