PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
// ---- data -------------------------------------------------------------------
$products = $pdo->query(
"SELECT uid, title, slug, teaser, subtitle, description, description2, highlights,
capabilities, applications, resources, resources_layout,
hidden, legacy, supportproduct, subproduct, hideonwebsite, hideonproducts, showdatapath
FROM tx_vitec_domain_model_product WHERE deleted = 0 ORDER BY LOWER(title), uid"
)->fetchAll();
// FAL references per product and fieldname, with file identifiers
$fileRefs = $pdo->query(
"SELECT r.uid_foreign, r.fieldname, f.identifier, f.name
FROM sys_file_reference r JOIN sys_file f ON f.uid = r.uid_local
WHERE r.deleted = 0 AND r.hidden = 0 AND r.tablenames = 'tx_vitec_domain_model_product'
AND r.fieldname IN ('heroimage','productimage','ogimage','videofile')
ORDER BY r.uid_foreign, r.fieldname, r.sorting_foreign"
)->fetchAll();
$files = [];
foreach ($fileRefs as $r) {
$files[(int)$r['uid_foreign']][$r['fieldname']][] = ['url' => '/fileadmin' . $r['identifier'], 'name' => (string)$r['name']];
}
// inline resource cards
$resourceRows = $pdo->query(
'SELECT * FROM tx_vitec_product_resource WHERE deleted = 0 AND hidden = 0 ORDER BY product, sorting'
)->fetchAll();
$resources = [];
foreach ($resourceRows as $r) {
$resources[(int)$r['product']][] = $r;
}
// MM: datasheets (download records) and solutions, in MM order
$datasheets = [];
foreach ($pdo->query(
'SELECT mm.uid_local AS product, d.title FROM tx_vitec_product_datasheet_mm mm
JOIN tx_vitec_domain_model_download d ON d.uid = mm.uid_foreign AND d.deleted = 0
ORDER BY mm.uid_local, mm.sorting'
)->fetchAll() as $r) {
$datasheets[(int)$r['product']][] = (string)$r['title'];
}
$solutions = [];
foreach ($pdo->query(
'SELECT mm.uid_local AS product, s.title FROM tx_vitec_product_solution_mm mm
JOIN tx_vitec_domain_model_solution s ON s.uid = mm.uid_foreign AND s.deleted = 0
ORDER BY mm.uid_local, mm.sorting'
)->fetchAll() as $r) {
$solutions[(int)$r['product']][] = (string)$r['title'];
}
// inline feature cards (Why choose ...?) + their icon references
$features = [];
foreach ($pdo->query(
'SELECT uid, product, title, cardtext FROM tx_vitec_product_feature
WHERE deleted = 0 AND hidden = 0 ORDER BY product, sorting, uid'
)->fetchAll() as $r) {
$features[(int)$r['product']][] = $r;
}
$featureIcons = [];
foreach ($pdo->query(
"SELECT r.uid_foreign, f.identifier, f.name
FROM sys_file_reference r JOIN sys_file f ON f.uid = r.uid_local
WHERE r.deleted = 0 AND r.hidden = 0 AND r.tablenames = 'tx_vitec_product_feature'
AND r.fieldname = 'icon'
ORDER BY r.uid_foreign, r.sorting_foreign"
)->fetchAll() as $r) {
$featureIcons[(int)$r['uid_foreign']] ??= ['url' => '/fileadmin' . $r['identifier'], 'name' => (string)$r['name']];
}
// ---- helpers ------------------------------------------------------------------
function h(string $v): string
{
return htmlspecialchars($v, ENT_QUOTES | ENT_HTML5);
}
/** check/cross cell; a count > 1 is shown next to the check */
function cell(bool $ok, int $count = 0): string
{
if (!$ok) {
return '
✗
';
}
return '
✓' . ($count > 1 ? '' . $count . '' : '') . '
';
}
function filled(?string $v): bool
{
return trim(strip_tags((string)$v)) !== '';
}
/** labeled block in the detail pane; HTML of RTE fields is rendered */
function textBlock(string $label, ?string $value): string
{
if (!filled($value)) {
return '';
}
return '