‪TYPO3CMS  ‪main
PersistedAliasMapper.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
32 
53 {
54  use ‪AspectTrait;
57  use UnresolvedValueTrait;
58 
62  protected ‪$settings;
63 
67  protected ‪$tableName;
68 
72  protected ‪$routeFieldName;
73 
78 
82  protected ‪$persistenceFieldNames;
83 
87  protected ‪$languageFieldName;
88 
93 
97  protected ‪$slugUniqueInSite;
98 
102  public function ‪__construct(array ‪$settings)
103  {
104  ‪$tableName = ‪$settings['tableName'] ?? null;
105  ‪$routeFieldName = ‪$settings['routeFieldName'] ?? null;
106  ‪$routeValuePrefix = ‪$settings['routeValuePrefix'] ?? '';
107 
108  if (!is_string(‪$tableName)) {
109  throw new \InvalidArgumentException(
110  'tableName must be string',
111  1537277133
112  );
113  }
114  if (!is_string(‪$routeFieldName)) {
115  throw new \InvalidArgumentException(
116  'routeFieldName name must be string',
117  1537277134
118  );
119  }
120  if (!is_string(‪$routeValuePrefix) || strlen(‪$routeValuePrefix) > 1) {
121  throw new \InvalidArgumentException(
122  '$routeValuePrefix must be string with one character',
123  1537277136
124  );
125  }
126 
127  $this->settings = ‪$settings;
128  $this->tableName = ‪$tableName;
129  $this->routeFieldName = ‪$routeFieldName;
130  $this->routeValuePrefix = ‪$routeValuePrefix;
131  $this->languageFieldName = ‪$GLOBALS['TCA'][‪$this->tableName]['ctrl']['languageField'] ?? null;
132  $this->languageParentFieldName = ‪$GLOBALS['TCA'][‪$this->tableName]['ctrl']['transOrigPointerField'] ?? null;
133  $this->persistenceFieldNames = $this->‪buildPersistenceFieldNames();
134  $this->slugUniqueInSite = $this->isSlugUniqueInSite($this->tableName, $this->routeFieldName);
135  }
136 
140  public function ‪generate(string $value): ?string
141  {
142  $result = $this->‪findByIdentifier($value);
143  $result = $this->‪resolveOverlay($result);
144  if (!isset($result[$this->routeFieldName])) {
145  return null;
146  }
148  (string)$result[$this->routeFieldName]
149  );
150  }
151 
155  public function ‪resolve(string $value): ?string
156  {
157  $value = $this->routeValuePrefix . $this->‪purgeRouteValuePrefix($value);
158  $result = $this->‪findByRouteFieldValue($value);
159  if (($result[$this->languageParentFieldName] ?? null) > 0) {
160  return (string)$result[‪$this->languageParentFieldName];
161  }
162  if (isset($result['uid'])) {
163  return (string)$result['uid'];
164  }
165  return null;
166  }
167 
171  protected function ‪buildPersistenceFieldNames(): array
172  {
173  return array_filter([
174  'uid',
175  'pid',
176  $this->routeFieldName,
177  $this->languageFieldName,
178  $this->languageParentFieldName,
179  ]);
180  }
181 
185  protected function ‪purgeRouteValuePrefix(?string $value): ?string
186  {
187  if (empty($this->routeValuePrefix) || $value === null) {
188  return $value;
189  }
190  return ltrim($value, $this->routeValuePrefix);
191  }
192 
193  protected function ‪findByIdentifier(string $value): ?array
194  {
196  return null;
197  }
198 
199  $queryBuilder = $this->‪createQueryBuilder();
200  $result = $queryBuilder
201  ->select(...$this->persistenceFieldNames)
202  ->where($queryBuilder->expr()->eq(
203  'uid',
204  $queryBuilder->createNamedParameter($value, ‪Connection::PARAM_INT)
205  ))
206  ->executeQuery()
207  ->fetchAssociative();
208  return $result !== false ? $result : null;
209  }
210 
211  protected function ‪findByRouteFieldValue(string $value): ?array
212  {
213  $languageAware = $this->languageFieldName !== null && $this->languageParentFieldName !== null;
214 
215  $queryBuilder = $this->‪createQueryBuilder();
216  $constraints = [
217  $queryBuilder->expr()->eq(
218  $this->routeFieldName,
219  $queryBuilder->createNamedParameter($value)
220  ),
221  ];
222 
223  $languageIds = null;
224  if ($languageAware) {
225  $languageIds = $this->resolveAllRelevantLanguageIds();
226  $constraints[] = $queryBuilder->expr()->in(
227  $this->languageFieldName,
228  $queryBuilder->createNamedParameter($languageIds, ‪Connection::PARAM_INT_ARRAY)
229  );
230  }
231 
232  $results = $queryBuilder
233  ->select(...$this->persistenceFieldNames)
234  ->where(...$constraints)
235  ->executeQuery()
236  ->fetchAllAssociative();
237  // limit results to be contained in rootPageId of current Site
238  // (which is defining the route configuration currently being processed)
239  if ($this->slugUniqueInSite) {
240  $results = array_values($this->filterContainedInSite($results));
241  }
242  // return first result record in case table is not language aware
243  if (!$languageAware) {
244  return $results[0] ?? null;
245  }
246  // post-process language fallbacks
247  return $this->resolveLanguageFallback($results, $this->languageFieldName, $languageIds);
248  }
249 
250  protected function ‪createQueryBuilder(): QueryBuilder
251  {
252  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
253  ->getQueryBuilderForTable($this->tableName)
254  ->from($this->tableName);
255  $queryBuilder->setRestrictions(
256  GeneralUtility::makeInstance(FrontendRestrictionContainer::class, GeneralUtility::makeInstance(Context::class))
257  );
258  // Frontend Groups are not available at this time
259  // So this must be excluded to allow access restricted records
260  $queryBuilder->getRestrictions()->removeByType(FrontendGroupRestriction::class);
261  return $queryBuilder;
262  }
263 
264  protected function ‪resolveOverlay(?array ‪$record): ?array
265  {
266  $languageId = $this->siteLanguage->getLanguageId();
267  if (‪$record === null || $languageId === 0) {
268  return ‪$record;
269  }
270 
271  $pageRepository = $this->‪createPageRepository();
272  return $pageRepository->getLanguageOverlay($this->tableName, ‪$record) ?: null;
273  }
274 
275  protected function ‪createPageRepository(): ‪PageRepository
276  {
277  $context = clone GeneralUtility::makeInstance(Context::class);
278  $context->setAspect(
279  'language',
281  );
282  return GeneralUtility::makeInstance(
283  PageRepository::class,
284  $context
285  );
286  }
287 }
‪TYPO3\CMS\Core\Routing\Aspect\UnresolvedValueInterface
Definition: UnresolvedValueInterface.php:24
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\createPageRepository
‪createPageRepository()
Definition: PersistedAliasMapper.php:267
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Core\Context\LanguageAspectFactory
Definition: LanguageAspectFactory.php:27
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\buildPersistenceFieldNames
‪string[] buildPersistenceFieldNames()
Definition: PersistedAliasMapper.php:163
‪TYPO3\CMS\Core\Routing\Aspect\SiteAccessorTrait
Definition: SiteAccessorTrait.php:31
‪TYPO3\CMS\Core\Site\SiteLanguageAwareInterface
Definition: SiteLanguageAwareInterface.php:26
‪TYPO3\CMS\Core\Context\LanguageAspectFactory\createFromSiteLanguage
‪static createFromSiteLanguage(SiteLanguage $language)
Definition: LanguageAspectFactory.php:31
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendGroupRestriction
Definition: FrontendGroupRestriction.php:30
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$tableName
‪string $tableName
Definition: PersistedAliasMapper.php:65
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$routeFieldName
‪string $routeFieldName
Definition: PersistedAliasMapper.php:69
‪TYPO3\CMS\Core\Site\SiteAwareInterface
Definition: SiteAwareInterface.php:26
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\resolveOverlay
‪resolveOverlay(?array $record)
Definition: PersistedAliasMapper.php:256
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\purgeRouteValuePrefix
‪string purgeRouteValuePrefix(?string $value)
Definition: PersistedAliasMapper.php:177
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Core\Routing\Aspect\SiteLanguageAccessorTrait
Definition: SiteLanguageAccessorTrait.php:26
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$routeValuePrefix
‪string $routeValuePrefix
Definition: PersistedAliasMapper.php:73
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\resolve
‪resolve(string $value)
Definition: PersistedAliasMapper.php:147
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$settings
‪array $settings
Definition: PersistedAliasMapper.php:61
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\findByIdentifier
‪findByIdentifier(string $value)
Definition: PersistedAliasMapper.php:185
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper
Definition: PersistedAliasMapper.php:53
‪TYPO3\CMS\Core\Routing\Aspect
Definition: AspectFactory.php:18
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\generate
‪generate(string $value)
Definition: PersistedAliasMapper.php:132
‪TYPO3\CMS\Core\Routing\Aspect\AspectTrait
Definition: AspectTrait.php:23
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\__construct
‪__construct(array $settings)
Definition: PersistedAliasMapper.php:94
‪TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface
Definition: StaticMappableAspectInterface.php:23
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\createQueryBuilder
‪createQueryBuilder()
Definition: PersistedAliasMapper.php:242
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$slugUniqueInSite
‪bool $slugUniqueInSite
Definition: PersistedAliasMapper.php:89
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$persistenceFieldNames
‪string[] $persistenceFieldNames
Definition: PersistedAliasMapper.php:77
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer
Definition: FrontendRestrictionContainer.php:30
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$languageFieldName
‪string null $languageFieldName
Definition: PersistedAliasMapper.php:81
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Core\Routing\Aspect\PersistedMappableAspectInterface
Definition: PersistedMappableAspectInterface.php:24
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\findByRouteFieldValue
‪findByRouteFieldValue(string $value)
Definition: PersistedAliasMapper.php:203
‪TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper\$languageParentFieldName
‪string null $languageParentFieldName
Definition: PersistedAliasMapper.php:85