From bd69d119a0a23381c358352b986c2466f600defb Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Mon, 27 Jan 2020 15:52:01 +0100 Subject: [PATCH] [TASK] Dissolve PersistenceDelegate for persisted mappers The intention of PersistenceDelegate was to provide a generic API to resolve or generate route components. However the implementation did not provide any additional behavior. It just was used as structural pattern which did not provide any further advantages. That's the reason why PersistenceDelegate gets dissolved and its database connection invocation is moved to its corresponding callers PersistedAliasMapper and PersistedPatternMapper. For backward-compatibility reasons previous (protected) implementation is still kept in legacy layer for those two route aspects. Resolves: #90218 Releases: master, 9.5 Change-Id: I49eb58372c139c9b8274593efa2892a43eb81508 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63049 Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Susanne Moog <look@susi.dev> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Johannes Schlier Reviewed-by: Susanne Moog <look@susi.dev> --- .../Routing/Aspect/DelegateInterface.php | 2 + .../Routing/Aspect/PersistedAliasMapper.php | 97 ++++++++----------- .../Routing/Aspect/PersistedPatternMapper.php | 78 +++++++-------- .../Routing/Aspect/PersistenceDelegate.php | 2 + .../PersistedAliasMapperLegacyTrait.php | 70 +++++++++++++ .../PersistedPatternMapperLegacyTrait.php | 93 ++++++++++++++++++ 6 files changed, 241 insertions(+), 101 deletions(-) create mode 100644 typo3/sysext/core/Classes/Routing/Legacy/PersistedAliasMapperLegacyTrait.php create mode 100644 typo3/sysext/core/Classes/Routing/Legacy/PersistedPatternMapperLegacyTrait.php diff --git a/typo3/sysext/core/Classes/Routing/Aspect/DelegateInterface.php b/typo3/sysext/core/Classes/Routing/Aspect/DelegateInterface.php index 8145e7fd25f5..27eb7feac533 100644 --- a/typo3/sysext/core/Classes/Routing/Aspect/DelegateInterface.php +++ b/typo3/sysext/core/Classes/Routing/Aspect/DelegateInterface.php @@ -19,6 +19,8 @@ namespace TYPO3\CMS\Core\Routing\Aspect; /** * Interface that describes delegations of tasks to different processors * when resolving or generating parameters for URLs. + * + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 */ interface DelegateInterface { diff --git a/typo3/sysext/core/Classes/Routing/Aspect/PersistedAliasMapper.php b/typo3/sysext/core/Classes/Routing/Aspect/PersistedAliasMapper.php index ebfc87066282..9b049bfa8670 100644 --- a/typo3/sysext/core/Classes/Routing/Aspect/PersistedAliasMapper.php +++ b/typo3/sysext/core/Classes/Routing/Aspect/PersistedAliasMapper.php @@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Context\LanguageAspectFactory; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Domain\Repository\PageRepository; +use TYPO3\CMS\Core\Routing\Legacy\PersistedAliasMapperLegacyTrait; use TYPO3\CMS\Core\Site\SiteLanguageAwareInterface; use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -47,6 +48,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMappableAspectInterface, SiteLanguageAwareInterface { use SiteLanguageAwareTrait; + use PersistedAliasMapperLegacyTrait; /** * @var array @@ -69,14 +71,14 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa protected $routeValuePrefix; /** - * @var PersistenceDelegate + * @var string[] */ - protected $persistenceDelegate; + protected $persistenceFieldNames; /** - * @var string[] + * @var string|null */ - protected $persistenceFieldNames; + protected $languageFieldName; /** * @var string|null @@ -116,8 +118,9 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa $this->tableName = $tableName; $this->routeFieldName = $routeFieldName; $this->routeValuePrefix = $routeValuePrefix; - $this->persistenceFieldNames = $this->buildPersistenceFieldNames(); + $this->languageFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null; $this->languageParentFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['transOrigPointerField'] ?? null; + $this->persistenceFieldNames = $this->buildPersistenceFieldNames(); } /** @@ -125,9 +128,7 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa */ public function generate(string $value): ?string { - $result = $this->getPersistenceDelegate()->generate([ - 'uid' => $value - ]); + $result = $this->findByIdentifier($value); $result = $this->resolveOverlay($result); if (!isset($result[$this->routeFieldName])) { return null; @@ -143,9 +144,7 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa public function resolve(string $value): ?string { $value = $this->routeValuePrefix . $this->purgeRouteValuePrefix($value); - $result = $this->getPersistenceDelegate()->resolve([ - $this->routeFieldName => $value - ]); + $result = $this->findByRouteFieldValue($value); if ($result[$this->languageParentFieldName] ?? null > 0) { return (string)$result[$this->languageParentFieldName]; } @@ -164,8 +163,8 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa 'uid', 'pid', $this->routeFieldName, - $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null, - $GLOBALS['TCA'][$this->tableName]['ctrl']['transOrigPointerField'] ?? null, + $this->languageFieldName, + $this->languageParentFieldName, ]); } @@ -181,55 +180,39 @@ class PersistedAliasMapper implements PersistedMappableAspectInterface, StaticMa return ltrim($value, $this->routeValuePrefix); } - /** - * @return PersistenceDelegate - */ - protected function getPersistenceDelegate(): PersistenceDelegate + protected function findByIdentifier(string $value): ?array { - if ($this->persistenceDelegate !== null) { - return $this->persistenceDelegate; - } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($this->tableName) - ->from($this->tableName); - // @todo Restrictions (Hidden? Workspace?) - - $resolveModifier = function (QueryBuilder $queryBuilder, array $values) { - return $queryBuilder->select(...$this->persistenceFieldNames)->where( - ...$this->createFieldConstraints($queryBuilder, $values) - ); - }; - $generateModifier = function (QueryBuilder $queryBuilder, array $values) { - return $queryBuilder->select(...$this->persistenceFieldNames)->where( - ...$this->createFieldConstraints($queryBuilder, $values) - ); - }; + $queryBuilder = $this->createQueryBuilder(); + $result = $queryBuilder + ->select(...$this->persistenceFieldNames) + ->where($queryBuilder->expr()->eq( + 'uid', + $queryBuilder->createNamedParameter($value, \PDO::PARAM_INT) + )) + ->execute() + ->fetch(); + return $result !== false ? $result : null; + } - return $this->persistenceDelegate = new PersistenceDelegate( - $queryBuilder, - $resolveModifier, - $generateModifier - ); + protected function findByRouteFieldValue(string $value): ?array + { + $queryBuilder = $this->createQueryBuilder(); + $result = $queryBuilder + ->select(...$this->persistenceFieldNames) + ->where($queryBuilder->expr()->eq( + $this->routeFieldName, + $queryBuilder->createNamedParameter($value, \PDO::PARAM_STR) + )) + ->execute() + ->fetch(); + return $result !== false ? $result : null; } - /** - * @param QueryBuilder $queryBuilder - * @param array $values - * @return array - */ - protected function createFieldConstraints(QueryBuilder $queryBuilder, array $values): array + protected function createQueryBuilder(): QueryBuilder { - $constraints = []; - foreach ($values as $fieldName => $fieldValue) { - $constraints[] = $queryBuilder->expr()->eq( - $fieldName, - $queryBuilder->createNamedParameter( - $fieldValue, - \PDO::PARAM_STR - ) - ); - } - return $constraints; + return GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($this->tableName) + ->from($this->tableName); } /** diff --git a/typo3/sysext/core/Classes/Routing/Aspect/PersistedPatternMapper.php b/typo3/sysext/core/Classes/Routing/Aspect/PersistedPatternMapper.php index c1a0a4c1ed65..f0c5b22a811b 100644 --- a/typo3/sysext/core/Classes/Routing/Aspect/PersistedPatternMapper.php +++ b/typo3/sysext/core/Classes/Routing/Aspect/PersistedPatternMapper.php @@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Context\LanguageAspectFactory; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Domain\Repository\PageRepository; +use TYPO3\CMS\Core\Routing\Legacy\PersistedPatternMapperLegacyTrait; use TYPO3\CMS\Core\Site\SiteLanguageAwareInterface; use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -50,6 +51,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class PersistedPatternMapper implements PersistedMappableAspectInterface, StaticMappableAspectInterface, SiteLanguageAwareInterface { use SiteLanguageAwareTrait; + use PersistedPatternMapperLegacyTrait; protected const PATTERN_RESULT = '#\{(?P<fieldName>[^}]+)\}#'; @@ -78,11 +80,6 @@ class PersistedPatternMapper implements PersistedMappableAspectInterface, Static */ protected $routeFieldResultNames; - /** - * @var PersistenceDelegate - */ - protected $persistenceDelegate; - /** * @var string|null */ @@ -127,9 +124,7 @@ class PersistedPatternMapper implements PersistedMappableAspectInterface, Static */ public function generate(string $value): ?string { - $result = $this->getPersistenceDelegate()->generate([ - 'uid' => $value - ]); + $result = $this->findByIdentifier($value); $result = $this->resolveOverlay($result); return $this->createRouteResult($result); } @@ -143,7 +138,7 @@ class PersistedPatternMapper implements PersistedMappableAspectInterface, Static return null; } $values = $this->filterNamesKeys($matches); - $result = $this->getPersistenceDelegate()->resolve($values); + $result = $this->findByRouteFieldValues($values); if ($result[$this->languageParentFieldName] ?? null > 0) { return (string)$result[$this->languageParentFieldName]; } @@ -193,51 +188,46 @@ class PersistedPatternMapper implements PersistedMappableAspectInterface, Static ); } - /** - * @return PersistenceDelegate - */ - protected function getPersistenceDelegate(): PersistenceDelegate + protected function findByIdentifier(string $value): ?array { - if ($this->persistenceDelegate !== null) { - return $this->persistenceDelegate; - } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($this->tableName) - ->from($this->tableName); - // @todo Restrictions (Hidden? Workspace?) + $queryBuilder = $this->createQueryBuilder(); + $result = $queryBuilder + ->select('*') + ->where($queryBuilder->expr()->eq( + 'uid', + $queryBuilder->createNamedParameter($value, \PDO::PARAM_INT) + )) + ->execute() + ->fetch(); + return $result !== false ? $result : null; + } - $resolveModifier = function (QueryBuilder $queryBuilder, array $values) { - return $queryBuilder->select('*')->where( - ...$this->createFieldConstraints($queryBuilder, $values, true) - ); - }; - $generateModifier = function (QueryBuilder $queryBuilder, array $values) { - return $queryBuilder->select('*')->where( - ...$this->createFieldConstraints($queryBuilder, $values) - ); - }; + protected function findByRouteFieldValues(array $values): ?array + { + $queryBuilder = $this->createQueryBuilder(); + $result = $queryBuilder + ->select('*') + ->where(...$this->createRouteFieldConstraints($queryBuilder, $values)) + ->execute() + ->fetch(); + return $result !== false ? $result : null; + } - return $this->persistenceDelegate = new PersistenceDelegate( - $queryBuilder, - $resolveModifier, - $generateModifier - ); + protected function createQueryBuilder(): QueryBuilder + { + return GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($this->tableName) + ->from($this->tableName); } /** * @param QueryBuilder $queryBuilder * @param array $values - * @param bool $resolveExpansion * @return array */ - protected function createFieldConstraints( - QueryBuilder $queryBuilder, - array $values, - bool $resolveExpansion = false - ): array { - $languageExpansion = $this->languageParentFieldName - && $resolveExpansion - && isset($values['uid']); + protected function createRouteFieldConstraints(QueryBuilder $queryBuilder, array $values): array + { + $languageExpansion = $this->languageParentFieldName && isset($values['uid']); $constraints = []; foreach ($values as $fieldName => $fieldValue) { diff --git a/typo3/sysext/core/Classes/Routing/Aspect/PersistenceDelegate.php b/typo3/sysext/core/Classes/Routing/Aspect/PersistenceDelegate.php index a0feffeb14a3..b31b9a75e072 100644 --- a/typo3/sysext/core/Classes/Routing/Aspect/PersistenceDelegate.php +++ b/typo3/sysext/core/Classes/Routing/Aspect/PersistenceDelegate.php @@ -21,6 +21,8 @@ use TYPO3\CMS\Core\Database\Query\QueryBuilder; /** * Delegate implementation in order to retrieve and generate values * using a database connection. + * + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 */ class PersistenceDelegate implements DelegateInterface { diff --git a/typo3/sysext/core/Classes/Routing/Legacy/PersistedAliasMapperLegacyTrait.php b/typo3/sysext/core/Classes/Routing/Legacy/PersistedAliasMapperLegacyTrait.php new file mode 100644 index 000000000000..735ef91c9f00 --- /dev/null +++ b/typo3/sysext/core/Classes/Routing/Legacy/PersistedAliasMapperLegacyTrait.php @@ -0,0 +1,70 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Core\Routing\Legacy; + +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Routing\Aspect\PersistenceDelegate; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +trait PersistedAliasMapperLegacyTrait +{ + /** + * @var PersistenceDelegate + */ + protected $persistenceDelegate; + + /** + * @return PersistenceDelegate + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 + */ + protected function getPersistenceDelegate(): PersistenceDelegate + { + if ($this->persistenceDelegate !== null) { + return $this->persistenceDelegate; + } + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($this->tableName) + ->from($this->tableName); + // @todo Restrictions (Hidden? Workspace?) + + $resolveModifier = function (QueryBuilder $queryBuilder, array $values) { + return $queryBuilder->select(...$this->persistenceFieldNames)->where( + ...$this->createFieldConstraints($queryBuilder, $values) + ); + }; + $generateModifier = function (QueryBuilder $queryBuilder, array $values) { + return $queryBuilder->select(...$this->persistenceFieldNames)->where( + ...$this->createFieldConstraints($queryBuilder, $values) + ); + }; + + return $this->persistenceDelegate = new PersistenceDelegate( + $queryBuilder, + $resolveModifier, + $generateModifier + ); + } + + /** + * @param QueryBuilder $queryBuilder + * @param array $values + * @return array + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 + */ + protected function createFieldConstraints(QueryBuilder $queryBuilder, array $values): array + { + $constraints = []; + foreach ($values as $fieldName => $fieldValue) { + $constraints[] = $queryBuilder->expr()->eq( + $fieldName, + $queryBuilder->createNamedParameter( + $fieldValue, + \PDO::PARAM_STR + ) + ); + } + return $constraints; + } +} diff --git a/typo3/sysext/core/Classes/Routing/Legacy/PersistedPatternMapperLegacyTrait.php b/typo3/sysext/core/Classes/Routing/Legacy/PersistedPatternMapperLegacyTrait.php new file mode 100644 index 000000000000..c33b8603d5bb --- /dev/null +++ b/typo3/sysext/core/Classes/Routing/Legacy/PersistedPatternMapperLegacyTrait.php @@ -0,0 +1,93 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Core\Routing\Legacy; + +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Routing\Aspect\PersistenceDelegate; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +trait PersistedPatternMapperLegacyTrait +{ + /** + * @var PersistenceDelegate + */ + protected $persistenceDelegate; + + /** + * @return PersistenceDelegate + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 + */ + protected function getPersistenceDelegate(): PersistenceDelegate + { + if ($this->persistenceDelegate !== null) { + return $this->persistenceDelegate; + } + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($this->tableName) + ->from($this->tableName); + // @todo Restrictions (Hidden? Workspace?) + + $resolveModifier = function (QueryBuilder $queryBuilder, array $values) { + return $queryBuilder->select('*')->where( + ...$this->createRouteFieldConstraints($queryBuilder, $values, true) + ); + }; + $generateModifier = function (QueryBuilder $queryBuilder, array $values) { + return $queryBuilder->select('*')->where( + ...$this->createRouteFieldConstraints($queryBuilder, $values) + ); + }; + + return $this->persistenceDelegate = new PersistenceDelegate( + $queryBuilder, + $resolveModifier, + $generateModifier + ); + } + + /** + * @param QueryBuilder $queryBuilder + * @param array $values + * @param bool $resolveExpansion + * @return array + * @deprecated since TYPO3 v10.3, will be removed in TYPO3 v11.0 + */ + protected function createRouteFieldConstraints( + QueryBuilder $queryBuilder, + array $values, + bool $resolveExpansion = false + ): array { + $languageExpansion = $this->languageParentFieldName + && $resolveExpansion + && isset($values['uid']); + + $constraints = []; + foreach ($values as $fieldName => $fieldValue) { + if ($languageExpansion && $fieldName === 'uid') { + continue; + } + $constraints[] = $queryBuilder->expr()->eq( + $fieldName, + $queryBuilder->createNamedParameter( + $fieldValue, + \PDO::PARAM_STR + ) + ); + } + // If requested, either match uid or language parent field value + if ($languageExpansion) { + $idParameter = $queryBuilder->createNamedParameter( + $values['uid'], + \PDO::PARAM_INT + ); + $constraints[] = $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq('uid', $idParameter), + $queryBuilder->expr()->eq($this->languageParentFieldName, $idParameter) + ); + } + + return $constraints; + } +} -- GitLab