diff --git a/typo3/sysext/belog/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/belog/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..8132214ce70545f63716aee49cadc09f68363e35 --- /dev/null +++ b/typo3/sysext/belog/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,37 @@ +<?php +declare(strict_types = 1); + +return [ + \TYPO3\CMS\Belog\Domain\Model\LogEntry::class => [ + 'tableName' => 'sys_log', + 'properties' => [ + 'backendUserUid' => [ + 'fieldName' => 'userid' + ], + 'recordUid' => [ + 'fieldName' => 'recuid' + ], + 'tableName' => [ + 'fieldName' => 'tablename' + ], + 'recordPid' => [ + 'fieldName' => 'recpid' + ], + 'detailsNumber' => [ + 'fieldName' => 'details_nr' + ], + 'ip' => [ + 'fieldName' => 'IP' + ], + 'workspaceUid' => [ + 'fieldName' => 'workspace' + ], + 'newId' => [ + 'fieldName' => 'NEWid' + ], + ] + ], + \TYPO3\CMS\Belog\Domain\Model\Workspace::class => [ + 'tableName' => 'sys_workspace', + ], +]; diff --git a/typo3/sysext/belog/Configuration/TypoScript/setup.typoscript b/typo3/sysext/belog/Configuration/TypoScript/setup.typoscript index 9e515186d209fd1e6d491a1924a664f71007e52e..31ba3f7f04b99380f4bbcf20c8c9296a5dbce33a 100644 --- a/typo3/sysext/belog/Configuration/TypoScript/setup.typoscript +++ b/typo3/sysext/belog/Configuration/TypoScript/setup.typoscript @@ -1,27 +1,4 @@ module.tx_belog { - persistence.classes { - TYPO3\CMS\Belog\Domain\Model\LogEntry { - mapping { - tableName = sys_log - columns { - userid.mapOnProperty = backendUserUid - recuid.mapOnProperty = recordUid - tablename.mapOnProperty = tableName - recpid.mapOnProperty = recordPid - details_nr.mapOnProperty = detailsNumber - IP.mapOnProperty = ip - workspace.mapOnProperty = workspaceUid - NEWid.mapOnProperty = newId - } - } - } - TYPO3\CMS\Belog\Domain\Model\Workspace { - mapping { - tableName = sys_workspace - } - } - } - settings { selectableNumberOfLogEntries { 20 = 20 @@ -54,4 +31,4 @@ module.tx_belog { -1 = actionErrors } } -} \ No newline at end of file +} diff --git a/typo3/sysext/beuser/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/beuser/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..9a94608ed8ac846984b50a06a0859e1de234a46d --- /dev/null +++ b/typo3/sysext/beuser/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,30 @@ +<?php +declare(strict_types = 1); + +return [ + \TYPO3\CMS\Beuser\Domain\Model\BackendUser::class => [ + 'tableName' => 'be_users', + 'properties' => [ + 'allowedLanguages' => [ + 'fieldName' => 'allowed_languages' + ], + 'fileMountPoints' => [ + 'fieldName' => 'file_mountpoints' + ], + 'dbMountPoints' => [ + 'fieldName' => 'db_mountpoints' + ], + 'backendUserGroups' => [ + 'fieldName' => 'usergroup' + ], + ] + ], + \TYPO3\CMS\Beuser\Domain\Model\BackendUserGroup::class => [ + 'tableName' => 'be_groups', + 'properties' => [ + 'subGroups' => [ + 'fieldName' => 'subgroup' + ], + ] + ], +]; diff --git a/typo3/sysext/beuser/Configuration/TypoScript/setup.typoscript b/typo3/sysext/beuser/Configuration/TypoScript/setup.typoscript index 6ec5be88f0b98f2e2cb096a2d34c2a5e74788c51..ca8870f1e71a64a201b884ad1b438c0ffd4ea87f 100644 --- a/typo3/sysext/beuser/Configuration/TypoScript/setup.typoscript +++ b/typo3/sysext/beuser/Configuration/TypoScript/setup.typoscript @@ -1,26 +1,4 @@ // Model/table mapping -config.tx_extbase.persistence.classes { - TYPO3\CMS\Beuser\Domain\Model\BackendUser { - mapping { - tableName = be_users - columns { - allowed_languages.mapOnProperty = allowedLanguages - file_mountpoints.mapOnProperty = fileMountPoints - db_mountpoints.mapOnProperty = dbMountPoints - usergroup.mapOnProperty = backendUserGroups - } - } - } - TYPO3\CMS\Beuser\Domain\Model\BackendUserGroup { - mapping { - tableName = be_groups - columns { - subgroup.mapOnProperty = subGroups - } - } - } -} - module.tx_beuser { persistence { storagePid = 0 @@ -33,4 +11,4 @@ module.tx_beuser { // or if there are other settings set. dummy = foo } -} \ No newline at end of file +} diff --git a/typo3/sysext/core/Classes/Cache/Frontend/NullFrontend.php b/typo3/sysext/core/Classes/Cache/Frontend/NullFrontend.php new file mode 100644 index 0000000000000000000000000000000000000000..2b604cbf8f7bd05dddbb64dfcd3f18066ded2b9c --- /dev/null +++ b/typo3/sysext/core/Classes/Cache/Frontend/NullFrontend.php @@ -0,0 +1,156 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Core\Cache\Frontend; + +use TYPO3\CMS\Core\Cache\Backend\NullBackend; + +/** + * Class TYPO3\CMS\Core\Cache\Frontend\NullFrontend + */ +class NullFrontend implements FrontendInterface +{ + /** + * @var string + */ + private $identifier; + + /** + * @param string $identifier + */ + public function __construct(string $identifier) + { + $this->identifier = $identifier; + } + + /** + * Returns this cache's identifier + * + * @return string The identifier for this cache + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Returns the backend used by this cache + * + * @return \TYPO3\CMS\Core\Cache\Backend\BackendInterface The backend used by this cache + */ + public function getBackend() + { + return new NullBackend(''); + } + + /** + * Saves data in the cache. + * + * @param string $entryIdentifier Something which identifies the data - depends on concrete cache + * @param mixed $data The data to cache - also depends on the concrete cache implementation + * @param array $tags Tags to associate with this cache entry + * @param int $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime. + */ + public function set($entryIdentifier, $data, array $tags = [], $lifetime = null) + { + } + + /** + * Finds and returns data from the cache. + * + * @param string $entryIdentifier Something which identifies the cache entry - depends on concrete cache + * @return mixed + */ + public function get($entryIdentifier) + { + return null; + } + + /** + * Checks if a cache entry with the specified identifier exists. + * + * @param string $entryIdentifier An identifier specifying the cache entry + * @return bool TRUE if such an entry exists, FALSE if not + */ + public function has($entryIdentifier) + { + return false; + } + + /** + * Removes the given cache entry from the cache. + * + * @param string $entryIdentifier An identifier specifying the cache entry + * @return bool TRUE if such an entry exists, FALSE if not + */ + public function remove($entryIdentifier) + { + return false; + } + + /** + * Removes all cache entries of this cache. + */ + public function flush() + { + } + + /** + * Removes all cache entries of this cache which are tagged by the specified tag. + * + * @param string $tag The tag the entries must have + */ + public function flushByTag($tag) + { + } + + /** + * Removes all cache entries of this cache which are tagged by any of the specified tags. + * + * @param string[] $tags List of tags + */ + public function flushByTags(array $tags) + { + } + + /** + * Does garbage collection + */ + public function collectGarbage() + { + } + + /** + * Checks the validity of an entry identifier. Returns TRUE if it's valid. + * + * @param string $identifier An identifier to be checked for validity + * @return bool + */ + public function isValidEntryIdentifier($identifier) + { + return false; + } + + /** + * Checks the validity of a tag. Returns TRUE if it's valid. + * + * @param string $tag A tag to be checked for validity + * @return bool + */ + public function isValidTag($tag) + { + return false; + } +} diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-87623-ReplaceConfigpersistenceclassesTyposcriptConfiguration.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-87623-ReplaceConfigpersistenceclassesTyposcriptConfiguration.rst new file mode 100644 index 0000000000000000000000000000000000000000..06abf95795e1dc39bde8995c19b4bcc1e3637cd5 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-87623-ReplaceConfigpersistenceclassesTyposcriptConfiguration.rst @@ -0,0 +1,94 @@ +.. include:: ../../Includes.txt + +============================================================================== +Breaking: #87623 - Replace config.persistence.classes typoscript configuration +============================================================================== + +See :issue:`87623` + +Description +=========== + +The configuration of classes in the context of the Extbase persistence is no longer possible via typoscript. +All typoscript concerning the configuration of classes in that context needs to be converted to php, residing +in `EXT:Configuration/Extbase/Persistence/Classes.php`. + + +Impact +====== + +Unless converted to php, the configuration in typoscript does no longer have any effect and therefore the following things do no longer work: + +- Overwriting table names for models whose table name derived by conventions differ from the desired one. +- The mapping of database field names to model property names +- The definition of model sub classes which is necessary for a proper implementation of single table inheritance. + + +Affected Installations +====================== + +All installations that configure persistence related classes via typoscript. + + +Migration +========= + +Every extension that used typoscript for such configuration must provide a php configuration class called: +`EXT:Configuration/Extbase/Persistence/Classes.php` + +The migration is best described by an example: + +.. code-block:: typoscript + + config.tx_extbase { + persistence { + classes { + TYPO3\CMS\Extbase\Domain\Model\FileMount { + mapping { + tableName = sys_filemounts + columns { + title.mapOnProperty = title + path.mapOnProperty = path + base.mapOnProperty = isAbsolutePath + } + } + } + } + } + } + +This configuration will look like this, defined in php: + +.. code-block:: php + + <?php + declare(strict_types = 1); + + return [ + \TYPO3\CMS\Extbase\Domain\Model\FileMount::class => [ + 'tableName' => 'sys_filemounts', + 'properties' => [ + 'title' => [ + 'fieldName' => 'title' + ], + 'path' => [ + 'fieldName' => 'path' + ], + 'isAbsolutePath' => [ + 'fieldName' => 'base' + ], + ], + ], + ]; + +A few things are noteworthy here: + +- The typoscript node `mapping` has been dropped and all sub nodes like `tableName` and `columns` are now located directly + in the top node, i.e. the class name. +- The mapping of columns changed due to the fact that `mapOnProperty` has been dropped and the mapping direction changed. + With typoscript the top nodes were called like the class names which indicates the mapping direction model to table. But + then, one had to define a mapping by columns instead of properties, which means, the mapping directions was reversed, + forcing you to map database table fields on properties. This was quite confusing and the configuration is now eased as + one can always think in the model to table mapping direction. + +.. index:: TypoScript, NotScanned, ext:extbase diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..e7d69404370260aa5d0bb70101a135bc795241f0 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,22 @@ +<?php +declare(strict_types = 1); + +return [ + \OliverHader\IrreTutorial\Domain\Model\Content::class => [ + 'tableName' => 'tt_content', + 'properties' => [ + 'hotels' => [ + 'fieldName' => 'tx_irretutorial_1nff_hotels' + ], + ] + ], + \OliverHader\IrreTutorial\Domain\Model\Hotel::class => [ + 'tableName' => 'tx_irretutorial_1nff_hotel', + ], + \OliverHader\IrreTutorial\Domain\Model\Offer::class => [ + 'tableName' => 'tx_irretutorial_1nff_offer', + ], + \OliverHader\IrreTutorial\Domain\Model\Price::class => [ + 'tableName' => 'tx_irretutorial_1nff_price', + ], +]; diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/TypoScript/setup.typoscript b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/TypoScript/setup.typoscript index b614b9378dca7de4bd3ad53d6a9aaf4ca5a6de32..9882ab7d762220f4251cac440ca00604ff05060e 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/TypoScript/setup.typoscript +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/TypoScript/setup.typoscript @@ -6,31 +6,6 @@ plugin.tx_irretutorial { } persistence { storagePid.data = page:uid - classes { - OliverHader\IrreTutorial\Domain\Model\Content { - mapping { - tableName = tt_content - columns { - tx_irretutorial_1nff_hotels.mapOnProperty = hotels - } - } - } - OliverHader\IrreTutorial\Domain\Model\Hotel { - mapping { - tableName = tx_irretutorial_1nff_hotel - } - } - OliverHader\IrreTutorial\Domain\Model\Offer { - mapping { - tableName = tx_irretutorial_1nff_offer - } - } - OliverHader\IrreTutorial\Domain\Model\Price { - mapping { - tableName = tx_irretutorial_1nff_price - } - } - } } } diff --git a/typo3/sysext/extbase/Classes/Core/Bootstrap.php b/typo3/sysext/extbase/Classes/Core/Bootstrap.php index 4b19deaa75522414f5d6d6d4e4f0de5afe1908ef..d23405a29420c111203156b8be1abcfefda6dd81 100644 --- a/typo3/sysext/extbase/Classes/Core/Bootstrap.php +++ b/typo3/sysext/extbase/Classes/Core/Bootstrap.php @@ -19,8 +19,11 @@ namespace TYPO3\CMS\Extbase\Core; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\Route; +use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Web\Response as ExtbaseResponse; +use TYPO3\CMS\Extbase\Persistence\ClassesConfigurationFactory; /** * Creates a request an dispatches it to the controller which was specified @@ -30,6 +33,11 @@ use TYPO3\CMS\Extbase\Mvc\Web\Response as ExtbaseResponse; */ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface { + /** + * @var array + */ + public static $persistenceClasses = []; + /** * Back reference to the parent content object * This has to be public as it is set directly from TYPO3 @@ -75,6 +83,7 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface } $this->initializeObjectManager(); $this->initializeConfiguration($configuration); + $this->initializePersistenceClassesConfiguration(); $this->initializePersistence(); } @@ -110,6 +119,16 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface // todo: to fetch this configuration from the configuration manager. } + /** + * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException + */ + private function initializePersistenceClassesConfiguration(): void + { + $cacheManager = GeneralUtility::makeInstance(CacheManager::class); + GeneralUtility::makeInstance(ClassesConfigurationFactory::class, $cacheManager) + ->createClassesConfiguration(); + } + /** * Initializes the persistence framework * diff --git a/typo3/sysext/extbase/Classes/Persistence/ClassesConfiguration.php b/typo3/sysext/extbase/Classes/Persistence/ClassesConfiguration.php new file mode 100644 index 0000000000000000000000000000000000000000..71b561e4ffd2b04bf7e140de93d1e58ece04a8fb --- /dev/null +++ b/typo3/sysext/extbase/Classes/Persistence/ClassesConfiguration.php @@ -0,0 +1,84 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Persistence; + +/** + * Class TYPO3\CMS\Extbase\Persistence\ClassesConfiguration + */ +class ClassesConfiguration +{ + /** + * @var array + */ + private $configuration; + + /** + * @param array $configuration + */ + public function __construct(array $configuration) + { + $this->configuration = $configuration; + } + + /** + * @param string $className + * @return bool + */ + public function hasClass(string $className): bool + { + return array_key_exists($className, $this->configuration); + } + + /** + * @param string $className + * @return array|null + */ + public function getConfigurationFor(string $className): ?array + { + return $this->configuration[$className] ?? null; + } + + /** + * Resolves all subclasses for the given set of (sub-)classes. + * The whole classes configuration is used to determine all subclasses recursively. + * + * @param string $className + * @return array An numeric array that contains all available subclasses-strings as values. + */ + public function getSubClasses(string $className): array + { + return $this->resolveSubClassesRecursive($className); + } + + /** + * @param string $className + * @param array $subClasses + * @return array + */ + private function resolveSubClassesRecursive(string $className, array $subClasses = []): array + { + foreach ($this->configuration[$className]['subclasses'] ?? [] as $subclass) { + if (in_array($subclass, $subClasses, true)) { + continue; + } + + $subClasses[] = $subclass; + $subClasses = $this->resolveSubClassesRecursive($subclass, $subClasses); + } + + return $subClasses; + } +} diff --git a/typo3/sysext/extbase/Classes/Persistence/ClassesConfigurationFactory.php b/typo3/sysext/extbase/Classes/Persistence/ClassesConfigurationFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..baf346f4ca95dd7638d251bd7d324ea0fbbb337e --- /dev/null +++ b/typo3/sysext/extbase/Classes/Persistence/ClassesConfigurationFactory.php @@ -0,0 +1,139 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Persistence; + +use TYPO3\CMS\Core\Cache\CacheManager; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; +use TYPO3\CMS\Core\Cache\Frontend\NullFrontend; +use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Package\PackageManager; +use TYPO3\CMS\Core\SingletonInterface; +use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; +use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject; + +/** + * Class TYPO3\CMS\Extbase\Persistence\ClassesConfiguration + */ +final class ClassesConfigurationFactory implements SingletonInterface +{ + /** + * @var FrontendInterface + */ + private $cacheFrontend; + + /** + * @param CacheManager|null $cacheManager can be null to disable caching in this factory + */ + public function __construct(CacheManager $cacheManager = null) + { + $cacheIdentifier = 'extbase'; + + $cacheFrontend = new NullFrontend($cacheIdentifier); + if ($cacheManager !== null) { + try { + $cacheFrontend = $cacheManager->getCache($cacheIdentifier); + } catch (\TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException $e) { + // Handling this exception is not needed as $cacheFrontend is + // a NullFrontend at this moment. + } + } + + $this->cacheFrontend = $cacheFrontend; + } + + /** + * @return ClassesConfiguration + */ + public function createClassesConfiguration(): ClassesConfiguration + { + $cacheEntryIdentifier = 'PersistenceClasses_' . sha1(TYPO3_version . Environment::getProjectPath()); + + if ($this->cacheFrontend->has($cacheEntryIdentifier)) { + return new ClassesConfiguration($this->cacheFrontend->get($cacheEntryIdentifier)); + } + + $classes = []; + foreach (GeneralUtility::makeInstance(PackageManager::class)->getActivePackages() as $activePackage) { + $persistenceClassesFile = $activePackage->getPackagePath() . 'Configuration/Extbase/Persistence/Classes.php'; + if (file_exists($persistenceClassesFile)) { + $definedClasses = require $persistenceClassesFile; + if (is_array($definedClasses)) { + ArrayUtility::mergeRecursiveWithOverrule( + $classes, + $definedClasses, + true, + false + ); + } + } + } + + $classes = $this->inheritPropertiesFromParentClasses($classes); + + $this->cacheFrontend->set($cacheEntryIdentifier, $classes); + + return new ClassesConfiguration($classes); + } + + /** + * todo: this method is flawed, see https://forge.typo3.org/issues/87566 + * + * @param array $classes + * @return array + */ + private function inheritPropertiesFromParentClasses(array $classes): array + { + foreach (array_keys($classes) as $className) { + if (!isset($classes[$className]['properties'])) { + $classes[$className]['properties'] = []; + } + + /* + * At first we need to clean the list of parent classes. + * This methods is expected to be called for models that either inherit + * AbstractEntity or AbstractValueObject, therefore we want to know all + * parents of $className until one of these parents. + */ + $relevantParentClasses = []; + $parentClasses = class_parents($className); + while (null !== $parentClass = array_shift($parentClasses)) { + if (in_array($parentClass, [AbstractEntity::class, AbstractValueObject::class], true)) { + break; + } + + $relevantParentClasses[] = $parentClass; + } + + /* + * Once we found all relevant parent classes of $class, we can check their + * property configuration and merge theirs with the current one. This is necessary + * to get the property configuration of parent classes in the current one to not + * miss data in the model later on. + */ + foreach ($relevantParentClasses as $currentClassName) { + if (null === $properties = $classes[$currentClassName]['properties'] ?? null) { + continue; + } + + ArrayUtility::mergeRecursiveWithOverrule($classes[$className]['properties'], $properties, true, false); + } + } + + return $classes; + } +} diff --git a/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/A.php b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/A.php new file mode 100644 index 0000000000000000000000000000000000000000..4295c43650e1df6def4715f5e9aacdf45f3d9c17 --- /dev/null +++ b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/A.php @@ -0,0 +1,25 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model; + +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; + +/** + * Class TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\A + */ +class A extends AbstractEntity +{ +} diff --git a/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/B.php b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/B.php new file mode 100644 index 0000000000000000000000000000000000000000..07ac115273c12f68284f36086e92f985e2d831c0 --- /dev/null +++ b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/B.php @@ -0,0 +1,23 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model; + +/** + * Class TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\B + */ +class B extends A +{ +} diff --git a/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/C.php b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/C.php new file mode 100644 index 0000000000000000000000000000000000000000..ef740a9b1c6fcd5777e0b686622567712a5e5f8d --- /dev/null +++ b/typo3/sysext/extbase/Classes/Persistence/Fixtures/Domain/Model/C.php @@ -0,0 +1,23 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model; + +/** + * Class TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\C + */ +class C extends B +{ +} diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php index 9e0f1fa6b0ba456db382515cc664ab0458b84fd8..07a25978b1a71a6d1617bbfe58310522f304c3ef 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php @@ -1082,6 +1082,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface } } $className = get_class($object); + // todo: decide what to do with this option. if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) { return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid']; } diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php index 81120fc035afa6cc858d0a63f3eb816bdf87c93b..49acc13e0f0bd03af755413cde2c3e18ea9fee77 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php @@ -16,6 +16,8 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\Query\QueryHelper; +use TYPO3\CMS\Extbase\Persistence\ClassesConfiguration; +use TYPO3\CMS\Extbase\Persistence\ClassesConfigurationFactory; use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException; /** @@ -56,6 +58,11 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface */ protected $dataMaps = []; + /** + * @var ClassesConfiguration + */ + private $classesConfiguration; + /** * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager @@ -66,7 +73,8 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService, \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager, \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager, - \TYPO3\CMS\Core\Cache\CacheManager $cacheManager + \TYPO3\CMS\Core\Cache\CacheManager $cacheManager, + ClassesConfigurationFactory $classesConfigurationFactory ) { $this->reflectionService = $reflectionService; $this->configurationManager = $configurationManager; @@ -74,6 +82,7 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface $this->cacheManager = $cacheManager; $this->dataMapCache = $this->cacheManager->getCache('extbase'); + $this->classesConfiguration = $classesConfigurationFactory->createClassesConfiguration(); } /** @@ -121,46 +130,28 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface $recordType = null; $subclasses = []; $tableName = $this->resolveTableName($className); - $columnMapping = []; - $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); - $classSettings = $frameworkConfiguration['persistence']['classes'][$className] ?? null; - if ($classSettings !== null) { - if (isset($classSettings['subclasses']) && is_array($classSettings['subclasses'])) { - $subclasses = $this->resolveSubclassesRecursive($frameworkConfiguration['persistence']['classes'], $classSettings['subclasses']); - } - if (isset($classSettings['mapping']['recordType']) && $classSettings['mapping']['recordType'] !== '') { + $fieldNameToPropertyNameMapping = []; + if ($this->classesConfiguration->hasClass($className)) { + $classSettings = $this->classesConfiguration->getConfigurationFor($className); + $subclasses = $this->classesConfiguration->getSubClasses($className); + if (isset($classSettings['recordType']) && $classSettings['recordType'] !== '') { $recordType = $classSettings['mapping']['recordType']; } - if (isset($classSettings['mapping']['tableName']) && $classSettings['mapping']['tableName'] !== '') { - $tableName = $classSettings['mapping']['tableName']; + if (isset($classSettings['tableName']) && $classSettings['tableName'] !== '') { + $tableName = $classSettings['tableName']; } - $classHierarchy = array_merge([$className], class_parents($className)); - foreach ($classHierarchy as $currentClassName) { - if (in_array($currentClassName, [\TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class])) { - break; - } - $currentClassSettings = $frameworkConfiguration['persistence']['classes'][$currentClassName]; - if ($currentClassSettings !== null) { - if (isset($currentClassSettings['mapping']['columns']) && is_array($currentClassSettings['mapping']['columns'])) { - \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($columnMapping, $currentClassSettings['mapping']['columns'], true, false); - } - } + foreach ($classSettings['properties'] ?? [] as $propertyName => $propertyDefinition) { + $fieldNameToPropertyNameMapping[$propertyDefinition['fieldName']] = $propertyName; } } /** @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap $dataMap */ $dataMap = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap::class, $className, $tableName, $recordType, $subclasses); $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName); - // $classPropertyNames = $this->reflectionService->getClassPropertyNames($className); - $tcaColumnsDefinition = $this->getColumnsDefinition($tableName); - \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($tcaColumnsDefinition, $columnMapping); - // @todo Is this is too powerful? - foreach ($tcaColumnsDefinition as $columnName => $columnDefinition) { - if (isset($columnDefinition['mapOnProperty'])) { - $propertyName = $columnDefinition['mapOnProperty']; - } else { - $propertyName = \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToLowerCamelCase($columnName); - } + foreach ($this->getColumnsDefinition($tableName) as $columnName => $columnDefinition) { + $propertyName = $fieldNameToPropertyNameMapping[$columnName] + ?? \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToLowerCamelCase($columnName); + // @todo: shall we really create column maps for non existing properties? // @todo: check why this could happen in the first place. TCA definitions for non existing model properties? $columnMap = $this->createColumnMap($columnName, $propertyName); @@ -199,27 +190,6 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface return $tableName; } - /** - * Resolves all subclasses for the given set of (sub-)classes. - * The whole classes configuration is used to determine all subclasses recursively. - * - * @param array $classesConfiguration The framework configuration part [persistence][classes]. - * @param array $subclasses An array of subclasses defined via TypoScript - * @return array An numeric array that contains all available subclasses-strings as values. - */ - protected function resolveSubclassesRecursive(array $classesConfiguration, array $subclasses) - { - $allSubclasses = []; - foreach ($subclasses as $subclass) { - $allSubclasses[] = $subclass; - if (isset($classesConfiguration[$subclass]['subclasses']) && is_array($classesConfiguration[$subclass]['subclasses'])) { - $childSubclasses = $this->resolveSubclassesRecursive($classesConfiguration, $classesConfiguration[$subclass]['subclasses']); - $allSubclasses = array_merge($allSubclasses, $childSubclasses); - } - } - return $allSubclasses; - } - /** * Returns the TCA ctrl section of the specified table; or NULL if not set * diff --git a/typo3/sysext/extbase/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/extbase/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..d8c8426d88874d94613f75265d672ea4ea4eebf2 --- /dev/null +++ b/typo3/sysext/extbase/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,117 @@ +<?php +declare(strict_types = 1); + +return [ + \TYPO3\CMS\Extbase\Domain\Model\FileMount::class => [ + 'tableName' => 'sys_filemounts', + 'properties' => [ + 'title' => [ + 'fieldName' => 'title' + ], + 'path' => [ + 'fieldName' => 'path' + ], + 'isAbsolutePath' => [ + 'fieldName' => 'base' + ], + ], + ], + \TYPO3\CMS\Extbase\Domain\Model\FileReference::class => [ + 'tableName' => 'sys_file_reference', + ], + \TYPO3\CMS\Extbase\Domain\Model\File::class => [ + 'tableName' => 'sys_file', + ], + \TYPO3\CMS\Extbase\Domain\Model\BackendUser::class => [ + 'tableName' => 'be_users', + 'properties' => [ + 'userName' => [ + 'fieldName' => 'username' + ], + 'isAdministrator' => [ + 'fieldName' => 'admin' + ], + 'isDisabled' => [ + 'fieldName' => 'disable' + ], + 'realName' => [ + 'fieldName' => 'realName' + ], + 'startDateAndTime' => [ + 'fieldName' => 'starttime' + ], + 'endDateAndTime' => [ + 'fieldName' => 'endtime' + ], + 'ipLockIsDisabled' => [ + 'fieldName' => 'disableIPlock' + ], + 'lastLoginDateAndTime' => [ + 'fieldName' => 'lastlogin' + ], + ], + ], + \TYPO3\CMS\Extbase\Domain\Model\BackendUserGroup::class => [ + 'tableName' => 'be_groups', + 'properties' => [ + 'subGroups' => [ + 'fieldName' => 'subgroup' + ], + 'modules' => [ + 'fieldName' => 'groupMods' + ], + 'tablesListening' => [ + 'fieldName' => 'tables_select' + ], + 'tablesModify' => [ + 'fieldName' => 'tables_modify' + ], + 'pageTypes' => [ + 'fieldName' => 'pagetypes_select' + ], + 'allowedExcludeFields' => [ + 'fieldName' => 'non_exclude_fields' + ], + 'explicitlyAllowAndDeny' => [ + 'fieldName' => 'explicit_allowdeny' + ], + 'allowedLanguages' => [ + 'fieldName' => 'allowed_languages' + ], + 'workspacePermission' => [ + 'fieldName' => 'workspace_perms' + ], + 'databaseMounts' => [ + 'fieldName' => 'db_mountpoints' + ], + 'fileOperationPermissions' => [ + 'fieldName' => 'file_permissions' + ], + 'lockToDomain' => [ + 'fieldName' => 'lockToDomain' + ], + 'tsConfig' => [ + 'fieldName' => 'TSconfig' + ], + ], + ], + \TYPO3\CMS\Extbase\Domain\Model\FrontendUser::class => [ + 'tableName' => 'fe_users', + 'properties' => [ + 'lockToDomain' => [ + 'fieldName' => 'lockToDomain' + ], + ], + ], + \TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup::class => [ + 'tableName' => 'fe_groups', + 'properties' => [ + 'lockToDomain' => [ + 'fieldName' => 'lockToDomain' + ], + ], + ], + \TYPO3\CMS\Extbase\Domain\Model\Category::class => [ + 'tableName' => 'sys_category', + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..e317d2f8c9859cd2930e4e7ee8d74f2a4b7b0588 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,23 @@ +<?php +declare(strict_types = 1); + +return [ + \ExtbaseTeam\BlogExample\Domain\Model\Administrator::class => [ + 'tableName' => 'fe_users', + 'recordType' => \ExtbaseTeam\BlogExample\Domain\Model\Administrator::class + ], + \ExtbaseTeam\BlogExample\Domain\Model\TtContent::class => [ + 'tableName' => 'tt_content', + 'properties' => [ + 'uid' => [ + 'fieldName' => 'uid' + ], + 'pid' => [ + 'fieldName' => 'pid' + ], + 'header' => [ + 'fieldName' => 'header' + ], + ], + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_typoscript_setup.typoscript b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_typoscript_setup.typoscript deleted file mode 100644 index 05a80663cb43d8d6f6658a44b89abd36519fb26f..0000000000000000000000000000000000000000 --- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_typoscript_setup.typoscript +++ /dev/null @@ -1,29 +0,0 @@ - # global configuration - -config.tx_extbase { - persistence{ - classes { - Extbase\Domain\ModelFrontendUser { - subclasses { - ExtbaseTeam\BlogExample\Domain\Model\Administrator = ExtbaseTeam\BlogExample\Domain\Model\Administrator - } - } - ExtbaseTeam\BlogExample\Domain\Model\Administrator { - mapping { - tableName = fe_users - recordType = ExtbaseTeam\BlogExample\Domain\Model\Administrator - } - } - ExtbaseTeam\BlogExample\Domain\Model\TtContent { - mapping { - tableName = tt_content - columns { - uid.mapOnProperty = uid - pid.mapOnProperty = pid - header.mapOnProperty = header - } - } - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Configuration/Extbase/Persistence/Classes.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000000000000000000000000000000000000..f3b842322a4edb497209a7b0a952efd602334e51 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,8 @@ +<?php +declare(strict_types = 1); + +return [ + \ExtbaseTeam\B\Domain\Model\B::class => [ + 'tableName' => 'tx_a_domain_model_a' + ] +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_typoscript_setup.typoscript b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_typoscript_setup.typoscript deleted file mode 100644 index 3ae18ddcb792bce8daaef12d05a9c2ac63144648..0000000000000000000000000000000000000000 --- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_typoscript_setup.typoscript +++ /dev/null @@ -1,9 +0,0 @@ -config.tx_extbase.persistence { - classes { - ExtbaseTeam\B\Domain\Model\B { - mapping { - tableName = tx_a_domain_model_a - } - } - } -} diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationFactoryTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9badd87ac05996b88989eb16fc9ea7f2d84d4baf --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationFactoryTest.php @@ -0,0 +1,103 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence; + +use TYPO3\CMS\Extbase\Persistence\ClassesConfigurationFactory; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Class TYPO3\CMS\Extbase\Tests\Unit\Persistence\ClassesConfigurationTest + */ +class ClassesConfigurationFactoryTest extends UnitTestCase +{ + /** + * @test + */ + public function inheritPropertiesFromParentClasses(): void + { + $classesConfigurationFactory = new ClassesConfigurationFactory(); + + $classes = [ + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\A::class => [ + 'properties' => [ + 'propertiesFromA' => [ + 'fieldName' => 'field_name_a' + ] + ] + ], + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\B::class => [ + 'properties' => [ + 'propertiesFromA' => [ + 'fieldName' => 'field_name_z' + ], + 'propertiesFromB' => [ + 'fieldName' => 'field_name_b' + ] + ] + ], + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\C::class => [ + 'properties' => [ + 'columnNameC' => [ + 'fieldName' => 'field_name_c' + ] + ] + ], + ]; + + $reflectionMethod = (new \ReflectionClass($classesConfigurationFactory)) + ->getMethod('inheritPropertiesFromParentClasses'); + $reflectionMethod->setAccessible(true); + $classes = $reflectionMethod->invoke($classesConfigurationFactory, $classes); + + static::assertSame( + [ + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\A::class => [ + 'properties' => [ + 'propertiesFromA' => [ + 'fieldName' => 'field_name_a' + ], + ] + ], + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\B::class => [ + 'properties' => [ + 'propertiesFromA' => [ + // todo: this is flawed, we'd actually expect field_name_z here + // todo: see https://forge.typo3.org/issues/87566 + 'fieldName' => 'field_name_a' + ], + 'propertiesFromB' => [ + 'fieldName' => 'field_name_b' + ], + ] + ], + \TYPO3\CMS\Extbase\Persistence\Fixtures\Domain\Model\C::class => [ + 'properties' => [ + 'columnNameC' => [ + 'fieldName' => 'field_name_c' + ], + 'propertiesFromA' => [ + 'fieldName' => 'field_name_a' + ], + 'propertiesFromB' => [ + 'fieldName' => 'field_name_b' + ], + ] + ], + ], + $classes + ); + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..10960d48c1ff740415630443a21bf214bedd2b33 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/ClassesConfigurationTest.php @@ -0,0 +1,173 @@ +<?php +declare(strict_types = 1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence; + +use TYPO3\CMS\Extbase\Persistence\ClassesConfiguration; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Class TYPO3\CMS\Extbase\Tests\Unit\Persistence\ClassesConfigurationTest + */ +class ClassesConfigurationTest extends UnitTestCase +{ + /** + * @test + */ + public function hasClassReturnsTrue(): void + { + $className = 'ClassName'; + $classesConfiguration = new ClassesConfiguration([$className => []]); + static::assertTrue($classesConfiguration->hasClass($className)); + } + + /** + * @test + */ + public function hasClassReturnsFalse(): void + { + $className = 'ClassName'; + $classesConfiguration = new ClassesConfiguration([]); + static::assertFalse($classesConfiguration->hasClass($className)); + } + + /** + * @test + */ + public function getConfigurationForReturnsArray(): void + { + $configuration = [ + 'ClassName' => [ + 'tableName' => 'table' + ] + ]; + $classesConfiguration = new ClassesConfiguration($configuration); + static::assertSame( + $configuration['ClassName'], + $classesConfiguration->getConfigurationFor('ClassName') + ); + } + + /** + * @test + */ + public function getConfigurationForReturnsNull(): void + { + $classesConfiguration = new ClassesConfiguration([]); + static::assertNull($classesConfiguration->getConfigurationFor('ClassName')); + } + + /** + * @return array + */ + public function resolveSubclassesRecursiveDataProvider(): array + { + return [ + [ + [ + 'B', + 'C', + 'A', + ], + [ + 'A' => [ + 'subclasses' => [ + 'B', + ] + ], + 'B' => [ + 'subclasses' => [ + 'C' + ] + ], + 'C' => [ + 'subclasses' => [ + 'A' + ] + ], + ], + 'A' + ], + [ + [ + 'A', + 'B', + 'C', + ], + [ + 'A' => [ + 'subclasses' => [ + 'B', + ] + ], + 'B' => [ + 'subclasses' => [ + 'C' + ] + ], + 'C' => [ + 'subclasses' => [ + 'A' + ] + ], + ], + 'C' + ], + [ + [ + 'C', + 'A', + 'B', + ], + [ + 'A' => [ + 'subclasses' => [ + 'C', + ] + ], + 'B' => [ + 'subclasses' => [ + 'C', + 'B', + ] + ], + 'C' => [ + 'subclasses' => [ + 'A', + 'B', + ] + ], + ], + 'B' + ], + ]; + } + + /** + * @dataProvider resolveSubclassesRecursiveDataProvider + * @test + * @param array $expected + * @param array $configuration + * @param string $className + */ + public function getSubclasses(array $expected, array $configuration, string $className): void + { + $classesConfiguration = new ClassesConfiguration($configuration); + static::assertSame( + $expected, + $classesConfiguration->getSubClasses($className) + ); + } +} diff --git a/typo3/sysext/extbase/ext_typoscript_setup.typoscript b/typo3/sysext/extbase/ext_typoscript_setup.typoscript index 4cb1f9bbb3264678fc8506b99fe907eabd6708aa..14f31a929b0fee90a8075b100cd48e5e1987d432 100644 --- a/typo3/sysext/extbase/ext_typoscript_setup.typoscript +++ b/typo3/sysext/extbase/ext_typoscript_setup.typoscript @@ -9,84 +9,6 @@ config.tx_extbase { persistence{ enableAutomaticCacheClearing = 1 updateReferenceIndex = 0 - classes { - TYPO3\CMS\Extbase\Domain\Model\FileMount { - mapping { - tableName = sys_filemounts - columns { - title.mapOnProperty = title - path.mapOnProperty = path - base.mapOnProperty = isAbsolutePath - } - } - } - TYPO3\CMS\Extbase\Domain\Model\FileReference { - mapping { - tableName = sys_file_reference - } - } - TYPO3\CMS\Extbase\Domain\Model\File { - mapping { - tableName = sys_file - } - } - TYPO3\CMS\Extbase\Domain\Model\BackendUser { - mapping { - tableName = be_users - columns { - username.mapOnProperty = userName - admin.mapOnProperty = isAdministrator - disable.mapOnProperty = isDisabled - realName.mapOnProperty = realName - starttime.mapOnProperty = startDateAndTime - endtime.mapOnProperty = endDateAndTime - disableIPlock.mapOnProperty = ipLockIsDisabled - lastlogin.mapOnProperty = lastLoginDateAndTime - } - } - } - TYPO3\CMS\Extbase\Domain\Model\BackendUserGroup { - mapping { - tableName = be_groups - columns { - subgroup.mapOnProperty = subGroups - groupMods.mapOnProperty = modules - tables_select.mapOnProperty = tablesListening - tables_modify.mapOnProperty = tablesModify - pagetypes_select.mapOnProperty = pageTypes - non_exclude_fields.mapOnProperty = allowedExcludeFields - explicit_allowdeny.mapOnProperty = explicitlyAllowAndDeny - allowed_languages.mapOnProperty = allowedLanguages - workspace_perms.mapOnProperty = workspacePermission - db_mountpoints.mapOnProperty = databaseMounts - file_permissions.mapOnProperty = fileOperationPermissions - lockToDomain.mapOnProperty = lockToDomain - TSconfig.mapOnProperty = tsConfig - } - } - } - TYPO3\CMS\Extbase\Domain\Model\FrontendUser { - mapping { - tableName = fe_users - columns { - lockToDomain.mapOnProperty = lockToDomain - } - } - } - TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup { - mapping { - tableName = fe_groups - columns { - lockToDomain.mapOnProperty = lockToDomain - } - } - } - TYPO3\CMS\Extbase\Domain\Model\Category { - mapping { - tableName = sys_category - } - } - } } features { # if enabled, default controller and/or action is skipped when creating URIs through the URI Builder (see https://wiki.typo3.org/Skip_default_arguments_in_URIs)