The product audit of public/product-check/ becomes data for the React frontend: new content element vitec_productcheck (plugin registration, wizard entry, TypoScript USER func) delivers under content.productcheck either the overview (every product with one {ok,count} check per field - hero, photos, og, video, teaser, subtitle, description, description2, highlights, capabilities, applications, features incl. icon count, resources, datasheets, solutions - plus per-field summary) or, with ?check=<uid>, the detail of one product (texts as stored HTML, images with original URL and 480px thumbnail, videos, feature cards with icons, resources, datasheets, solutions). ProductCheckService ports the PDO queries of the standalone page to Doctrine; the renderer is always USER_INT so the audit is never served from the page cache; check is excluded from the cHash. Migration adds the element to page 607 (/products/product-check). The standalone page stays untouched. Verified on DEV: overview 85 products in 0.13 s warm, detail 0.2-0.9 s, thumbnails resolve.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
60 lines
2.3 KiB
PHP
60 lines
2.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\UserFunc;
|
|
|
|
use Evomedien\Vitec\Service\ProductCheckService;
|
|
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
|
|
|
/**
|
|
* JSON for the content element `vitec_productcheck` (page /products/
|
|
* product-check): the product content audit, rendered by the React front
|
|
* end instead of the standalone page public/product-check/.
|
|
*
|
|
* Emitted under content.productcheck:
|
|
*
|
|
* without query {mode: "overview", generatedAt, fields[], summary, products[]}
|
|
* ?check=<uid> {mode: "detail", product: {…} | null}
|
|
* on failure {mode: "error", error}
|
|
*
|
|
* Always USER_INT: the audit must show the database as it is right now,
|
|
* so neither the TYPO3 page cache nor the `check` variant may stick.
|
|
* `check` is excluded from the cHash in ext_localconf.php. The front end
|
|
* is expected to load the detail variant client-side from /_json (one
|
|
* request per opened row), not to navigate the SSR page with the query.
|
|
*/
|
|
final class ProductCheckJsonRenderer
|
|
{
|
|
protected ?ContentObjectRenderer $cObj = null;
|
|
|
|
public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
|
|
{
|
|
$this->cObj = $cObj;
|
|
}
|
|
|
|
#[AsAllowedCallable]
|
|
public function render(string $content, array $conf): string
|
|
{
|
|
if ($this->cObj !== null && $this->cObj->getUserObjectType() === ContentObjectRenderer::OBJECTTYPE_USER) {
|
|
$this->cObj->convertToUserIntObject();
|
|
return '';
|
|
}
|
|
try {
|
|
$service = GeneralUtility::makeInstance(ProductCheckService::class);
|
|
$queryParams = $GLOBALS['TYPO3_REQUEST']?->getQueryParams() ?? [];
|
|
$check = (int)($queryParams['check'] ?? 0);
|
|
if ($check > 0) {
|
|
$payload = ['mode' => 'detail', 'product' => $service->detail($check)];
|
|
} else {
|
|
$payload = ['mode' => 'overview'] + $service->overview();
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$payload = ['mode' => 'error', 'error' => $e->getMessage()];
|
|
}
|
|
return (string)json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
|
|
}
|
|
}
|