Success-story migration + new plugins + JSON/UX polish
- Success Stories: full migration from old site (48/48, audited), markets n:n, quotation content block, columns content block, pretty SEO detail URLs via SuccessStoryPathRewrite middleware, list/detail split (list page 4 / story page), detailUrl/backUrl - New plugins: VITEC Locations (grid/list/map + RTE map text), VITEC Customer Logos (color/bw logic, only-show-selected), VITEC Card (one plugin for product/story/market/solution with reloading FlexForm + custom backend preview renderer) - Eventlist: layout dropdown (list/grid/teaserbar) in settings - Hero section CB: Images/Video tabs, background video + overlay - Product JSON: full category rootline (parents), fixed missing ConnectionPool import (all-products crash), category tree map - Backend preview CSS: container-query responsive (narrow columns) - Docs: ISO architecture spec, root + extension READMEs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ use Evomedien\Vitec\Domain\Repository\ProductRepository;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
@@ -406,15 +407,61 @@ class ProductListJsonRenderer
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
return array_map(static function ($cat) {
|
||||
return array_map(function ($cat) {
|
||||
return [
|
||||
'uid' => (int)$cat['uid'],
|
||||
'title' => $cat['title'] ?? '',
|
||||
'description' => $cat['description'] ?? '',
|
||||
'parents' => $this->getCategoryParents((int)$cat['uid']),
|
||||
];
|
||||
}, $categories);
|
||||
}
|
||||
|
||||
/** @var array<int,array{parent:int,title:string}>|null Lazy uid => [parent,title] map of all categories. */
|
||||
private ?array $categoryTreeMap = null;
|
||||
|
||||
/**
|
||||
* All ancestor categories of one category, ROOT FIRST (excluding itself).
|
||||
* Backed by a once-per-request map of sys_category — cheap for any number
|
||||
* of products. Cycle-safe.
|
||||
*
|
||||
* @return array<int,array{uid:int,title:string}>
|
||||
*/
|
||||
private function getCategoryParents(int $categoryUid): array
|
||||
{
|
||||
if ($this->categoryTreeMap === null) {
|
||||
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('sys_category');
|
||||
$rows = $qb
|
||||
->select('uid', 'parent', 'title')
|
||||
->from('sys_category')
|
||||
->where(
|
||||
$qb->expr()->eq('deleted', 0),
|
||||
$qb->expr()->eq('hidden', 0)
|
||||
)
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
$this->categoryTreeMap = [];
|
||||
foreach ($rows as $row) {
|
||||
$this->categoryTreeMap[(int)$row['uid']] = [
|
||||
'parent' => (int)$row['parent'],
|
||||
'title' => (string)$row['title'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$parents = [];
|
||||
$seen = [$categoryUid => true];
|
||||
$current = $this->categoryTreeMap[$categoryUid]['parent'] ?? 0;
|
||||
while ($current > 0 && isset($this->categoryTreeMap[$current]) && !isset($seen[$current])) {
|
||||
$seen[$current] = true;
|
||||
array_unshift($parents, ['uid' => $current, 'title' => $this->categoryTreeMap[$current]['title']]);
|
||||
$current = $this->categoryTreeMap[$current]['parent'];
|
||||
}
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
private function getDownloadFileType(int $downloadUid): string
|
||||
{
|
||||
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
|
||||
Reference in New Issue
Block a user