2 /***************************************************************
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
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.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
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.
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
26 * A data map to map a single table configured in $TCA on a domain object.
29 * @subpackage Persistence\Mapper
32 class Tx_Extbase_Persistence_Mapper_DataMap
{
34 * The domain class name
41 * The table name corresponding to the domain class configured in $TCA
48 * An array of column maps configured in $TCA
52 protected $columnMaps;
55 * Constructs this DataMap
57 * @param string $className The class name. This determines the table to fetch the configuration for
59 // TODO Refactor to factory pattern (DataMapFactory) and value object (DataMap)
60 public function __construct($className, $tableName = '', array $mapping = array()) {
61 $this->setClassName($className);
62 if (empty($tableName)) {
63 $this->setTableName(strtolower($className));
65 $this->setTableName($tableName);
67 $this->initialize($mapping);
71 * Sets the name of the class the colum map represents
75 public function setClassName($className) {
76 $this->className
= $className;
80 * Returns the name of the class the column map represents
82 * @return string The class name
84 public function getClassName() {
85 return $this->className
;
89 * Sets the name of the table the colum map represents
93 public function setTableName($tableName) {
94 $this->tableName
= $tableName;
98 * Returns the name of the table the column map represents
100 * @return string The table name
102 public function getTableName() {
103 return $this->tableName
;
107 * Initializes the data map by adding column maps for all the configured columns in the $TCA.
108 * It also resolves the type of values the column is holding and the typo of relation the column
113 protected function initialize(array $mapping) {
114 t3lib_div
::loadTCA($this->getTableName());
115 $columns = $GLOBALS['TCA'][$this->getTableName()]['columns'];
116 $this->addCommonColumns();
117 if (is_array($columns)) {
118 foreach ($columns as $columnName => $columnConfiguration) {
119 if (!empty($mapping[$columnName]['mapOnProperty'])) {
120 $propertyName = $mapping[$columnName]['mapOnProperty'];
122 $propertyName = Tx_Extbase_Utility_Extension
::convertUnderscoredToLowerCamelCase($columnName);
124 if (isset($mapping[$columnName]['foreignClass']) && !isset($columnConfiguration['config']['foreign_class'])) {
125 $columnConfiguration['config']['foreign_class'] = $mapping[$columnName]['foreignClass'];
127 $columnMap = new Tx_Extbase_Persistence_Mapper_ColumnMap($columnName, $propertyName);
128 $this->setPropertyType($columnMap, $columnConfiguration);
129 // TODO Check support for IRRE
130 $this->setRelations($columnMap, $columnConfiguration);
131 $this->addColumnMap($columnMap);
137 * Adds available common columns (e.g. tstamp or crdate) to the data map. It takes the configured column names
142 protected function addCommonColumns() {
143 // TODO Decide whether we should add pid and uid columns by default
144 $this->addColumn('uid', NULL, Tx_Extbase_Persistence_PropertyType
::LONG
);
145 if ($this->hasPidColumn()) {
146 $this->addColumn('pid', NULL, Tx_Extbase_Persistence_PropertyType
::LONG
);
148 if ($this->hasTimestampColumn()) {
149 $this->addColumn($this->getTimestampColumnName(), NULL, Tx_Extbase_Persistence_PropertyType
::DATE
);
151 if ($this->hasCreationDateColumn()) {
152 $this->addColumn($this->getCreationDateColumnName(), NULL, Tx_Extbase_Persistence_PropertyType
::DATE
);
154 if ($this->hasCreatorUidColumn()) {
155 $this->addColumn($this->getCreatorUidColumnName(), NULL, Tx_Extbase_Persistence_PropertyType
::LONG
);
157 if ($this->hasDeletedColumn()) {
158 $this->addColumn($this->getDeletedColumnName(), NULL, Tx_Extbase_Persistence_PropertyType
::BOOLEAN
);
160 if ($this->hasHiddenColumn()) {
161 $this->addColumn($this->getHiddenColumnName(), NULL, Tx_Extbase_Persistence_PropertyType
::BOOLEAN
);
166 * This method tries to determine the type of value the column hold by inspectiong the $TCA column configuration
169 * @param string $columnMap The column map
170 * @param string $columnConfiguration The column configuration from $TCA
173 protected function setPropertyType(Tx_Extbase_Persistence_Mapper_ColumnMap
&$columnMap, $columnConfiguration) {
174 $evalConfiguration = t3lib_div
::trimExplode(',', $columnConfiguration['config']['eval']);
175 if (in_array('date', $evalConfiguration) ||
in_array('datetime', $evalConfiguration)) {
176 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::DATE
);
177 } elseif ($columnConfiguration['config']['type'] === 'check' && empty($columnConfiguration['config']['items'])) {
178 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::BOOLEAN
);
179 } elseif (in_array('int', $evalConfiguration)) {
180 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::LONG
);
181 } elseif (in_array('double2', $evalConfiguration)) {
182 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::DOUBLE);
184 if (isset($columnConfiguration['config']['foreign_table'])) {
185 if (isset($columnConfiguration['config']['loadingStrategy'])) {
186 $columnMap->setLoadingStrategy($columnConfiguration['config']['loadingStrategy']);
188 $columnMap->setLoadingStrategy(Tx_Extbase_Persistence_Mapper_ColumnMap
::STRATEGY_EAGER
);
190 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::REFERENCE
);
192 $columnMap->setPropertyType(Tx_Extbase_Persistence_PropertyType
::STRING);
198 * This method tries to determine the type of type of relation to other tables and sets it based on
199 * the $TCA column configuration
201 * @param string $columnMap The column map
202 * @param string $columnConfiguration The column configuration from $TCA
205 protected function setRelations(Tx_Extbase_Persistence_Mapper_ColumnMap
&$columnMap, $columnConfiguration) {
206 if (isset($columnConfiguration['config']['foreign_table']) && !isset($columnConfiguration['config']['MM'])) {
207 if ($columnConfiguration['config']['maxitems'] == 1) {
208 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_ONE
);
210 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_MANY
);
212 $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
213 $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
214 $columnMap->setChildTableWhereStatement($columnConfiguration['config']['foreign_table_where']);
215 $columnMap->setChildSortbyFieldName($columnConfiguration['config']['foreign_sortby']);
216 $columnMap->setDeleteChildObjectsState($columnConfiguration['config']['deleteRelationsWithParent']);
217 $columnMap->setParentKeyFieldName($columnConfiguration['config']['foreign_field']);
218 $columnMap->setParentTableFieldName($columnConfiguration['config']['foreign_table_field']);
219 } elseif (array_key_exists('MM', $columnConfiguration['config'])) {
220 // TODO support for MM_insert_fields and MM_match_fields
221 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_AND_BELONGS_TO_MANY
);
222 $columnMap->setChildClassName($columnConfiguration['config']['foreign_class']);
223 $columnMap->setChildTableName($columnConfiguration['config']['foreign_table']);
224 $columnMap->setRelationTableName($columnConfiguration['config']['MM']);
225 if (is_array($columnConfiguration['config']['MM_match_fields'])) {
226 $columnMap->setRelationTableMatchFields($columnConfiguration['config']['MM_match_fields']);
228 $columnMap->setRelationTableWhereStatement($columnConfiguration['config']['MM_table_where']);
229 // TODO We currently do not support multi table relationships
230 if ($columnConfiguration['config']['MM_opposite_field']) {
231 $columnMap->setParentKeyFieldName('uid_foreign');
232 $columnMap->setChildKeyFieldName('uid_local');
233 $columnMap->setChildSortByFieldName('sorting_foreign');
235 $columnMap->setParentKeyFieldName('uid_local');
236 $columnMap->setChildKeyFieldName('uid_foreign');
237 $columnMap->setChildSortByFieldName('sorting');
240 $columnMap->setTypeOfRelation(Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_NONE
);
245 * Sets the column maps.
247 * @param array $columnMaps The column maps stored in a flat array.
250 public function setColumnMaps(array $columnMaps) {
251 $this->columnMaps
= $columnMaps;
255 * Adds a given column map to the data map.
257 * @param Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap The column map
260 public function addColumnMap(Tx_Extbase_Persistence_Mapper_ColumnMap
$columnMap) {
261 $this->columnMaps
[$columnMap->getPropertyName()] = $columnMap;
265 * Builds a column map out of the given column name, type of value (optional), and type of
266 * relation (optional) and adds it to the data map.
268 * @param string $columnName The column name
269 * @param string $propertyName The property name
270 * @param string $propertyType The type of value (default: string)
271 * @param string $typeOfRelation The type of relation (default: none)
272 * @return Tx_Extbase_Persistence_Mapper_DataMap Returns itself for a fluent interface
274 public function addColumn($columnName, $propertyName = '', $propertyType = Tx_Extbase_Persistence_PropertyType
::STRING, $typeOfRelation = Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_NONE
) {
275 if (empty($propertyName)) {
276 $propertyName = Tx_Extbase_Utility_Extension
::convertUnderscoredToLowerCamelCase($columnName);
279 $columnMap = new Tx_Extbase_Persistence_Mapper_ColumnMap($columnName, $propertyName);
280 $columnMap->setPropertyType($propertyType);
281 $columnMap->setTypeOfRelation($typeOfRelation);
282 $this->addColumnMap($columnMap);
287 * Returns all column maps
289 * @return array The column maps
291 public function getColumnMaps() {
292 return $this->columnMaps
;
296 * Returns the column map corresponding to the given property name.
298 * @param string $propertyName
299 * @return Tx_Extbase_Persistence_Mapper_ColumnMap|NULL The column map or NULL if no corresponding column map was found.
301 public function getColumnMap($propertyName) {
302 return $this->columnMaps
[$propertyName];
306 * Returns TRUE if the property is persistable (configured in $TCA)
308 * @param string $propertyName The property name
309 * @return boolean TRUE if the property is persistable (configured in $TCA)
311 public function isPersistableProperty($propertyName) {
312 return isset($this->columnMaps
[$propertyName]);
316 * Check if versioning is enabled .
320 public function isVersionable() {
321 return ($GLOBALS['TCA'] [$this->tableName
] ['ctrl'] ['versioningWS'] === '1');
325 * Returns TRUE if the table has a pid column holding the id of the page the record is virtually stored on.
326 * Currently we don't support tables without a pid column.
328 * @return boolean The result
330 public function hasPidColumn() {
331 // TODO Should we implement a check for having a pid column?
336 * Returns the name of a column holding the timestamp the record was modified
338 * @return string The field name
340 public function getTimestampColumnName() {
341 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['tstamp'];
345 * Returns TRUE if the table has a column holding the timestamp the record was modified
347 * @return boolean The result
349 public function hasTimestampColumn() {
350 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['tstamp']);
354 * Returns the name of a column holding the creation date timestamp
356 * @return string The field name
358 public function getCreationDateColumnName() {
359 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['crdate'];
363 * Returns TRUE if the table has olumn holding the creation date timestamp
365 * @return boolean The result
367 public function hasCreationDateColumn() {
368 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['crdate']);
372 * Returns the name of a column holding the uid of the back-end user who created this record
374 * @return string The field name
376 public function getCreatorUidColumnName() {
377 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['cruser_id'];
381 * Returns TRUE if the table has a column holding the uid of the back-end user who created this record
383 * @return boolean The result
385 public function hasCreatorUidColumn() {
386 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['cruser_id']);
390 * Returns the name of a column indicating the 'deleted' state of the row
392 * @return string The field name
394 public function getDeletedColumnName() {
395 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['delete'];
399 * Returns TRUE if the table has a column indicating the 'deleted' state of the row
401 * @return boolean The result
403 public function hasDeletedColumn() {
404 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['delete']);
408 * Returns the name of a column indicating the 'hidden' state of the row
410 * @return string The field name
412 public function getHiddenColumnName() {
413 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['disabled'];
417 * Returns TRUE if the table has a column indicating the 'hidden' state of the row
419 * @return boolean The result
421 public function hasHiddenColumn() {
422 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['disabled']);
426 * Returns the name of a column holding the timestamp the record should not displayed before
428 * @return string The field name
430 public function getStartTimeColumnName() {
431 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['starttime'];
435 * Returns TRUE if the table has a column holding the timestamp the record should not displayed before
437 * @return boolean The result
439 public function hasStartTimeColumn() {
440 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['starttime']);
444 * Returns the name of a column holding the timestamp the record should not displayed afterwards
446 * @return string The field name
448 public function getEndTimeColumnName() {
449 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['endtime'];
453 * Returns TRUE if the table has a column holding the timestamp the record should not displayed afterwards
455 * @return boolean The result
457 public function hasEndTimeColumn() {
458 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['endtime']);
462 * Returns the name of a column holding the uid of the front-end user group which is allowed to edit this record
464 * @return string The field name
466 public function getFrontEndUserGroupColumnName() {
467 return $GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['fe_group'];
471 * Returns TRUE if the table has a column holding the uid of the front-end user group which is allowed to edit this record
473 * @return boolean The result
475 public function hasFrontEndUserGroupColumn() {
476 return !empty($GLOBALS['TCA'][$this->getTableName()]['ctrl']['enablecolumns']['fe_group']);
480 * Converts a field name to the property name. It respects property name aliases defined in $TCA.
482 * @param string $fieldName The field name
483 * @return string $propertyName The property name
485 public function convertFieldNameToPropertyName($fieldName) {
486 $propertyName = $fieldName;
487 return $propertyName; // TODO Implement aliases for field names (see also convertPropertyNameToFieldName())
491 * Converts a preoperty name to the field name. It respects property name aliases defined in $TCA.
493 * @param string $fieldName The field name
494 * @return string $propertyName The property name
496 public function convertPropertyNameToFieldName($propertyName) {
497 $fieldName = $propertyName;
502 * Converts the given string into the given type
504 * @param integer $type one of the constants defined in Tx_Extbase_Persistence_PropertyType
505 * @param string $string a string representing a value of the given type
507 * @return string|int|float|DateTime|boolean
509 public function convertFieldValueToPropertyValue($type, $string) {
511 case Tx_Extbase_Persistence_PropertyType
::LONG
:
512 return (int) $string;
513 case Tx_Extbase_Persistence_PropertyType
::DOUBLE:
514 case Tx_Extbase_Persistence_PropertyType
::DECIMAL
:
515 return (float) $string;
516 case Tx_Extbase_Persistence_PropertyType
::DATE
:
517 return new DateTime(strftime('%Y-%m-%d %H:%M:%S', $string)); // TODO Check for Time Zone issues
518 case Tx_Extbase_Persistence_PropertyType
::BOOLEAN
:
519 return (boolean
) $string;
526 * Converts a value from a property type to a database field type
528 * @param mixed $propertyValue The property value
529 * @return mixed The converted value
531 public function convertPropertyValueToFieldValue($propertyValue) {
532 if (is_bool($propertyValue)) {
533 $convertedValue = $propertyValue ?
1 : 0;
534 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractDomainObject
) {
535 $convertedValue = $propertyValue->getUid();
536 } elseif (is_a($propertyValue, 'DateTime')) {
537 $convertedValue = $propertyValue->format('U');
538 } elseif (is_int($propertyValue)) {
539 $convertedValue = $propertyValue;
541 $convertedValue = $propertyValue;
543 return $convertedValue;