From 6c7b876c7a7f20b525768a0346d741aff3dffea4 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Wed, 28 Aug 2024 13:43:50 +0200
Subject: [PATCH] [TASK] Streamline extbase data / column mapping
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The extbase data and column mapping is now properly
using the TCA schema API. While streamlining the
code some divergences regarding relation handling
have been observed (e.g. use of "maxitems" and
"renderType" options). Those will be tackled
separately, since streamlining this might be
breaking.

Resolves: #104808
Releases: main
Change-Id: I5aedf761372a4c14da76619a85f931a411b1dcb9
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/85811
Tested-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
---
 .../DataHandling/RecordFieldTransformer.php   |   4 +-
 .../Schema/Capability/FieldCapability.php     |   5 +
 .../SystemInternalFieldCapability.php         |   5 +
 .../Schema/Field/DateTimeFieldType.php        |   4 +-
 .../core/Classes/Schema/RelationshipType.php  |   7 +-
 .../Generic/Mapper/ColumnMapFactory.php       | 177 +++++++-----------
 .../Generic/Mapper/DataMapFactory.php         |  82 ++++----
 .../Classes/Domain/Model/Example.php          |  33 ++++
 ...tx_testdatamapper_domain_model_example.php |  18 ++
 .../Generic/Mapper/ColumnMapFactoryTest.php   |  33 ++--
 .../Generic/Mapper/DataMapperTest.php         |  12 +-
 .../Generic/Mapper/DataMapperTest.php         |  43 ++---
 12 files changed, 210 insertions(+), 213 deletions(-)

diff --git a/typo3/sysext/core/Classes/DataHandling/RecordFieldTransformer.php b/typo3/sysext/core/Classes/DataHandling/RecordFieldTransformer.php
index 607e595acb3c..a3d4e05cce84 100644
--- a/typo3/sysext/core/Classes/DataHandling/RecordFieldTransformer.php
+++ b/typo3/sysext/core/Classes/DataHandling/RecordFieldTransformer.php
@@ -88,7 +88,7 @@ readonly class RecordFieldTransformer
 
         // type=file needs to be handled before RelationalFieldTypeInterface
         if ($fieldInformation instanceof FileFieldType) {
-            if ($fieldInformation->getRelationshipType()->isToOne()) {
+            if ($fieldInformation->getRelationshipType()->hasOne()) {
                 return new RecordPropertyClosure(
                     function () use ($rawRecord, $fieldInformation, $context): ?FileReference {
                         $fileReference = $this->relationResolver->resolveFileReferences($rawRecord, $fieldInformation, $context)[0] ?? null;
@@ -108,7 +108,7 @@ readonly class RecordFieldTransformer
             /** @var RecordFactory $recordFactory */
             // @todo This method is called by RecordFactory -> instantiating the factory here again shows, that those classes should actually be somehow belong together.
             $recordFactory = GeneralUtility::makeInstance(RecordFactory::class);
-            if ($fieldInformation->getRelationshipType()->isToOne()) {
+            if ($fieldInformation->getRelationshipType()->hasOne()) {
                 return new RecordPropertyClosure(
                     function () use ($rawRecord, $fieldInformation, $context, $recordFactory): ?RecordInterface {
                         $recordData = $this->relationResolver->resolve($rawRecord, $fieldInformation, $context)[0] ?? null;
diff --git a/typo3/sysext/core/Classes/Schema/Capability/FieldCapability.php b/typo3/sysext/core/Classes/Schema/Capability/FieldCapability.php
index 21c8e355724e..61bdfa363421 100644
--- a/typo3/sysext/core/Classes/Schema/Capability/FieldCapability.php
+++ b/typo3/sysext/core/Classes/Schema/Capability/FieldCapability.php
@@ -44,4 +44,9 @@ final readonly class FieldCapability implements SchemaCapabilityInterface
     {
         return $this->field;
     }
+
+    public function __toString(): string
+    {
+        return $this->getFieldName();
+    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Capability/SystemInternalFieldCapability.php b/typo3/sysext/core/Classes/Schema/Capability/SystemInternalFieldCapability.php
index 6feada11c52c..fdfca48d3ae1 100644
--- a/typo3/sysext/core/Classes/Schema/Capability/SystemInternalFieldCapability.php
+++ b/typo3/sysext/core/Classes/Schema/Capability/SystemInternalFieldCapability.php
@@ -36,4 +36,9 @@ final readonly class SystemInternalFieldCapability implements SchemaCapabilityIn
     {
         return $this->fieldName;
     }
+
+    public function __toString(): string
+    {
+        return $this->getFieldName();
+    }
 }
diff --git a/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php b/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
index e20639f638f5..18e1824b83c1 100644
--- a/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
+++ b/typo3/sysext/core/Classes/Schema/Field/DateTimeFieldType.php
@@ -17,6 +17,8 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Schema\Field;
 
+use TYPO3\CMS\Core\Database\Query\QueryHelper;
+
 /**
  * @internal This is an experimental implementation and might change until TYPO3 v13 LTS
  */
@@ -34,7 +36,7 @@ final readonly class DateTimeFieldType extends AbstractFieldType implements Fiel
 
     public function getPersistenceType(): ?string
     {
-        return $this->configuration['dbtype'] ?? null;
+        return in_array($this->configuration['dbType'] ?? null, QueryHelper::getDateTimeTypes(), true) ? $this->configuration['dbType'] : null;
     }
 
     public function isNullable(): bool
diff --git a/typo3/sysext/core/Classes/Schema/RelationshipType.php b/typo3/sysext/core/Classes/Schema/RelationshipType.php
index 541112fe4273..7bc2ce621cc9 100644
--- a/typo3/sysext/core/Classes/Schema/RelationshipType.php
+++ b/typo3/sysext/core/Classes/Schema/RelationshipType.php
@@ -72,11 +72,16 @@ enum RelationshipType: string
         return self::Undefined;
     }
 
-    public function isToOne(): bool
+    public function hasOne(): bool
     {
         return in_array($this, [self::OneToOne, self::ManyToOne], true);
     }
 
+    public function hasMany(): bool
+    {
+        return in_array($this, [self::ManyToMany, self::OneToMany, self::List], true);
+    }
+
     public function isSingularRelationship(): bool
     {
         return in_array($this, [self::OneToOne, self::ManyToOne, self::OneToMany, self::List], true);
diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/ColumnMapFactory.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/ColumnMapFactory.php
index 9814a5feef75..f435e084234b 100644
--- a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/ColumnMapFactory.php
+++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/ColumnMapFactory.php
@@ -17,10 +17,13 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
 
-use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\DataHandling\TableColumnType;
+use TYPO3\CMS\Core\Schema\Field\DateTimeFieldType;
+use TYPO3\CMS\Core\Schema\Field\FieldTypeInterface;
+use TYPO3\CMS\Core\Schema\Field\FolderFieldType;
+use TYPO3\CMS\Core\Schema\Field\RelationalFieldTypeInterface;
+use TYPO3\CMS\Core\Schema\RelationshipType;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedRelationException;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap\Relation;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoPropertyTypesException;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException;
@@ -29,15 +32,15 @@ use TYPO3\CMS\Extbase\Reflection\ReflectionService;
 /**
  * @internal only to be used within Extbase, not part of TYPO3 Core API.
  */
-class ColumnMapFactory
+readonly class ColumnMapFactory
 {
     public function __construct(
-        private readonly ReflectionService $reflectionService
+        private ReflectionService $reflectionService
     ) {}
 
-    public function create(string $columnName, array $columnDefinition, string $propertyName, string $className): ColumnMap
+    public function create(FieldTypeInterface $field, string $propertyName, string $className): ColumnMap
     {
-        $columnMap = GeneralUtility::makeInstance(ColumnMap::class, $columnName);
+        $columnMap = GeneralUtility::makeInstance(ColumnMap::class, $field->getName());
         try {
             $property = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
             $nonProxyPropertyTypes = $property->getFilteredTypes([$property, 'filterLazyLoadingProxyAndLazyObjectStorage']);
@@ -59,105 +62,70 @@ class ColumnMapFactory
         } catch (NoSuchPropertyException|NoPropertyTypesException $e) {
             [$type, $elementType] = [null, null];
         }
-        $columnMap = $this->setType($columnMap, $columnDefinition['config']);
-        $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $type, $elementType);
-        return $this->setDateTimeStorageFormat($columnMap, $columnDefinition['config']);
-    }
-
-    /**
-     * Set the table column type
-     */
-    protected function setType(ColumnMap $columnMap, array $columnConfiguration): ColumnMap
-    {
-        // todo: this method should only be called with proper arguments which means that the TCA integrity check should
-        // todo: take place outside this method.
-
-        $tableColumnType = $columnConfiguration['type'] ?? null;
-        $columnMap->setType(TableColumnType::tryFrom($tableColumnType) ?? TableColumnType::INPUT);
+        // @todo Why type "input" - shouldn't we better throw an exception here?
+        $columnMap->setType(TableColumnType::tryFrom($field->getType()) ?? TableColumnType::INPUT);
+        if ($field instanceof DateTimeFieldType) {
+            $columnMap->setDateTimeStorageFormat($field->getPersistenceType());
+        }
+        $columnMap = $this->setRelations($columnMap, $field, $type, $elementType);
         return $columnMap;
     }
 
     /**
      * This method tries to determine the type of relation to other tables and sets it based on
      * the $TCA column configuration
-     *
-     * @param ColumnMap $columnMap The column map
-     * @param array|null $columnConfiguration The column configuration from $TCA
      */
-    protected function setRelations(ColumnMap $columnMap, ?array $columnConfiguration, ?string $type, ?string $elementType): ColumnMap
+    protected function setRelations(ColumnMap $columnMap, FieldTypeInterface $field, ?string $type, ?string $elementType): ColumnMap
     {
-        if (!isset($columnConfiguration)) {
+        $columnConfiguration = $field->getConfiguration();
+        if ($field instanceof FolderFieldType
+            && !in_array((string)($columnConfiguration['relationship'] ?? ''), ['oneToOne', 'manyToOne'], true)
+            && (!isset($columnConfiguration['maxitems']) || $columnConfiguration['maxitems'] > 1)
+        ) {
+            $columnMap->setTypeOfRelation(Relation::HAS_MANY);
+            return $columnMap;
+        }
+
+        if ($field instanceof RelationalFieldTypeInterface === false) {
             return $columnMap;
         }
 
-        if (isset($columnConfiguration['MM'])) {
-            return $this->setManyToManyRelation($columnMap, $columnConfiguration);
+        if ($field->getRelationshipType() === RelationshipType::ManyToMany) {
+            return $this->setManyToManyRelation($columnMap, $field);
         }
 
         if ($elementType !== null) {
-            return $this->setOneToManyRelation($columnMap, $columnConfiguration);
+            return $this->setOneToManyRelation($columnMap, $field);
         }
 
         if ($type !== null && strpbrk($type, '_\\') !== false) {
             // @todo: check the strpbrk function call. Seems to be a check for Tx_Foo_Bar style class names
-            return $this->setOneToOneRelation($columnMap, $columnConfiguration);
+            return $this->setOneToOneRelation($columnMap, $field);
         }
 
-        if (
-            isset($columnConfiguration['type'], $columnConfiguration['renderType'])
-            && $columnConfiguration['type'] === 'select'
+        $columnConfiguration = $field->getConfiguration();
+        // @todo we should get rid of the "maxitems" and "renderType" cases here and rely purely on
+        //       the evaluated relationship type -> to be consistent with all non extbase components.
+        if ($field->getRelationshipType()->hasMany()
             && (
-                $columnConfiguration['renderType'] !== 'selectSingle'
-                || (isset($columnConfiguration['maxitems']) && $columnConfiguration['maxitems'] > 1)
+                !$field->isType(TableColumnType::GROUP, TableColumnType::SELECT)
+                || ($field->isType(TableColumnType::GROUP) && (!isset($columnConfiguration['maxitems']) || $columnConfiguration['maxitems'] > 1))
+                || ($field->isType(TableColumnType::SELECT) && (($columnConfiguration['renderType'] ?? '') !== 'selectSingle' || (int)($columnConfiguration['maxitems'] ?? 0) > 1))
             )
         ) {
             $columnMap->setTypeOfRelation(Relation::HAS_MANY);
             return $columnMap;
         }
 
-        if (
-            isset($columnConfiguration['type']) && ($columnConfiguration['type'] === 'group' || $columnConfiguration['type'] === 'folder')
-            && (!isset($columnConfiguration['maxitems']) || $columnConfiguration['maxitems'] > 1)
-        ) {
-            $columnMap->setTypeOfRelation(Relation::HAS_MANY);
-            return $columnMap;
-        }
-
-        return $columnMap;
-    }
-
-    /**
-     * Sets datetime storage format based on $TCA column configuration.
-     *
-     * @param ColumnMap $columnMap The column map
-     * @param array|null $columnConfiguration The column configuration from $TCA
-     */
-    protected function setDateTimeStorageFormat(ColumnMap $columnMap, ?array $columnConfiguration = null): ColumnMap
-    {
-        // todo: this method should only be called with proper arguments which means that the TCA integrity check should
-        // todo: take place outside this method.
-
-        if ($columnMap->getType() === TableColumnType::DATETIME
-            && in_array($columnConfiguration['dbType'] ?? '', QueryHelper::getDateTimeTypes(), true)
-        ) {
-            $columnMap->setDateTimeStorageFormat($columnConfiguration['dbType']);
-        }
-
         return $columnMap;
     }
 
     /**
-     * This method sets the configuration for a 1:1 relation based on
-     * the $TCA column configuration
-     *
-     * @param ColumnMap $columnMap The column map
-     * @param array|null $columnConfiguration The column configuration from $TCA
+     * This method sets the configuration for a 1:1 relation based on the $TCA column configuration
      */
-    protected function setOneToOneRelation(ColumnMap $columnMap, ?array $columnConfiguration = null): ColumnMap
+    protected function setOneToOneRelation(ColumnMap $columnMap, FieldTypeInterface $field): ColumnMap
     {
-        // todo: this method should only be called with proper arguments which means that the TCA integrity check should
-        // todo: take place outside this method.
-
+        $columnConfiguration = $field->getConfiguration();
         $columnMap->setTypeOfRelation(Relation::HAS_ONE);
         // check if foreign_table is set, which usually won't be the case for type "group" fields
         if (!empty($columnConfiguration['foreign_table'])) {
@@ -177,16 +145,11 @@ class ColumnMapFactory
      * This method sets the configuration for a 1:n relation based on
      * the $TCA column configuration
      *
-     * @param ColumnMap $columnMap The column map
-     * @param array|null $columnConfiguration The column configuration from $TCA
-     *
      * @internal
      */
-    public function setOneToManyRelation(ColumnMap $columnMap, ?array $columnConfiguration = null): ColumnMap
+    public function setOneToManyRelation(ColumnMap $columnMap, FieldTypeInterface $field): ColumnMap
     {
-        // todo: this method should only be called with proper arguments which means that the TCA integrity check should
-        // todo: take place outside this method.
-
+        $columnConfiguration = $field->getConfiguration();
         $columnMap->setTypeOfRelation(Relation::HAS_MANY);
         // check if foreign_table is set, which usually won't be the case for type "group" fields
         if (!empty($columnConfiguration['foreign_table'])) {
@@ -204,44 +167,30 @@ class ColumnMapFactory
     }
 
     /**
-     * This method sets the configuration for a m:n relation based on
-     * the $TCA column configuration
-     *
-     * @param ColumnMap $columnMap The column map
-     * @param array|null $columnConfiguration The column configuration from $TCA
-     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedRelationException
+     * This method sets the configuration for a m:n relation based on the $TCA column configuration
      */
-    protected function setManyToManyRelation(ColumnMap $columnMap, ?array $columnConfiguration = null): ColumnMap
+    protected function setManyToManyRelation(ColumnMap $columnMap, FieldTypeInterface $field): ColumnMap
     {
-        // todo: this method should only be called with proper arguments which means that the TCA integrity check should
-        // todo: take place outside this method.
-
-        if (isset($columnConfiguration['MM'])) {
-            $columnMap->setTypeOfRelation(Relation::HAS_AND_BELONGS_TO_MANY);
-            // check if foreign_table is set, which usually won't be the case for type "group" fields
-            if (!empty($columnConfiguration['foreign_table'])) {
-                $columnMap->setChildTableName($columnConfiguration['foreign_table']);
-            }
-            // todo: don't update column map if value(s) isn't/aren't set.
-            $columnMap->setRelationTableName($columnConfiguration['MM']);
-            if (isset($columnConfiguration['MM_match_fields']) && is_array($columnConfiguration['MM_match_fields'])) {
-                $columnMap->setRelationTableMatchFields($columnConfiguration['MM_match_fields']);
-            }
-            // todo: don't update column map if value(s) isn't/aren't set.
-            if (!empty($columnConfiguration['MM_opposite_field'])) {
-                $columnMap->setParentKeyFieldName('uid_foreign');
-                $columnMap->setChildKeyFieldName('uid_local');
-                $columnMap->setChildSortByFieldName('sorting_foreign');
-            } else {
-                $columnMap->setParentKeyFieldName('uid_local');
-                $columnMap->setChildKeyFieldName('uid_foreign');
-                $columnMap->setChildSortByFieldName('sorting');
-            }
+        $columnConfiguration = $field->getConfiguration();
+        $columnMap->setTypeOfRelation(Relation::HAS_AND_BELONGS_TO_MANY);
+        // check if foreign_table is set, which usually won't be the case for type "group" fields
+        if ($columnConfiguration['foreign_table'] ?? false) {
+            $columnMap->setChildTableName($columnConfiguration['foreign_table']);
+        }
+        // todo: don't update column map if value(s) isn't/aren't set.
+        $columnMap->setRelationTableName($columnConfiguration['MM']);
+        if (isset($columnConfiguration['MM_match_fields']) && is_array($columnConfiguration['MM_match_fields'])) {
+            $columnMap->setRelationTableMatchFields($columnConfiguration['MM_match_fields']);
+        }
+        // todo: don't update column map if value(s) isn't/aren't set.
+        if (!empty($columnConfiguration['MM_opposite_field'])) {
+            $columnMap->setParentKeyFieldName('uid_foreign');
+            $columnMap->setChildKeyFieldName('uid_local');
+            $columnMap->setChildSortByFieldName('sorting_foreign');
         } else {
-            // todo: this else part is actually superfluous because \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::setRelations
-            // todo: only calls this method if $columnConfiguration['MM'] is set.
-
-            throw new 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);
+            $columnMap->setParentKeyFieldName('uid_local');
+            $columnMap->setChildKeyFieldName('uid_foreign');
+            $columnMap->setChildSortByFieldName('sorting');
         }
         return $columnMap;
     }
diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php
index 69b858d85b3f..4578e017b5cd 100644
--- a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php
+++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php
@@ -19,6 +19,8 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
 
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
+use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
+use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
@@ -66,6 +68,8 @@ class DataMapFactory implements SingletonInterface
 
     private ColumnMapFactory $columnMapFactory;
 
+    private TcaSchemaFactory $tcaSchemaFactory;
+
     protected string $baseCacheIdentifier;
 
     public function __construct(
@@ -74,6 +78,7 @@ class DataMapFactory implements SingletonInterface
         CacheManager $cacheManager,
         ClassesConfiguration $classesConfiguration,
         ColumnMapFactory $columnMapFactory,
+        TcaSchemaFactory $tcaSchemaFactory,
         string $baseCacheIdentifier
     ) {
         $this->reflectionService = $reflectionService;
@@ -82,6 +87,7 @@ class DataMapFactory implements SingletonInterface
         $this->dataMapCache = $this->cacheManager->getCache('extbase');
         $this->classesConfiguration = $classesConfiguration;
         $this->columnMapFactory = $columnMapFactory;
+        $this->tcaSchemaFactory = $tcaSchemaFactory;
         $this->baseCacheIdentifier = $baseCacheIdentifier;
     }
 
@@ -147,13 +153,12 @@ class DataMapFactory implements SingletonInterface
         $dataMap = GeneralUtility::makeInstance(DataMap::class, $className, $tableName, $recordType, $subclasses);
         $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName);
 
-        foreach ($this->getColumnsDefinition($tableName) as $columnName => $columnDefinition) {
+        foreach ($this->tcaSchemaFactory->get($tableName)->getFields() as $columnName => $columnDefinition) {
             $propertyName = $fieldNameToPropertyNameMapping[$columnName]
                 ?? GeneralUtility::underscoredToLowerCamelCase($columnName);
             $dataMap->addColumnMap(
                 $propertyName,
                 $this->columnMapFactory->create(
-                    $columnName,
                     $columnDefinition,
                     $propertyName,
                     $className
@@ -183,62 +188,55 @@ class DataMapFactory implements SingletonInterface
         return $tableName;
     }
 
-    /**
-     * Returns the TCA columns array of the specified table
-     *
-     * @param string $tableName An optional table name to fetch the columns definition from
-     * @return array The TCA columns definition
-     */
-    protected function getColumnsDefinition(string $tableName): array
-    {
-        return is_array($GLOBALS['TCA'][$tableName]['columns'] ?? null) ? $GLOBALS['TCA'][$tableName]['columns'] : [];
-    }
-
     protected function addMetaDataColumnNames(DataMap $dataMap, string $tableName): DataMap
     {
-        $controlSection = $GLOBALS['TCA'][$tableName]['ctrl'] ?? null;
-        if ($controlSection === null) {
+        if (!$this->tcaSchemaFactory->has($tableName)) {
             return $dataMap;
         }
+        $schema = $this->tcaSchemaFactory->get($tableName);
         $dataMap->setPageIdColumnName('pid');
-        if (isset($controlSection['tstamp'])) {
-            $dataMap->setModificationDateColumnName($controlSection['tstamp']);
+        if ($schema->hasCapability(TcaSchemaCapability::UpdatedAt)) {
+            $dataMap->setModificationDateColumnName((string)$schema->getCapability(TcaSchemaCapability::UpdatedAt));
         }
-        if (isset($controlSection['crdate'])) {
-            $dataMap->setCreationDateColumnName($controlSection['crdate']);
+        if ($schema->hasCapability(TcaSchemaCapability::CreatedAt)) {
+            $dataMap->setCreationDateColumnName((string)$schema->getCapability(TcaSchemaCapability::CreatedAt));
         }
-        if (isset($controlSection['delete'])) {
-            $dataMap->setDeletedFlagColumnName($controlSection['delete']);
+        if ($schema->hasCapability(TcaSchemaCapability::SoftDelete)) {
+            $dataMap->setDeletedFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::SoftDelete));
         }
-        if (isset($controlSection['languageField'])) {
-            $dataMap->setLanguageIdColumnName($controlSection['languageField']);
-        }
-        if (isset($controlSection['transOrigPointerField'])) {
-            $dataMap->setTranslationOriginColumnName($controlSection['transOrigPointerField']);
+        if ($schema->hasCapability(TcaSchemaCapability::Language)) {
+            $languageCapability = $schema->getCapability(TcaSchemaCapability::Language);
+            $dataMap->setLanguageIdColumnName($languageCapability->getLanguageField()->getName());
+            $dataMap->setTranslationOriginColumnName($languageCapability->getTranslationOriginPointerField()->getName());
+            if ($languageCapability->hasDiffSourceField()) {
+                $dataMap->setTranslationOriginDiffSourceName($languageCapability->getDiffSourceField()->getName());
+            }
         }
-        if (isset($controlSection['transOrigDiffSourceField'])) {
-            $dataMap->setTranslationOriginDiffSourceName($controlSection['transOrigDiffSourceField']);
+        if ($schema->getSubSchemaDivisorField() !== null) {
+            $dataMap->setRecordTypeColumnName($schema->getSubSchemaDivisorField()->getName());
         }
-        if (isset($controlSection['type'])) {
-            $dataMap->setRecordTypeColumnName($controlSection['type']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionRootLevel)) {
+            // @todo Evaluate if this is correct. We currently have to use canExistOnPages() to keep previous
+            //       behaviour, which is (bool)$rootlevel, so treating "-1" and "1" as TURE, and only 0 als FALSE.
+            $dataMap->setRootLevel($schema->getCapability(TcaSchemaCapability::RestrictionRootLevel)->canExistOnPages());
         }
-        if (isset($controlSection['rootLevel'])) {
-            $dataMap->setRootLevel($controlSection['rootLevel']);
+        if (isset($schema->getRawConfiguration()['is_static'])) {
+            $dataMap->setIsStatic($schema->getRawConfiguration()['is_static']);
         }
-        if (isset($controlSection['is_static'])) {
-            $dataMap->setIsStatic($controlSection['is_static']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionDisabledField)) {
+            $dataMap->setDisabledFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::RestrictionDisabledField));
         }
-        if (isset($controlSection['enablecolumns']['disabled'])) {
-            $dataMap->setDisabledFlagColumnName($controlSection['enablecolumns']['disabled']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionStartTime)) {
+            $dataMap->setDisabledFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::RestrictionStartTime));
         }
-        if (isset($controlSection['enablecolumns']['starttime'])) {
-            $dataMap->setStartTimeColumnName($controlSection['enablecolumns']['starttime']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionEndTime)) {
+            $dataMap->setDisabledFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::RestrictionEndTime));
         }
-        if (isset($controlSection['enablecolumns']['endtime'])) {
-            $dataMap->setEndTimeColumnName($controlSection['enablecolumns']['endtime']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionUserGroup)) {
+            $dataMap->setDisabledFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::RestrictionUserGroup));
         }
-        if (isset($controlSection['enablecolumns']['fe_group'])) {
-            $dataMap->setFrontEndUserGroupColumnName($controlSection['enablecolumns']['fe_group']);
+        if ($schema->hasCapability(TcaSchemaCapability::RestrictionDisabledField)) {
+            $dataMap->setDisabledFlagColumnName((string)$schema->getCapability(TcaSchemaCapability::RestrictionDisabledField));
         }
         return $dataMap;
     }
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php
index 79bfec7b923a..2af05a5f2911 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Classes/Domain/Model/Example.php
@@ -29,6 +29,9 @@ class Example extends AbstractEntity
     protected ?\DateTime $uninitializedDateTimeProperty;
     protected \DateTime $uninitializedMandatoryDateTimeProperty;
     protected ?\DateTime $initializedDateTimeProperty = null;
+    protected ?\DateTime $initializedDateTimePropertyDate = null;
+    protected ?\DateTime $initializedDateTimePropertyDatetime = null;
+    protected ?\DateTime $initializedDateTimePropertyTime = null;
     protected ?CustomDateTime $customDateTime = null;
     public $unknownType;
     public Enum\StringBackedEnum $stringBackedEnum;
@@ -116,6 +119,36 @@ class Example extends AbstractEntity
         $this->initializedDateTimeProperty = $initializedDateTimeProperty;
     }
 
+    public function getInitializedDateTimePropertyDate(): ?\DateTime
+    {
+        return $this->initializedDateTimePropertyDate;
+    }
+
+    public function setInitializedDateTimePropertyDate(?\DateTime $initializedDateTimePropertyDate): void
+    {
+        $this->initializedDateTimePropertyDate = $initializedDateTimePropertyDate;
+    }
+
+    public function getInitializedDateTimePropertyDatetime(): ?\DateTime
+    {
+        return $this->initializedDateTimePropertyDatetime;
+    }
+
+    public function setInitializedDateTimePropertyDatetime(?\DateTime $initializedDateTimePropertyDatetime): void
+    {
+        $this->initializedDateTimePropertyDatetime = $initializedDateTimePropertyDatetime;
+    }
+
+    public function getInitializedDateTimePropertyTime(): ?\DateTime
+    {
+        return $this->initializedDateTimePropertyTime;
+    }
+
+    public function setInitializedDateTimePropertyTime(?\DateTime $initializedDateTimePropertyTime): void
+    {
+        $this->initializedDateTimePropertyTime = $initializedDateTimePropertyTime;
+    }
+
     public function getCustomDateTime(): ?CustomDateTime
     {
         return $this->customDateTime;
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php
index 68adf7331f07..86d5faa70035 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/test_data_mapper/Configuration/TCA/tx_testdatamapper_domain_model_example.php
@@ -43,6 +43,24 @@ return [
                 'type' => 'datetime',
             ],
         ],
+        'initialized_date_time_property_date' => [
+            'config' => [
+                'type' => 'datetime',
+                'dbType' => 'date',
+            ],
+        ],
+        'initialized_date_time_property_datetime' => [
+            'config' => [
+                'type' => 'datetime',
+                'dbType' => 'datetime',
+            ],
+        ],
+        'initialized_date_time_property_time' => [
+            'config' => [
+                'type' => 'datetime',
+                'dbType' => 'time',
+            ],
+        ],
         'custom_date_time' => [
             'config' => [
                 'type' => 'datetime',
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/ColumnMapFactoryTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/ColumnMapFactoryTest.php
index d849d6da1fa0..7083e0859a7c 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/ColumnMapFactoryTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/ColumnMapFactoryTest.php
@@ -20,6 +20,8 @@ namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence\Generic\Mapper;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\Attributes\Test;
 use TYPO3\CMS\Core\DataHandling\TableColumnType;
+use TYPO3\CMS\Core\Schema\FieldTypeFactory;
+use TYPO3\CMS\Core\Schema\RelationMap;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap\Relation;
@@ -32,11 +34,13 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
     protected bool $initializeDatabase = false;
 
     protected ColumnMapFactory $columnMapFactory;
+    protected FieldTypeFactory $fieldTypeFactory;
 
     protected function setUp(): void
     {
         parent::setUp();
         $this->columnMapFactory = $this->get(ColumnMapFactory::class);
+        $this->fieldTypeFactory = $this->get(FieldTypeFactory::class);
     }
 
     public static function createWithGroupTypeDataProvider(): \Generator
@@ -97,7 +101,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
     {
         self::assertEquals(
             $expectedColumnMap,
-            $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class)
+            $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class)
         );
     }
 
@@ -219,7 +223,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
     {
         self::assertEquals(
             $expectedColumnMap,
-            $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class)
+            $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class)
         );
     }
 
@@ -248,7 +252,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
     {
         self::assertEquals(
             $expectedColumnMap,
-            $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class)
+            $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class)
         );
     }
 
@@ -284,7 +288,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
     {
         self::assertEquals(
             $expectedColumnMap,
-            $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class)
+            $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class)
         );
     }
 
@@ -304,8 +308,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        $columnMap = $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class);
-
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
         self::assertSame(
             [
                 'fieldname' => 'foo_model',
@@ -330,8 +333,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        $columnMap = $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class);
-
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
         self::assertSame(
             [
                 'fieldname' => 'foo_model',
@@ -364,7 +366,8 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        self::assertEquals($expectedColumnMap, $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class));
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
+        self::assertEquals($expectedColumnMap, $columnMap);
     }
 
     #[Test]
@@ -390,7 +393,8 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        self::assertEquals($expectedColumnMap, $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class));
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
+        self::assertEquals($expectedColumnMap, $columnMap);
     }
 
     #[Test]
@@ -416,7 +420,8 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        self::assertEquals($expectedColumnMap, $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class));
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
+        self::assertEquals($expectedColumnMap, $columnMap);
     }
 
     public static function columnMapIsInitializedWithFieldEvaluationsForDateTimeFieldsDataProvider(): array
@@ -442,8 +447,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        $columnMap = $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class);
-
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
         self::assertSame($expectedValue, $columnMap->getDateTimeStorageFormat());
     }
 
@@ -490,8 +494,7 @@ final class ColumnMapFactoryTest extends FunctionalTestCase
             ],
         ];
 
-        $columnMap = $this->columnMapFactory->create($columnName, $columnConfiguration, $propertyName, ColumnMapFactoryEntityFixture::class);
-
+        $columnMap = $this->columnMapFactory->create($this->fieldTypeFactory->createFieldType($columnName, $columnConfiguration, 'virtual', new RelationMap()), $propertyName, ColumnMapFactoryEntityFixture::class);
         self::assertSame($expectedType, $columnMap->getType());
     }
 }
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
index d2c9b0477e78..a6ea93436daa 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Generic/Mapper/DataMapperTest.php
@@ -367,17 +367,17 @@ final class DataMapperTest extends FunctionalTestCase
     #[Test]
     public function mapDateTimeHandlesDifferentFieldEvaluations(string|int|null $value, ?string $storageFormat, ?string $expectedValue): void
     {
-        $GLOBALS['TCA']['tx_testdatamapper_domain_model_example']['columns']['initialized_date_time_property']['config']['dbType'] = $storageFormat;
         $rows = [
             [
                 'uid' => 123,
-                'initialized_date_time_property' => $value,
+                'initialized_date_time_property' . ($storageFormat !== null ? '_' . $storageFormat : '') => $value,
             ],
         ];
         $dataMapper = $this->get(DataMapper::class);
         $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
 
-        self::assertSame($expectedValue, $mappedObjectsArray[0]->getInitializedDateTimeProperty()?->format('c'));
+        $getter = 'getInitializedDateTimeProperty' . ($storageFormat !== null ? ucfirst($storageFormat) : '');
+        self::assertSame($expectedValue, $mappedObjectsArray[0]->{$getter}()?->format('c'));
 
         // Flush DataMapFactory cache on each run.
         $cacheManager = $this->get(CacheManager::class);
@@ -404,18 +404,18 @@ final class DataMapperTest extends FunctionalTestCase
         date_default_timezone_set('America/Chicago');
         $usedTimeZone = date_default_timezone_get();
 
-        $GLOBALS['TCA']['tx_testdatamapper_domain_model_example']['columns']['initialized_date_time_property']['config']['dbType'] = $storageFormat;
         $rows = [
             [
                 'uid' => 123,
-                'initialized_date_time_property' => $value,
+                'initialized_date_time_property' . ($storageFormat !== null ? '_' . $storageFormat : '') => $value,
             ],
         ];
         $dataMapper = $this->get(DataMapper::class);
         $mappedObjectsArray = $dataMapper->map(Example::class, $rows);
 
+        $getter = 'getInitializedDateTimeProperty' . ($storageFormat !== null ? ucfirst($storageFormat) : '');
         $expectedValue = $expectedValue !== null ? new \DateTime($expectedValue, new \DateTimeZone($usedTimeZone)) : $expectedValue;
-        self::assertEquals($expectedValue, $mappedObjectsArray[0]->getInitializedDateTimeProperty());
+        self::assertEquals($expectedValue, $mappedObjectsArray[0]->{$getter}());
 
         // Flush DataMapFactory cache on each run.
         $cacheManager = $this->get(CacheManager::class);
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
index 58e530646455..eeb59ec5529c 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
@@ -21,6 +21,8 @@ use Doctrine\Instantiator\InstantiatorInterface;
 use PHPUnit\Framework\Attributes\Test;
 use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Schema\Field\SelectRelationFieldType;
+use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
 use TYPO3\CMS\Extbase\Persistence\ClassesConfiguration;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
@@ -58,6 +60,7 @@ final class DataMapperTest extends UnitTestCase
             $this->createMock(CacheManager::class),
             $this->createMock(ClassesConfiguration::class),
             $this->columnMapFactory,
+            $this->createMock(TcaSchemaFactory::class),
             'foo'
         );
 
@@ -78,9 +81,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar'], [])
         );
 
         // Act
@@ -96,10 +97,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_default_sortby' => '',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_default_sortby' => ''], [])
         );
 
         // Act
@@ -115,10 +113,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_default_sortby' => 'pid invalid',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_default_sortby' => 'pid invalid'], [])
         );
 
         // Act
@@ -137,10 +132,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_sortby' => 'uid',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_sortby' => 'uid'], [])
         );
 
         // Act
@@ -159,11 +151,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_sortby' => 'uid',
-                'foreign_default_sortby' => 'pid',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_sortby' => 'uid', 'foreign_default_sortby' => 'pid'], [])
         );
 
         // Act
@@ -182,10 +170,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_default_sortby' => 'pid',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_default_sortby' => 'pid'], [])
         );
 
         // Act
@@ -204,10 +189,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_default_sortby' => 'pid desc',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_default_sortby' => 'pid desc'], [])
         );
 
         // Act
@@ -226,10 +208,7 @@ final class DataMapperTest extends UnitTestCase
         // Arrange
         $this->columnMapFactory->setOneToManyRelation(
             $this->columnMap,
-            [
-                'foreign_table' => 'tx_myextension_bar',
-                'foreign_default_sortby' => 'pid desc, title, uid asc',
-            ]
+            new SelectRelationFieldType('foo', ['foreign_table' => 'tx_myextension_bar', 'foreign_default_sortby' => 'pid desc, title, uid asc'], [])
         );
 
         // Act
-- 
GitLab