Closes two annex items that turned out to be the same defect seen from two
sides: markets could link to a detail page, solutions could not, and the code
that turns a page uid into a URL existed four times over.
detail_page for Solution (B-11)
- ext_tables.sql, TCA (group on pages, maxitems 1, in showitem behind slug)
and Domain\Model\Solution mirror the Market field exactly
- SolutionShowJsonRenderer emits detailUrl
- ModelcardJsonRenderer serialized no detailUrl at all: its shared
market|solution branch never carried the field. Market modelcards therefore
gain a link here too, not just solutions.
Service\LinkResolver (B-12)
- one implementation replaces four private copies (Market list/show, Usecase
list/show). The copies had drifted: Market returned null on failure, Usecase
an empty string. The contract is now explicit -- null means "no link".
- Usecase keeps a thin resolvePageUrl() wrapper appending ?? '', because it
concatenates paths and a null would tear the strings apart.
- LocationsJsonRenderer and NewsJsonRenderer stay out on purpose: they resolve
a full parameter construct resp. slug paths plus canonical, which is not
page-uid-to-URL and does not fit the same contract.
Requires on the server, the column exists in code only:
vendor/bin/typo3 database:updateschema "*.add,*.change"
vendor/bin/typo3 cache:flush
49 lines
1.6 KiB
PHP
49 lines
1.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\Service;
|
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
|
|
|
/**
|
|
* Resolves a page uid to its frontend URL.
|
|
*
|
|
* A headless renderer cannot hand a raw page uid to the frontend — React has no
|
|
* way to turn `12` into a path. Every field that carries a link (`detailUrl`,
|
|
* `headerLink`, the usecase list's item links) is therefore resolved server side.
|
|
*
|
|
* The resolution goes through `typoLink_URL`, so route enhancers and the slug of
|
|
* the target page are honoured; building the path by hand would bypass both.
|
|
*
|
|
* This lived as a private copy in four renderers (Market list/show, Usecase
|
|
* list/show). The copies had drifted in their failure handling — two returned
|
|
* null, two an empty string — which is why the contract is spelled out here:
|
|
* **null means "no link"**, and callers that need a string coalesce it.
|
|
*/
|
|
final class LinkResolver
|
|
{
|
|
/**
|
|
* Returns null when no page is set or the link cannot be resolved.
|
|
*
|
|
* Never throws: a broken link must not take down the whole JSON response,
|
|
* so a failure downgrades to "no link" like an empty field would.
|
|
*/
|
|
public static function pageUrl(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;
|
|
}
|
|
}
|
|
}
|