cObj` dynamically. The renderer is * handed over through this setter, and only when the method exists at all - * ContentObjectRenderer::callUserFunction() duck-types it with * `is_callable([$classObj, 'setContentObjectRenderer'])`. Without the method * `$this->cObj` stays null and the userFunc never sees its own record. */ public function setContentObjectRenderer(ContentObjectRenderer $cObj): void { $this->cObj = $cObj; } #[AsAllowedCallable] public function render(string $content, array $conf): string { try { $row = is_array($this->cObj?->data ?? null) ? $this->cObj->data : null; if ($row === null) { return ''; } $uid = (int)($row['uid'] ?? 0); if ($uid <= 0) { return ''; } // No shortcut on the tx_vitec_bg_image counter: sys_file_reference is // the single source of truth, and a counter that is stale or missing // (schema not updated yet) would silently swallow a set image. One // indexed query per container is cheaper than that class of bug. $image = GeneralUtility::makeInstance(UsecaseSerializer::class) ->image($uid, 'tx_vitec_bg_image', 'tt_content'); if ($image === null) { return ''; } return (string)json_encode([ 'image' => $image, 'size' => $this->resolveSize($row), 'position' => (string)($row['tx_vitec_bg_position'] ?? 'center center'), ]); } catch (\Throwable $e) { return ''; } } /** * The stored size, resolved into something the front end can hand straight * to CSS. `custom` is the only value that needs a second field; turning it * into "80%" here keeps the promise that `size` is always usable as-is, * instead of making every consumer look up a companion field. * * A custom size without a usable percentage falls back to `auto` - the CSS * default - rather than emitting "0%" or the meaningless word "custom". * * @param array $row */ private function resolveSize(array $row): string { $size = (string)($row['tx_vitec_bg_size'] ?? 'cover'); if ($size !== 'custom') { return $size; } $percent = (int)($row['tx_vitec_bg_size_percent'] ?? 0); return $percent > 0 ? $percent . '%' : 'auto'; } }