TYPO3  7.6
DataMapFactory.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 {
25  protected $reflectionService;
26 
31 
35  protected $objectManager;
36 
40  protected $cacheManager;
41 
45  protected $dataMapCache;
46 
50  public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
51  {
52  $this->reflectionService = $reflectionService;
53  }
54 
58  public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
59  {
60  $this->configurationManager = $configurationManager;
61  }
62 
66  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
67  {
68  $this->objectManager = $objectManager;
69  }
70 
74  public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
75  {
76  $this->cacheManager = $cacheManager;
77  }
78 
84  public function initializeObject()
85  {
86  $this->dataMapCache = $this->cacheManager->getCache('extbase_datamapfactory_datamap');
87  }
88 
97  public function buildDataMap($className)
98  {
99  $dataMap = $this->dataMapCache->get(str_replace('\\', '%', $className));
100  if ($dataMap === false) {
101  $dataMap = $this->buildDataMapInternal($className);
102  $this->dataMapCache->set(str_replace('\\', '%', $className), $dataMap);
103  }
104  return $dataMap;
105  }
106 
116  protected function buildDataMapInternal($className)
117  {
118  if (!class_exists($className)) {
119  throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidClassException('Could not find class definition for name "' . $className . '". This could be caused by a mis-spelling of the class name in the class definition.');
120  }
121  $recordType = null;
122  $subclasses = array();
123  $tableName = $this->resolveTableName($className);
124  $columnMapping = array();
125  $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
126  $classSettings = $frameworkConfiguration['persistence']['classes'][$className];
127  if ($classSettings !== null) {
128  if (isset($classSettings['subclasses']) && is_array($classSettings['subclasses'])) {
129  $subclasses = $this->resolveSubclassesRecursive($frameworkConfiguration['persistence']['classes'], $classSettings['subclasses']);
130  }
131  if (isset($classSettings['mapping']['recordType']) && $classSettings['mapping']['recordType'] !== '') {
132  $recordType = $classSettings['mapping']['recordType'];
133  }
134  if (isset($classSettings['mapping']['tableName']) && $classSettings['mapping']['tableName'] !== '') {
135  $tableName = $classSettings['mapping']['tableName'];
136  }
137  $classHierarchy = array_merge(array($className), class_parents($className));
138  foreach ($classHierarchy as $currentClassName) {
139  if (in_array($currentClassName, array(\TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class))) {
140  break;
141  }
142  $currentClassSettings = $frameworkConfiguration['persistence']['classes'][$currentClassName];
143  if ($currentClassSettings !== null) {
144  if (isset($currentClassSettings['mapping']['columns']) && is_array($currentClassSettings['mapping']['columns'])) {
145  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($columnMapping, $currentClassSettings['mapping']['columns'], true, false);
146  }
147  }
148  }
149  }
151  $dataMap = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap::class, $className, $tableName, $recordType, $subclasses);
152  $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName);
153  // $classPropertyNames = $this->reflectionService->getClassPropertyNames($className);
154  $tcaColumnsDefinition = $this->getColumnsDefinition($tableName);
155  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($tcaColumnsDefinition, $columnMapping);
156  // @todo Is this is too powerful?
157 
158  foreach ($tcaColumnsDefinition as $columnName => $columnDefinition) {
159  if (isset($columnDefinition['mapOnProperty'])) {
160  $propertyName = $columnDefinition['mapOnProperty'];
161  } else {
162  $propertyName = \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToLowerCamelCase($columnName);
163  }
164  // if (in_array($propertyName, $classPropertyNames)) {
165  // @todo Enable check for property existence
166  $columnMap = $this->createColumnMap($columnName, $propertyName);
167  $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
168  $columnMap = $this->setType($columnMap, $columnDefinition['config']);
169  $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $propertyMetaData);
170  $columnMap = $this->setFieldEvaluations($columnMap, $columnDefinition['config']);
171  $dataMap->addColumnMap($columnMap);
172  }
173  return $dataMap;
174  }
175 
182  protected function resolveTableName($className)
183  {
184  $className = ltrim($className, '\\');
185  if (strpos($className, '\\') !== false) {
186  $classNameParts = explode('\\', $className);
187  // Skip vendor and product name for core classes
188  if (strpos($className, 'TYPO3\\CMS\\') === 0) {
189  $classPartsToSkip = 2;
190  } else {
191  $classPartsToSkip = 1;
192  }
193  $tableName = 'tx_' . strtolower(implode('_', array_slice($classNameParts, $classPartsToSkip)));
194  } else {
195  $tableName = strtolower($className);
196  }
197  return $tableName;
198  }
199 
208  protected function resolveSubclassesRecursive(array $classesConfiguration, array $subclasses)
209  {
210  $allSubclasses = array();
211  foreach ($subclasses as $subclass) {
212  $allSubclasses[] = $subclass;
213  if (isset($classesConfiguration[$subclass]['subclasses']) && is_array($classesConfiguration[$subclass]['subclasses'])) {
214  $childSubclasses = $this->resolveSubclassesRecursive($classesConfiguration, $classesConfiguration[$subclass]['subclasses']);
215  $allSubclasses = array_merge($allSubclasses, $childSubclasses);
216  }
217  }
218  return $allSubclasses;
219  }
220 
227  protected function getControlSection($tableName)
228  {
229  return is_array($GLOBALS['TCA'][$tableName]['ctrl']) ? $GLOBALS['TCA'][$tableName]['ctrl'] : null;
230  }
231 
238  protected function getColumnsDefinition($tableName)
239  {
240  return is_array($GLOBALS['TCA'][$tableName]['columns']) ? $GLOBALS['TCA'][$tableName]['columns'] : array();
241  }
242 
248  protected function addMetaDataColumnNames(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap $dataMap, $tableName)
249  {
250  $controlSection = $GLOBALS['TCA'][$tableName]['ctrl'];
251  $dataMap->setPageIdColumnName('pid');
252  if (isset($controlSection['tstamp'])) {
253  $dataMap->setModificationDateColumnName($controlSection['tstamp']);
254  }
255  if (isset($controlSection['crdate'])) {
256  $dataMap->setCreationDateColumnName($controlSection['crdate']);
257  }
258  if (isset($controlSection['cruser_id'])) {
259  $dataMap->setCreatorColumnName($controlSection['cruser_id']);
260  }
261  if (isset($controlSection['delete'])) {
262  $dataMap->setDeletedFlagColumnName($controlSection['delete']);
263  }
264  if (isset($controlSection['languageField'])) {
265  $dataMap->setLanguageIdColumnName($controlSection['languageField']);
266  }
267  if (isset($controlSection['transOrigPointerField'])) {
268  $dataMap->setTranslationOriginColumnName($controlSection['transOrigPointerField']);
269  }
270  if (isset($controlSection['transOrigDiffSourceField'])) {
271  $dataMap->setTranslationOriginDiffSourceName($controlSection['transOrigDiffSourceField']);
272  }
273  if (isset($controlSection['type'])) {
274  $dataMap->setRecordTypeColumnName($controlSection['type']);
275  }
276  if (isset($controlSection['rootLevel'])) {
277  $dataMap->setRootLevel($controlSection['rootLevel']);
278  }
279  if (isset($controlSection['is_static'])) {
280  $dataMap->setIsStatic($controlSection['is_static']);
281  }
282  if (isset($controlSection['enablecolumns']['disabled'])) {
283  $dataMap->setDisabledFlagColumnName($controlSection['enablecolumns']['disabled']);
284  }
285  if (isset($controlSection['enablecolumns']['starttime'])) {
286  $dataMap->setStartTimeColumnName($controlSection['enablecolumns']['starttime']);
287  }
288  if (isset($controlSection['enablecolumns']['endtime'])) {
289  $dataMap->setEndTimeColumnName($controlSection['enablecolumns']['endtime']);
290  }
291  if (isset($controlSection['enablecolumns']['fe_group'])) {
292  $dataMap->setFrontEndUserGroupColumnName($controlSection['enablecolumns']['fe_group']);
293  }
294  return $dataMap;
295  }
296 
304  protected function setType(ColumnMap $columnMap, $columnConfiguration)
305  {
306  $tableColumnType = (isset($columnConfiguration['type'])) ? $columnConfiguration['type'] : null;
307  $columnMap->setType(\TYPO3\CMS\Core\DataHandling\TableColumnType::cast($tableColumnType));
308  $tableColumnSubType = (isset($columnConfiguration['internal_type'])) ? $columnConfiguration['internal_type'] : null;
309  $columnMap->setInternalType(\TYPO3\CMS\Core\DataHandling\TableColumnSubType::cast($tableColumnSubType));
310 
311  return $columnMap;
312  }
313 
323  protected function setRelations(ColumnMap $columnMap, $columnConfiguration, $propertyMetaData)
324  {
325  if (isset($columnConfiguration)) {
326  if (isset($columnConfiguration['MM'])) {
327  $columnMap = $this->setManyToManyRelation($columnMap, $columnConfiguration);
328  } elseif (isset($propertyMetaData['elementType'])) {
329  $columnMap = $this->setOneToManyRelation($columnMap, $columnConfiguration);
330  } elseif (isset($propertyMetaData['type']) && strpbrk($propertyMetaData['type'], '_\\') !== false) {
331  $columnMap = $this->setOneToOneRelation($columnMap, $columnConfiguration);
332  } elseif (isset($columnConfiguration['type']) && $columnConfiguration['type'] === 'select' && isset($columnConfiguration['maxitems']) && $columnConfiguration['maxitems'] > 1) {
334  } else {
336  }
337  } else {
339  }
340  return $columnMap;
341  }
342 
350  protected function setFieldEvaluations(ColumnMap $columnMap, array $columnConfiguration = null)
351  {
352  if (!empty($columnConfiguration['eval'])) {
353  $fieldEvaluations = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $columnConfiguration['eval'], true);
354  $dateTimeEvaluations = array('date', 'datetime');
355 
356  if (!empty(array_intersect($dateTimeEvaluations, $fieldEvaluations)) && !empty($columnConfiguration['dbType'])) {
357  $columnMap->setDateTimeStorageFormat($columnConfiguration['dbType']);
358  }
359  }
360 
361  return $columnMap;
362  }
363 
372  protected function setOneToOneRelation(ColumnMap $columnMap, $columnConfiguration)
373  {
375  $columnMap->setChildTableName($columnConfiguration['foreign_table']);
376  $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
377  $columnMap->setChildSortByFieldName($columnConfiguration['foreign_sortby']);
378  $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
379  $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
380  if (is_array($columnConfiguration['foreign_match_fields'])) {
381  $columnMap->setRelationTableMatchFields($columnConfiguration['foreign_match_fields']);
382  }
383  return $columnMap;
384  }
385 
394  protected function setOneToManyRelation(ColumnMap $columnMap, $columnConfiguration)
395  {
397  $columnMap->setChildTableName($columnConfiguration['foreign_table']);
398  $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
399  $columnMap->setChildSortByFieldName($columnConfiguration['foreign_sortby']);
400  $columnMap->setParentKeyFieldName($columnConfiguration['foreign_field']);
401  $columnMap->setParentTableFieldName($columnConfiguration['foreign_table_field']);
402  if (is_array($columnConfiguration['foreign_match_fields'])) {
403  $columnMap->setRelationTableMatchFields($columnConfiguration['foreign_match_fields']);
404  }
405  return $columnMap;
406  }
407 
417  protected function setManyToManyRelation(ColumnMap $columnMap, $columnConfiguration)
418  {
419  if (isset($columnConfiguration['MM'])) {
421  $columnMap->setChildTableName($columnConfiguration['foreign_table']);
422  $columnMap->setChildTableWhereStatement($columnConfiguration['foreign_table_where']);
423  $columnMap->setRelationTableName($columnConfiguration['MM']);
424  if (is_array($columnConfiguration['MM_match_fields'])) {
425  $columnMap->setRelationTableMatchFields($columnConfiguration['MM_match_fields']);
426  }
427  if (is_array($columnConfiguration['MM_insert_fields'])) {
428  $columnMap->setRelationTableInsertFields($columnConfiguration['MM_insert_fields']);
429  }
430  $columnMap->setRelationTableWhereStatement($columnConfiguration['MM_table_where']);
431  if (!empty($columnConfiguration['MM_opposite_field'])) {
432  $columnMap->setParentKeyFieldName('uid_foreign');
433  $columnMap->setChildKeyFieldName('uid_local');
434  $columnMap->setChildSortByFieldName('sorting_foreign');
435  } else {
436  $columnMap->setParentKeyFieldName('uid_local');
437  $columnMap->setChildKeyFieldName('uid_foreign');
438  $columnMap->setChildSortByFieldName('sorting');
439  }
440  } else {
441  throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedRelationException('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);
442  }
443  if ($this->getControlSection($columnMap->getRelationTableName()) !== null) {
444  $columnMap->setRelationTablePageIdColumnName('pid');
445  }
446  return $columnMap;
447  }
448 
457  protected function createColumnMap($columnName, $propertyName)
458  {
459  return $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::class, $columnName, $propertyName);
460  }
461 }