TYPO3 API  SVNRelease
DataMapFactory.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
00006  *  All rights reserved
00007  *
00008  *  This script is part of the TYPO3 project. The TYPO3 project is
00009  *  free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  The GNU General Public License can be found at
00015  *  http://www.gnu.org/copyleft/gpl.html.
00016  *
00017  *  This script is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  *  GNU General Public License for more details.
00021  *
00022  *  This copyright notice MUST APPEAR in all copies of the script!
00023  ***************************************************************/
00024 
00025 /**
00026  * A factory for a data map to map a single table configured in $TCA on a domain object.
00027  *
00028  * @package Extbase
00029  * @subpackage Persistence\Mapper
00030  * @version $ID:$
00031  */
00032 class Tx_Extbase_Persistence_Mapper_DataMapFactory implements t3lib_Singleton {
00033 
00034     /**
00035      * @var Tx_Extbase_Reflection_Service
00036      */
00037     protected $reflectionService;
00038 
00039     /**
00040      * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
00041      */
00042     protected $configurationManager;
00043 
00044     /**
00045      * Injects the reflection service
00046      *
00047      * @param Tx_Extbase_Reflection_Service $reflectionService
00048      * @return void
00049      */
00050     public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
00051         $this->reflectionService = $reflectionService;
00052     }
00053 
00054     /**
00055      * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager
00056      * @return void
00057      */
00058     public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
00059         $this->configurationManager = $configurationManager;
00060     }
00061 
00062     /**
00063      * Builds a data map by adding column maps for all the configured columns in the $TCA.
00064      * It also resolves the type of values the column is holding and the typo of relation the column
00065      * represents.
00066      *
00067      * @param string $className The class name you want to fetch the Data Map for
00068      * @return Tx_Extbase_Persistence_Mapper_DataMap The data map
00069      */
00070     public function buildDataMap($className) {
00071         if (!class_exists($className)) {
00072             throw new Tx_Extbase_Persistence_Exception_InvalidClass('Could not find class definition for name "' . $className . '". This could be caused by a mis-spelling of the class name in the class definition.');
00073         }
00074 
00075         $recordType = NULL;
00076         $subclasses = array();
00077         $tableName = strtolower($className);
00078         $columnMapping = array();
00079 
00080         $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
00081         $classSettings = $frameworkConfiguration['persistence']['classes'][$className];
00082         if ($classSettings !== NULL) {
00083             if (isset($classSettings['subclasses']) && is_array($classSettings['subclasses'])) {
00084                 $subclasses = $classSettings['subclasses'];
00085             }
00086             if (isset($classSettings['mapping']['recordType']) && strlen($classSettings['mapping']['recordType']) > 0) {
00087                 $recordType = $classSettings['mapping']['recordType'];
00088             }
00089             if (isset($classSettings['mapping']['tableName']) && strlen($classSettings['mapping']['tableName']) > 0) {
00090                 $tableName = $classSettings['mapping']['tableName'];
00091             }
00092             $classHierachy = array_merge(array($className), class_parents($className));
00093             foreach ($classHierachy as $currentClassName) {
00094                 if (in_array($currentClassName, array('Tx_Extbase_DomainObject_AbstractEntity', 'Tx_Extbase_DomainObject_AbstractValueObject'))) {
00095                     break;
00096                 }
00097                 $currentClassSettings = $frameworkConfiguration['persistence']['classes'][$currentClassName];
00098                 if ($currentClassSettings !== NULL) {
00099                     if (isset($currentClassSettings['mapping']['columns']) && is_array($currentClassSettings['mapping']['columns'])) {
00100                         $columnMapping = t3lib_div::array_merge_recursive_overrule($columnMapping, $currentClassSettings['mapping']['columns'], 0, FALSE); // FALSE means: do not include empty values form 2nd array
00101                     }
00102                 }
00103             }
00104         }
00105 
00106         $dataMap = new Tx_Extbase_Persistence_Mapper_DataMap($className, $tableName, $recordType, $subclasses);
00107         $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName);
00108 
00109         // $classPropertyNames = $this->reflectionService->getClassPropertyNames($className);
00110         $tcaColumnsDefinition = $this->getColumnsDefinition($tableName);
00111         $tcaColumnsDefinition = t3lib_div::array_merge_recursive_overrule($tcaColumnsDefinition, $columnMapping); // TODO Is this is too powerful?
00112         foreach ($tcaColumnsDefinition as $columnName => $columnDefinition) {
00113             if (isset($columnDefinition['mapOnProperty'])) {
00114                 $propertyName = $columnDefinition['mapOnProperty'];
00115             } else {
00116                 $propertyName = t3lib_div::underscoredToLowerCamelCase($columnName);
00117             }
00118             // if (in_array($propertyName, $classPropertyNames)) { // TODO Enable check for property existance
00119                 $columnMap = new Tx_Extbase_Persistence_Mapper_ColumnMap($columnName, $propertyName);
00120                 $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
00121                 $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $propertyMetaData);
00122                 $dataMap->addColumnMap($columnMap);
00123             // }
00124         }
00125         // debug($dataMap);
00126         return $dataMap;
00127     }
00128 
00129     /**
00130      * Returns the TCA ctrl section of the specified table; or NULL if not set
00131      *
00132      * @param string $tableName An optional table name to fetch the columns definition from
00133      * @return array The TCA columns definition
00134      */
00135     protected function getControlSection($tableName) {
00136         $this->includeTca($tableName);
00137         return is_array($GLOBALS['TCA'][$tableName]['ctrl']) ? $GLOBALS['TCA'][$tableName]['ctrl'] : NULL;
00138     }
00139 
00140     /**
00141      * Returns the TCA columns array of the specified table
00142      *
00143      * @param string $tableName An optional table name to fetch the columns definition from
00144      * @return array The TCA columns definition
00145      */
00146     protected function getColumnsDefinition($tableName) {
00147         $this->includeTca($tableName);
00148         return is_array($GLOBALS['TCA'][$tableName]['columns']) ? $GLOBALS['TCA'][$tableName]['columns'] : array();
00149     }
00150 
00151     /**
00152      * Includes the TCA for the given table
00153      *
00154      * @param string $tableName An optional table name to fetch the columns definition from
00155      * @return void
00156      */
00157     protected function includeTca($tableName) {
00158         if (TYPO3_MODE === 'FE') {
00159             $GLOBALS['TSFE']->includeTCA();
00160         }
00161         t3lib_div::loadTCA($tableName);
00162     }
00163 
00164     protected function addMetaDataColumnNames(Tx_Extbase_Persistence_Mapper_DataMap $dataMap, $tableName) {
00165         $controlSection = $GLOBALS['TCA'][$tableName]['ctrl'];
00166         $dataMap->setPageIdColumnName('pid');
00167         if (isset($controlSection['tstamp'])) $dataMap->setModificationDateColumnName($controlSection['tstamp']);
00168         if (isset($controlSection['crdate'])) $dataMap->setCreationDateColumnName($controlSection['crdate']);
00169         if (isset($controlSection['cruser_id'])) $dataMap->setCreatorColumnName($controlSection['cruser_id']);
00170         if (isset($controlSection['delete'])) $dataMap->setDeletedFlagColumnName($controlSection['delete']);
00171         if (isset($controlSection['languageField'])) $dataMap->setLanguageIdColumnName($controlSection['languageField']);
00172         if (isset($controlSection['transOrigPointerField'])) $dataMap->setTranslationOriginColumnName($controlSection['transOrigPointerField']);
00173         if (isset($controlSection['type'])) $dataMap->setRecordTypeColumnName($controlSection['type']);
00174         if (isset($controlSection['enablecolumns']['disabled'])) $dataMap->setDisabledFlagColumnName($controlSection['enablecolumns']['disabled']);
00175         if (isset($controlSection['enablecolumns']['starttime'])) $dataMap->setStartTimeColumnName($controlSection['enablecolumns']['starttime']);
00176         if (isset($controlSection['enablecolumns']['endtime'])) $dataMap->setEndTimeColumnName($controlSection['enablecolumns']['endtime']);
00177         if (isset($controlSection['enablecolumns']['fe_group'])) $dataMap->setFrontEndUserGroupColumnName($controlSection['enablecolumns']['fe_group']);
00178         return $dataMap;
00179     }
00180 
00181     /**
00182      * This method tries to determine the type of type of relation to other tables and sets it based on
00183      * the $TCA column configuration
00184      *
00185      * @param Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap The column map
00186      * @param string $columnConfiguration The column configuration from $TCA
00187      * @param array $propertyMetaData The property metadata as delivered by the reflection service
00188      * @return void
00189      */
00190     protected function setRelations(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration, $propertyMetaData) {
00191         if (isset($columnConfiguration)) {
00192             if (isset($columnConfiguration['MM']) || isset($columnConfiguration['foreign_selector'])) {
00193                 $columnMap = $this->setManyToManyRelation($columnMap, $columnConfiguration);
00194             } elseif (isset($propertyMetaData['elementType'])) {
00195                 $columnMap = $this->setOneToManyRelation($columnMap, $columnConfiguration);
00196             } elseif (isset($propertyMetaData['type']) && strpos($propertyMetaData['type'], '_') !== FALSE) {
00197                 $columnMap = $this->setOneToOneRelation($columnMap, $columnConfiguration);
00198             } else {
00199                     $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_NONE);
00200             }
00201         } else {
00202             $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_NONE);
00203         }
00204         return $columnMap;
00205     }
00206 
00207     /**
00208      * This method sets the configuration for a 1:1 relation based on
00209      * the $TCA column configuration
00210      *
00211      * @param string $columnMap The column map
00212      * @param string $columnConfiguration The column configuration from $TCA
00213      * @return void
00214      */
00215     protected function setOneToOneRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
00216         $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE);
00217         $columnMap->setChildTableName($columnConfiguration['foreign_table']);
00218         $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
00219         $columnMap->setChildSortbyFieldName($columnConfiguration['foreign_sortby']);
00220         $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
00221         $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
00222         return $columnMap;
00223     }
00224 
00225     /**
00226      * This method sets the configuration for a 1:n relation based on
00227      * the $TCA column configuration
00228      *
00229      * @param string $columnMap The column map
00230      * @param string $columnConfiguration The column configuration from $TCA
00231      * @return void
00232      */
00233     protected function setOneToManyRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
00234         $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY);
00235         $columnMap->setChildTableName($columnConfiguration['foreign_table']);
00236         $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
00237         $columnMap->setChildSortbyFieldName($columnConfiguration['foreign_sortby']);
00238         $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
00239         $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
00240         return $columnMap;
00241     }
00242 
00243     /**
00244      * This method sets the configuration for a m:n relation based on
00245      * the $TCA column configuration
00246      *
00247      * @param string $columnMap The column map
00248      * @param string $columnConfiguration The column configuration from $TCA
00249      * @return void
00250      */
00251     protected function setManyToManyRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
00252         $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY);
00253         if (isset($columnConfiguration['MM'])) {
00254             $columnMap->setChildTableName($columnConfiguration['foreign_table']);
00255             $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
00256             $columnMap->setRelationTableName($columnConfiguration['MM']);
00257             if (is_array($columnConfiguration['MM_match_fields'])) {
00258                 $columnMap->setRelationTableMatchFields($columnConfiguration['MM_match_fields']);
00259             }
00260             if (is_array($columnConfiguration['MM_insert_fields'])) {
00261                 $columnMap->setRelationTableInsertFields($columnConfiguration['MM_insert_fields']);
00262             }
00263             $columnMap->setRelationTableWhereStatement($columnConfiguration['MM_table_where']);
00264             if (!empty($columnConfiguration['MM_opposite_field'])) {
00265                 $columnMap->setParentKeyFieldName('uid_foreign');
00266                 $columnMap->setChildKeyFieldName('uid_local');
00267                 $columnMap->setChildSortByFieldName('sorting_foreign');
00268             } else {
00269                 $columnMap->setParentKeyFieldName('uid_local');
00270                 $columnMap->setChildKeyFieldName('uid_foreign');
00271                 $columnMap->setChildSortByFieldName('sorting');
00272             }
00273         } elseif (isset($columnConfiguration['foreign_selector'])) {
00274             $columns = $this->getColumnsDefinition($columnConfiguration['foreign_table']);
00275             $childKeyFieldName = $columnConfiguration['foreign_selector'];
00276             $columnMap->setChildTableName($columns[$childKeyFieldName]['config']['foreign_table']);
00277             $columnMap->setRelationTableName($columnConfiguration['foreign_table']);
00278             $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
00279             $columnMap->setChildKeyFieldName($childKeyFieldName);
00280             $columnMap->setChildSortByFieldName($columnConfiguration['foreign_sortby']);
00281         } else {
00282             throw new Tx_Extbase_Persistence_Exception_UnsupportedRelation('The given information to build a many-to-many-relation was not sufficient. Check your TCA definitions. mm-relations with IRRE must have at least a defined "MM" or "foreign_selector".', 1268817963);
00283         }
00284         if ($this->getControlSection($columnMap->getRelationTableName()) !== NULL) {
00285             $columnMap->setRelationTablePageIdColumnName('pid');
00286         }
00287         return $columnMap;
00288     }
00289 
00290 }