Im Moment implementiere ich eine Extension, bei der es unter anderem digitale Produkte gibt. Die Produkte können jeweils von einem Typ sein – z.B. Kurs oder Tutorial. Der Kunde hat sich für die Produkte sprechende URLs gewünscht, die den Produkttyp enthalten – z.B. /kurs/produkt-1 oder /tutorial/produkt-2. Da schien es mir sinnvoll, pro Produkt-Typ eine separate Detailseite anzugelen, deren Slug entsprechend zu setzen und in der Site Config hübsche URLs zu konfigurieren.
Nun gibt es folgendes Problem: die Produkt-Typen stehen in der Datenbank und können erweitert werden. Die Site Config hingegen ist ja dateibasiert und wird versioniert. Bei der Konfiguration der hübschen URLs kann mit limitToPages
angegeben werden, für welche Seiten die URLs angewendet werden. Mal absehen davon, dass die IDs der Detailseiten sich lokal, auf Stage und Live durchaus unterscheiden können, wäre es nicht schön, wenn limitToPages
dynamisch aus der Datenbank ermittelt werden könnte.
Seit TYPO3 10.3. gibt es die Möglichkeit, in der Site Config Placeholder zu verwenden und diese mit eingenen Prozessoren zu verarbeiten. In der Dokumentation findet sich ein Beispiel: YAML API.
Und so sieht man Implementierung aus. In der Site Configuration config.yaml gebe ich die Platzhalter an:
limitToPages: '%detailPid(product)%'
In LocalConfiguration.php wird der Processor registriert:
<?php [...] 'yamlLoader' => [ 'placeholderProcessors' => [ \Example\MyExtension\Configuration\Processor\DetailPagePlaceholderProcessor::class => [ 'after' => [ \TYPO3\CMS\Core\Configuration\Processor\Placeholder\ValueFromReferenceArrayProcessor::class ] ] ] ], [...]
Und das ist der Processor:
<?php declare(strict_types=1); namespace Example\MyExtension\Configuration\Processor; use TYPO3\CMS\Core\Configuration\Processor\Placeholder\PlaceholderProcessorInterface; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Utility\GeneralUtility; class DetailPagePlaceholderProcessor implements PlaceholderProcessorInterface { public function canProcess(string $placeholder, array $referenceArray): bool { return strpos($placeholder, '%detailPid(') !== false; } public function process(string $value, array $referenceArray) { $detailPages = $this->getDetailPages(); // Throw this exception if the placeholder can't be substituted if (empty($detailPages)) { throw new \UnexpectedValueException('No detail page found', 1692345790); } return $detailPages; } private function getDetailPages() { $table = 'tx_myextension_domain_model_type'; $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); $queryBuilder ->getRestrictions() ->removeAll() ->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $result = $queryBuilder ->select('*') ->from($table) ->where( $queryBuilder->expr()->gt('detail_pid', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)) ) ->execute() ->fetchAllAssociative(); $detailPid = []; foreach ($result as $row) { $detailPid[] = $row['detail_pid']; } return array_unique($detailPid); } }
Ich will nicht ausschließen, dass das Beispiel nicht funktioniert, denn das ist ein angepasster Demo-Code, der keine tatsächlichen Kundennamen oder Extension-Namen enthält.
Neueste Kommentare