cObj stays null and the element * path cannot see which record it is rendering. */ final class MegamenuJsonRenderer { private const CTYPE = 'evomegamenujson_megamenu'; protected ?ContentObjectRenderer $cObj = null; public function setContentObjectRenderer(ContentObjectRenderer $cObj): void { $this->cObj = $cObj; } /** * @param array $conf TypoScript: contentUid, storagePid */ #[AsAllowedCallable] public function render(string $content, array $conf): string { try { $cObj = $this->cObj ?? GeneralUtility::makeInstance(ContentObjectRenderer::class); $row = null; $uid = (int)($conf['contentUid'] ?? 0); if ($uid <= 0) { $data = is_array($cObj->data ?? null) ? $cObj->data : []; if ((string)($data['CType'] ?? '') === self::CTYPE) { // rendering the element itself $row = $data; $uid = (int)($data['uid'] ?? 0); } } if ($uid <= 0 && (int)($conf['storagePid'] ?? 0) > 0) { $uid = $this->firstElementOn((int)$conf['storagePid']); } if ($uid <= 0) { return ''; } $menu = GeneralUtility::makeInstance(MenuBuilder::class)->build($uid, $cObj, $row); return (string)json_encode($menu, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } catch (\Throwable) { // A broken menu must never take the page payload down with it. return ''; } } /** uid of the first megamenu element on a storage folder, 0 when none. */ private function firstElementOn(int $pid): int { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content'); $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); $uid = $queryBuilder->select('uid')->from('tt_content') ->where( $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, ParameterType::INTEGER)), $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter(self::CTYPE)) ) ->orderBy('sorting', 'ASC') ->setMaxResults(1) ->executeQuery()->fetchOne(); return is_numeric($uid) ? (int)$uid : 0; } }