diff --git a/packages/vitec/Classes/Domain/Model/Solution.php b/packages/vitec/Classes/Domain/Model/Solution.php index c478ae5..02db470 100644 --- a/packages/vitec/Classes/Domain/Model/Solution.php +++ b/packages/vitec/Classes/Domain/Model/Solution.php @@ -18,6 +18,13 @@ class Solution extends AbstractEntity */ protected $slug = ''; + /** + * Page uid presenting this solution (TCA type "group", allowed: pages). + * + * @var int + */ + protected $detailPage = 0; + /** * @var string */ @@ -86,6 +93,22 @@ class Solution extends AbstractEntity $this->slug = $slug; } + /** + * @return int + */ + public function getDetailPage(): int + { + return $this->detailPage; + } + + /** + * @param int $detailPage + */ + public function setDetailPage(int $detailPage): void + { + $this->detailPage = $detailPage; + } + /** * @return string */ diff --git a/packages/vitec/Classes/Service/LinkResolver.php b/packages/vitec/Classes/Service/LinkResolver.php new file mode 100644 index 0000000..cfc2a46 --- /dev/null +++ b/packages/vitec/Classes/Service/LinkResolver.php @@ -0,0 +1,48 @@ +typoLink_URL(['parameter' => (string)$pageUid]); + + return $url !== '' ? $url : null; + } catch (\Throwable $e) { + return null; + } + } +} diff --git a/packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php b/packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php index 2a17bae..8af115b 100644 --- a/packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php @@ -5,13 +5,13 @@ declare(strict_types=1); namespace Evomedien\Vitec\UserFunc; use Doctrine\DBAL\ParameterType; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\RteResolver; use Evomedien\Vitec\Service\UsecaseSerializer; use TYPO3\CMS\Core\Attribute\AsAllowedCallable; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; /** * UserFunc: render all VITEC markets as JSON (headless). @@ -168,29 +168,8 @@ class MarketListJsonRenderer 'subtitle' => (string)($r['subtitle'] ?? ''), 'teaser' => (string)($r['teaser'] ?? ''), 'description' => RteResolver::html($r['description'] ?? ''), - 'detailUrl' => $this->detailUrl((int)($r['detail_page'] ?? 0)), + 'detailUrl' => LinkResolver::pageUrl((int)($r['detail_page'] ?? 0)), 'image' => $serializer->image($uid, 'image', self::TABLE, true), ]; } - - /** - * Resolve the `detail_page` uid to a URL. The frontend cannot do anything - * with a raw page uid, so the link is built server side — same convention - * as `headerLink` and LocationsJsonRenderer. - * - * Returns null when no page is set or the link cannot be resolved. - */ - private function detailUrl(int $pageUid): ?string - { - if ($pageUid <= 0) { - return null; - } - try { - $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $url = $cObj->typoLink_URL(['parameter' => (string)$pageUid]); - return $url !== '' ? $url : null; - } catch (\Throwable $e) { - return null; - } - } } diff --git a/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php index 980088b..88ef70d 100755 --- a/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php @@ -10,10 +10,10 @@ use Doctrine\DBAL\ParameterType; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Service\FlexFormService; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\RteResolver; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; -use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; /** * UserFunc to render a single market as JSON for headless output. @@ -161,7 +161,7 @@ class MarketShowJsonRenderer 'subtitle' => (string)($market['subtitle'] ?? ''), 'teaser' => (string)($market['teaser'] ?? ''), 'description' => RteResolver::html($market['description'] ?? ''), - 'detailUrl' => $this->detailUrl((int)($market['detail_page'] ?? 0)), + 'detailUrl' => LinkResolver::pageUrl((int)($market['detail_page'] ?? 0)), 'categories' => $this->getMarketCategories($uid), 'image' => $this->getMarketImage($uid), ]; @@ -271,25 +271,4 @@ class MarketShowJsonRenderer ]; }, $categories); } - - /** - * Resolve the `detail_page` uid to a URL. The frontend cannot do anything - * with a raw page uid, so the link is built server side — same convention - * as `headerLink` and LocationsJsonRenderer. - * - * Returns null when no page is set or the link cannot be resolved. - */ - private function detailUrl(int $pageUid): ?string - { - if ($pageUid <= 0) { - return null; - } - try { - $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $url = $cObj->typoLink_URL(['parameter' => (string)$pageUid]); - return $url !== '' ? $url : null; - } catch (\Throwable $e) { - return null; - } - } } diff --git a/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php b/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php index 58638c6..365c1ad 100755 --- a/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Evomedien\Vitec\UserFunc; use Doctrine\DBAL\ParameterType; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\RteResolver; use Evomedien\Vitec\Service\UsecaseSerializer; use TYPO3\CMS\Core\Attribute\AsAllowedCallable; @@ -163,7 +164,7 @@ class ModelcardJsonRenderer ]; } - // market | solution — identical field set. + // market | solution — identical field set, both carry `detail_page`. return [ 'uid' => $uid, 'title' => (string)($row['title'] ?? ''), @@ -171,6 +172,7 @@ class ModelcardJsonRenderer 'subtitle' => (string)($row['subtitle'] ?? ''), 'teaser' => (string)($row['teaser'] ?? ''), 'description' => RteResolver::html($row['description'] ?? ''), + 'detailUrl' => LinkResolver::pageUrl((int)($row['detail_page'] ?? 0)), 'image' => $serializer->image($uid, 'image', self::MODEL_TABLES[$modelType], true), ]; } diff --git a/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php index 2757157..de83c5e 100755 --- a/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\ParameterType; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Service\FlexFormService; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\RteResolver; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; @@ -160,6 +161,7 @@ class SolutionShowJsonRenderer 'subtitle' => (string)($solution['subtitle'] ?? ''), 'teaser' => (string)($solution['teaser'] ?? ''), 'description' => RteResolver::html($solution['description'] ?? ''), + 'detailUrl' => LinkResolver::pageUrl((int)($solution['detail_page'] ?? 0)), 'categories' => $this->getSolutionCategories($uid), 'image' => $this->getSolutionImage($uid), ]; diff --git a/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php b/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php index 59df63a..47b9df2 100755 --- a/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Evomedien\Vitec\UserFunc; use Doctrine\DBAL\ParameterType; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\UsecaseSerializer; use TYPO3\CMS\Core\Attribute\AsAllowedCallable; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -146,14 +147,14 @@ class UsecaseListJsonRenderer } } - /** Resolve a page uid to its frontend path (route enhancer aware). */ + /** + * Resolve a page uid to its frontend path (route enhancer aware). + * + * Callers here build paths by concatenation, so the empty string stays the + * "no link" value; LinkResolver's null is coalesced away. + */ private function resolvePageUrl(int $pageUid): string { - try { - $cObj = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class); - return (string)$cObj->typoLink_URL(['parameter' => (string)$pageUid]); - } catch (\Throwable $e) { - return ''; - } + return LinkResolver::pageUrl($pageUid) ?? ''; } } diff --git a/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php index 3143e20..f9bc676 100755 --- a/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Evomedien\Vitec\UserFunc; use Doctrine\DBAL\ParameterType; +use Evomedien\Vitec\Service\LinkResolver; use Evomedien\Vitec\Service\UsecaseSerializer; use TYPO3\CMS\Core\Attribute\AsAllowedCallable; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -153,14 +154,14 @@ class UsecaseShowJsonRenderer return (int)($row['uid'] ?? 0); } - /** Resolve a page uid to its frontend path (route enhancer aware). */ + /** + * Resolve a page uid to its frontend path (route enhancer aware). + * + * Callers here build paths by concatenation, so the empty string stays the + * "no link" value; LinkResolver's null is coalesced away. + */ private function resolvePageUrl(int $pageUid): string { - try { - $cObj = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class); - return (string)$cObj->typoLink_URL(['parameter' => (string)$pageUid]); - } catch (\Throwable $e) { - return ''; - } + return LinkResolver::pageUrl($pageUid) ?? ''; } } diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php index d481f56..19d2cb9 100644 --- a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php @@ -24,7 +24,7 @@ return [ ], 'types' => [ '1' => [ - 'showitem' => 'title, slug, subtitle, teaser, description, image, + 'showitem' => 'title, slug, detail_page, subtitle, teaser, description, image, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime', @@ -129,6 +129,17 @@ return [ 'eval' => 'uniqueInPid', ], ], + 'detail_page' => [ + 'exclude' => true, + 'label' => 'Detail Page', + 'description' => 'Page that presents this solution. Resolved to a URL in the JSON output; leave empty for no link.', + 'config' => [ + 'type' => 'group', + 'allowed' => 'pages', + 'size' => 1, + 'maxitems' => 1, + ], + ], 'subtitle' => [ 'exclude' => true, 'label' => 'Subtitle', diff --git a/packages/vitec/ext_tables.sql b/packages/vitec/ext_tables.sql index 8019ba0..dde4e9a 100755 --- a/packages/vitec/ext_tables.sql +++ b/packages/vitec/ext_tables.sql @@ -155,6 +155,7 @@ CREATE TABLE tx_vitec_domain_model_solution ( l10n_diffsource mediumblob, title varchar(255) DEFAULT '' NOT NULL, slug varchar(255) DEFAULT '' NOT NULL, + detail_page int(11) DEFAULT '0' NOT NULL, subtitle varchar(255) DEFAULT '' NOT NULL, teaser varchar(255) DEFAULT '' NOT NULL, description text,