[!!!] Extbase: Reintegrating branch "dispatcher" to trunk. Resolves: #10605
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Mapper / DataMapFactory.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * A factory for a data map to map a single table configured in $TCA on a domain object.
27 *
28 * @package Extbase
29 * @subpackage Persistence\Mapper
30 * @version $ID:$
31 */
32 class Tx_Extbase_Persistence_Mapper_DataMapFactory implements t3lib_Singleton {
33
34 /**
35 * @var Tx_Extbase_Reflection_Service
36 */
37 protected $reflectionService;
38
39 /**
40 * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
41 */
42 protected $configurationManager;
43
44 /**
45 * Injects the reflection service
46 *
47 * @param Tx_Extbase_Reflection_Service $reflectionService
48 * @return void
49 */
50 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
51 $this->reflectionService = $reflectionService;
52 }
53
54 /**
55 * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager
56 * @return void
57 */
58 public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
59 $this->configurationManager = $configurationManager;
60 }
61
62 /**
63 * Builds a data map by adding column maps for all the configured columns in the $TCA.
64 * It also resolves the type of values the column is holding and the typo of relation the column
65 * represents.
66 *
67 * @param string $className The class name you want to fetch the Data Map for
68 * @return Tx_Extbase_Persistence_Mapper_DataMap The data map
69 */
70 public function buildDataMap($className) {
71 if (!class_exists($className)) {
72 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.');
73 }
74
75 $recordType = NULL;
76 $subclasses = array();
77 $tableName = strtolower($className);
78 $columnMapping = array();
79
80 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
81 $classSettings = $frameworkConfiguration['persistence']['classes'][$className];
82 if ($classSettings !== NULL) {
83 if (isset($classSettings['subclasses']) && is_array($classSettings['subclasses'])) {
84 $subclasses = $classSettings['subclasses'];
85 }
86 if (isset($classSettings['mapping']['recordType']) && strlen($classSettings['mapping']['recordType']) > 0) {
87 $recordType = $classSettings['mapping']['recordType'];
88 }
89 if (isset($classSettings['mapping']['tableName']) && strlen($classSettings['mapping']['tableName']) > 0) {
90 $tableName = $classSettings['mapping']['tableName'];
91 }
92 $classHierachy = array_merge(array($className), class_parents($className));
93 foreach ($classHierachy as $currentClassName) {
94 if (in_array($currentClassName, array('Tx_Extbase_DomainObject_AbstractEntity', 'Tx_Extbase_DomainObject_AbstractValueObject'))) {
95 break;
96 }
97 $currentClassSettings = $frameworkConfiguration['persistence']['classes'][$currentClassName];
98 if ($currentClassSettings !== NULL) {
99 if (isset($currentClassSettings['mapping']['columns']) && is_array($currentClassSettings['mapping']['columns'])) {
100 $columnMapping = t3lib_div::array_merge_recursive_overrule($columnMapping, $currentClassSettings['mapping']['columns'], 0, FALSE); // FALSE means: do not include empty values form 2nd array
101 }
102 }
103 }
104 }
105
106 $dataMap = new Tx_Extbase_Persistence_Mapper_DataMap($className, $tableName, $recordType, $subclasses);
107 $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName);
108
109 // $classPropertyNames = $this->reflectionService->getClassPropertyNames($className);
110 $tcaColumnsDefinition = $this->getColumnsDefinition($tableName);
111 $tcaColumnsDefinition = t3lib_div::array_merge_recursive_overrule($tcaColumnsDefinition, $columnMapping); // TODO Is this is too powerful?
112 foreach ($tcaColumnsDefinition as $columnName => $columnDefinition) {
113 if (isset($columnDefinition['mapOnProperty'])) {
114 $propertyName = $columnDefinition['mapOnProperty'];
115 } else {
116 $propertyName = Tx_Extbase_Utility_Extension::convertUnderscoredToLowerCamelCase($columnName);
117 }
118 // if (in_array($propertyName, $classPropertyNames)) { // TODO Enable check for property existance
119 $columnMap = new Tx_Extbase_Persistence_Mapper_ColumnMap($columnName, $propertyName);
120 $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
121 $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $propertyMetaData);
122 $dataMap->addColumnMap($columnMap);
123 // }
124 }
125 // debug($dataMap);
126 return $dataMap;
127 }
128
129 /**
130 * Returns the TCA ctrl section of the specified table; or NULL if not set
131 *
132 * @param string $tableName An optional table name to fetch the columns definition from
133 * @return array The TCA columns definition
134 */
135 protected function getControlSection($tableName) {
136 $this->includeTca($tableName);
137 return is_array($GLOBALS['TCA'][$tableName]['ctrl']) ? $GLOBALS['TCA'][$tableName]['ctrl'] : NULL;
138 }
139
140 /**
141 * Returns the TCA columns array of the specified table
142 *
143 * @param string $tableName An optional table name to fetch the columns definition from
144 * @return array The TCA columns definition
145 */
146 protected function getColumnsDefinition($tableName) {
147 $this->includeTca($tableName);
148 return is_array($GLOBALS['TCA'][$tableName]['columns']) ? $GLOBALS['TCA'][$tableName]['columns'] : array();
149 }
150
151 /**
152 * Includes the TCA for the given table
153 *
154 * @param string $tableName An optional table name to fetch the columns definition from
155 * @return void
156 */
157 protected function includeTca($tableName) {
158 if (TYPO3_MODE === 'FE') {
159 $GLOBALS['TSFE']->includeTCA();
160 }
161 t3lib_div::loadTCA($tableName);
162 }
163
164 protected function addMetaDataColumnNames(Tx_Extbase_Persistence_Mapper_DataMap $dataMap, $tableName) {
165 $controlSection = $GLOBALS['TCA'][$tableName]['ctrl'];
166 $dataMap->setPageIdColumnName('pid');
167 if (isset($controlSection['tstamp'])) $dataMap->setModificationDateColumnName($controlSection['tstamp']);
168 if (isset($controlSection['crdate'])) $dataMap->setCreationDateColumnName($controlSection['crdate']);
169 if (isset($controlSection['cruser_id'])) $dataMap->setCreatorColumnName($controlSection['cruser_id']);
170 if (isset($controlSection['delete'])) $dataMap->setDeletedFlagColumnName($controlSection['delete']);
171 if (isset($controlSection['languageField'])) $dataMap->setLanguageIdColumnName($controlSection['languageField']);
172 if (isset($controlSection['transOrigPointerField'])) $dataMap->setTranslationOriginColumnName($controlSection['transOrigPointerField']);
173 if (isset($controlSection['type'])) $dataMap->setRecordTypeColumnName($controlSection['type']);
174 if (isset($controlSection['enablecolumns']['disabled'])) $dataMap->setDisabledFlagColumnName($controlSection['enablecolumns']['disabled']);
175 if (isset($controlSection['enablecolumns']['starttime'])) $dataMap->setStartTimeColumnName($controlSection['enablecolumns']['starttime']);
176 if (isset($controlSection['enablecolumns']['endtime'])) $dataMap->setEndTimeColumnName($controlSection['enablecolumns']['endtime']);
177 if (isset($controlSection['enablecolumns']['fe_group'])) $dataMap->setFrontEndUserGroupColumnName($controlSection['enablecolumns']['fe_group']);
178 return $dataMap;
179 }
180
181 /**
182 * This method tries to determine the type of type of relation to other tables and sets it based on
183 * the $TCA column configuration
184 *
185 * @param Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap The column map
186 * @param string $columnConfiguration The column configuration from $TCA
187 * @param array $propertyMetaData The property metadata as delivered by the reflection service
188 * @return void
189 */
190 protected function setRelations(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration, $propertyMetaData) {
191 if (isset($columnConfiguration)) {
192 if (isset($columnConfiguration['MM']) || isset($columnConfiguration['foreign_selector'])) {
193 $columnMap = $this->setManyToManyRelation($columnMap, $columnConfiguration);
194 } elseif (isset($propertyMetaData['elementType'])) {
195 $columnMap = $this->setOneToManyRelation($columnMap, $columnConfiguration);
196 } elseif (isset($propertyMetaData['type']) && strpos($propertyMetaData['type'], '_') !== FALSE) {
197 $columnMap = $this->setOneToOneRelation($columnMap, $columnConfiguration);
198 } else {
199 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_NONE);
200 }
201 } else {
202 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_NONE);
203 }
204 return $columnMap;
205 }
206
207 /**
208 * This method sets the configuration for a 1:1 relation based on
209 * the $TCA column configuration
210 *
211 * @param string $columnMap The column map
212 * @param string $columnConfiguration The column configuration from $TCA
213 * @return void
214 */
215 protected function setOneToOneRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
216 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE);
217 $columnMap->setChildTableName($columnConfiguration['foreign_table']);
218 $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
219 $columnMap->setChildSortbyFieldName($columnConfiguration['foreign_sortby']);
220 $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
221 $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
222 return $columnMap;
223 }
224
225 /**
226 * This method sets the configuration for a 1:n relation based on
227 * the $TCA column configuration
228 *
229 * @param string $columnMap The column map
230 * @param string $columnConfiguration The column configuration from $TCA
231 * @return void
232 */
233 protected function setOneToManyRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
234 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY);
235 $columnMap->setChildTableName($columnConfiguration['foreign_table']);
236 $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
237 $columnMap->setChildSortbyFieldName($columnConfiguration['foreign_sortby']);
238 $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
239 $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
240 return $columnMap;
241 }
242
243 /**
244 * This method sets the configuration for a m:n relation based on
245 * the $TCA column configuration
246 *
247 * @param string $columnMap The column map
248 * @param string $columnConfiguration The column configuration from $TCA
249 * @return void
250 */
251 protected function setManyToManyRelation(Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, $columnConfiguration) {
252 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY);
253 if (isset($columnConfiguration['MM'])) {
254 $columnMap->setChildTableName($columnConfiguration['foreign_table']);
255 $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
256 $columnMap->setRelationTableName($columnConfiguration['MM']);
257 if (is_array($columnConfiguration['MM_match_fields'])) {
258 $columnMap->setRelationTableMatchFields($columnConfiguration['MM_match_fields']);
259 }
260 if (is_array($columnConfiguration['MM_insert_fields'])) {
261 $columnMap->setRelationTableInsertFields($columnConfiguration['MM_insert_fields']);
262 }
263 $columnMap->setRelationTableWhereStatement($columnConfiguration['MM_table_where']);
264 if (!empty($columnConfiguration['MM_opposite_field'])) {
265 $columnMap->setParentKeyFieldName('uid_foreign');
266 $columnMap->setChildKeyFieldName('uid_local');
267 $columnMap->setChildSortByFieldName('sorting_foreign');
268 } else {
269 $columnMap->setParentKeyFieldName('uid_local');
270 $columnMap->setChildKeyFieldName('uid_foreign');
271 $columnMap->setChildSortByFieldName('sorting');
272 }
273 } elseif (isset($columnConfiguration['foreign_selector'])) {
274 $columns = $this->getColumnsDefinition($columnConfiguration['foreign_table']);
275 $childKeyFieldName = $columnConfiguration['foreign_selector'];
276 $columnMap->setChildTableName($columns[$childKeyFieldName]['config']['foreign_table']);
277 $columnMap->setRelationTableName($columnConfiguration['foreign_table']);
278 $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
279 $columnMap->setChildKeyFieldName($childKeyFieldName);
280 $columnMap->setChildSortByFieldName($columnConfiguration['foreign_sortby']);
281 } else {
282 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);
283 }
284 if ($this->getControlSection($columnMap->getRelationTableName()) !== NULL) {
285 $columnMap->setRelationTablePageIdColumnName('pid');
286 }
287 return $columnMap;
288 }
289
290 }