178 lines
6.2 KiB
PHP
Executable File
178 lines
6.2 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\UserFunc;
|
|
|
|
use Doctrine\DBAL\ParameterType;
|
|
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;
|
|
|
|
/**
|
|
* UserFunc: render the VITEC Card plugin as JSON (headless).
|
|
*
|
|
* ONE plugin for four card types — the FlexForm picks the model type
|
|
* (product | story | market | solution) plus one record; ALL card data
|
|
* comes from the model. Output under content.card:
|
|
*
|
|
* { "modelType": "product", "layout": "vertical", "item": { … } }
|
|
*
|
|
* Image resolution is delegated to UsecaseSerializer::image() (generic,
|
|
* srcset + SVG-safe). `render()` = top-level plugin / page discovery,
|
|
* `renderForRecord()` = one tt_content row (container-nestable).
|
|
* Exception-safe.
|
|
*/
|
|
class ModelcardJsonRenderer
|
|
{
|
|
private const MODEL_TABLES = [
|
|
'product' => 'tx_vitec_domain_model_product',
|
|
'story' => 'tx_vitec_domain_model_usecase',
|
|
'market' => 'tx_vitec_domain_model_market',
|
|
'solution' => 'tx_vitec_domain_model_solution',
|
|
];
|
|
|
|
#[AsAllowedCallable]
|
|
public function render(string $content, array $conf): string
|
|
{
|
|
$row = is_array($this->cObj->data ?? null) ? $this->cObj->data : null;
|
|
if ($row && (string)($row['CType'] ?? '') === 'vitec_modelcard') {
|
|
return $this->renderForRecord($row);
|
|
}
|
|
|
|
$pageId = 0;
|
|
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
|
if ($request !== null) {
|
|
$pageInfo = $request->getAttribute('frontend.page.information');
|
|
if ($pageInfo !== null) {
|
|
$pageId = (int)$pageInfo->getId();
|
|
}
|
|
}
|
|
if ($pageId <= 0) {
|
|
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
|
|
}
|
|
if ($pageId <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
|
|
$ces = $qb
|
|
->select('*')
|
|
->from('tt_content')
|
|
->where(
|
|
$qb->expr()->eq('pid', $qb->createNamedParameter($pageId, ParameterType::INTEGER)),
|
|
$qb->expr()->eq('CType', $qb->createNamedParameter('vitec_modelcard', ParameterType::STRING)),
|
|
$qb->expr()->eq('deleted', 0),
|
|
$qb->expr()->eq('hidden', 0)
|
|
)
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
|
|
if (empty($ces)) {
|
|
return '';
|
|
}
|
|
|
|
return $this->renderForRecord($ces[0]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $contentElement
|
|
*/
|
|
public function renderForRecord(array $contentElement): string
|
|
{
|
|
try {
|
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
|
$settings = $flexFormData['settings'] ?? [];
|
|
|
|
$modelType = (string)($settings['modelType'] ?? 'product');
|
|
$layout = (string)($settings['layout'] ?? 'vertical');
|
|
$debugMode = (bool)($settings['debug'] ?? false);
|
|
$recordUid = (int)($settings[$modelType === 'story' ? 'story' : $modelType] ?? 0);
|
|
|
|
$item = null;
|
|
if ($recordUid > 0 && isset(self::MODEL_TABLES[$modelType])) {
|
|
$row = $this->fetchRecord(self::MODEL_TABLES[$modelType], $recordUid);
|
|
if ($row !== null) {
|
|
$item = $this->serializeItem($modelType, $row);
|
|
}
|
|
}
|
|
|
|
$response = [
|
|
'modelType' => $modelType,
|
|
'layout' => $layout,
|
|
'item' => $item,
|
|
];
|
|
|
|
if ($debugMode) {
|
|
$response['debug'] = ['recordUid' => $recordUid, 'settings' => $settings];
|
|
}
|
|
|
|
return (string)json_encode($response);
|
|
} catch (\Throwable $e) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<string,mixed>|null
|
|
*/
|
|
private function fetchRecord(string $table, int $uid): ?array
|
|
{
|
|
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
|
|
$row = $qb
|
|
->select('*')
|
|
->from($table)
|
|
->where(
|
|
$qb->expr()->eq('uid', $qb->createNamedParameter($uid, ParameterType::INTEGER)),
|
|
$qb->expr()->eq('deleted', 0),
|
|
$qb->expr()->eq('hidden', 0)
|
|
)
|
|
->executeQuery()
|
|
->fetchAssociative();
|
|
|
|
return $row ?: null;
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $row
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function serializeItem(string $modelType, array $row): array
|
|
{
|
|
$serializer = GeneralUtility::makeInstance(UsecaseSerializer::class);
|
|
$uid = (int)$row['uid'];
|
|
|
|
if ($modelType === 'story') {
|
|
// Success stories already have the canonical card serialisation.
|
|
return $serializer->serializeListItem($row);
|
|
}
|
|
|
|
if ($modelType === 'product') {
|
|
return [
|
|
'uid' => $uid,
|
|
'title' => (string)($row['title'] ?? ''),
|
|
'slug' => (string)($row['slug'] ?? ''),
|
|
'subtitle' => (string)($row['subtitle'] ?? ''),
|
|
'teaser' => (string)($row['teaser'] ?? ''),
|
|
'image' => $serializer->image($uid, 'image', 'tx_vitec_domain_model_product', true)
|
|
?? $serializer->image($uid, 'productimage', 'tx_vitec_domain_model_product', true),
|
|
];
|
|
}
|
|
|
|
// market | solution — identical field set.
|
|
return [
|
|
'uid' => $uid,
|
|
'title' => (string)($row['title'] ?? ''),
|
|
'slug' => (string)($row['slug'] ?? ''),
|
|
'subtitle' => (string)($row['subtitle'] ?? ''),
|
|
'teaser' => (string)($row['teaser'] ?? ''),
|
|
'description' => RteResolver::html($row['description'] ?? ''),
|
|
'image' => $serializer->image($uid, 'image', self::MODEL_TABLES[$modelType], true),
|
|
];
|
|
}
|
|
}
|