Content checks: flag placeholder images instead of counting them as done
Files below fileadmin/placeholders/ (or named "...placeholder...") are grey dummies. PageCheckService and ProductCheckService now report them: - image cells carry `placeholder` = number of placeholder files (only when > 0); `ok` is unchanged (something is attached) - summary.filled counts only cells without placeholders, new summary.placeholder counts the cells that have one - detail images carry `placeholder: true|false` - cards: `placeholder` = cards whose icon/image is a placeholder; record: the record image Additive change to the JSON contract; frontend rendering follows (ticket). Requested by Harry 2026-11-13. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
20bcbeb731
commit
17b08df283
3 files changed
+137
-47
No files matched your search
@@ -24,6 +24,13 @@ use TYPO3\CMS\Extbase\Service\ImageService;
|
||||
* detail(): one product with all texts (RTE HTML as stored), images
|
||||
* (original URL + ~480 px thumbnail), videos, feature cards
|
||||
* with icons, resources, datasheets, solutions.
|
||||
*
|
||||
* Placeholders (Harry 2026-11-13, same rule as PageCheckService): files below
|
||||
* fileadmin/placeholders/ or named "...placeholder..." do not pass as
|
||||
* finished. Image cells carry `placeholder` = number of such files (only when
|
||||
* > 0), summary.filled counts only cells without placeholders and
|
||||
* summary.placeholder the cells that have one; detail images carry
|
||||
* `placeholder: true|false`.
|
||||
*/
|
||||
final class ProductCheckService
|
||||
{
|
||||
@@ -84,20 +91,23 @@ final class ProductCheckService
|
||||
$products = $this->products();
|
||||
$uids = array_map(static fn(array $p): int => (int)$p['uid'], $products);
|
||||
$files = $this->fileCounts($uids);
|
||||
$placeholders = $this->fileCounts($uids, true);
|
||||
$features = $this->featureCounts($uids);
|
||||
$resources = $this->countBy('tx_vitec_product_resource', 'product', 'deleted = 0 AND hidden = 0');
|
||||
$datasheets = $this->mmCounts('tx_vitec_product_datasheet_mm', 'tx_vitec_domain_model_download');
|
||||
$solutions = $this->mmCounts('tx_vitec_product_solution_mm', 'tx_vitec_domain_model_solution');
|
||||
|
||||
$filled = array_fill_keys(array_column(self::FIELDS, 'key'), 0);
|
||||
$placeholder = array_fill_keys(array_column(self::FIELDS, 'key'), 0);
|
||||
$rows = [];
|
||||
foreach ($products as $p) {
|
||||
$uid = (int)$p['uid'];
|
||||
$f = $files[$uid] ?? [];
|
||||
$ph = $placeholders[$uid] ?? [];
|
||||
$checks = [
|
||||
'hero' => $this->check($f['heroimage'] ?? 0),
|
||||
'photos' => $this->check($f['productimage'] ?? 0),
|
||||
'og' => $this->check($f['ogimage'] ?? 0),
|
||||
'hero' => $this->check($f['heroimage'] ?? 0, $ph['heroimage'] ?? 0),
|
||||
'photos' => $this->check($f['productimage'] ?? 0, $ph['productimage'] ?? 0),
|
||||
'og' => $this->check($f['ogimage'] ?? 0, $ph['ogimage'] ?? 0),
|
||||
'video' => $this->check($f['videofile'] ?? 0),
|
||||
'teaser' => $this->check($this->filled($p['teaser']) ? 1 : 0),
|
||||
'subtitle' => $this->check($this->filled($p['subtitle']) ? 1 : 0),
|
||||
@@ -112,7 +122,9 @@ final class ProductCheckService
|
||||
'solutions' => $this->check($solutions[$uid] ?? 0),
|
||||
];
|
||||
foreach ($checks as $key => $c) {
|
||||
if ($c['ok']) {
|
||||
if (($c['placeholder'] ?? 0) > 0) {
|
||||
$placeholder[$key]++;
|
||||
} elseif ($c['ok']) {
|
||||
$filled[$key]++;
|
||||
}
|
||||
}
|
||||
@@ -122,7 +134,7 @@ final class ProductCheckService
|
||||
return [
|
||||
'generatedAt' => date(\DATE_ATOM),
|
||||
'fields' => self::FIELDS,
|
||||
'summary' => ['products' => count($rows), 'filled' => $filled],
|
||||
'summary' => ['products' => count($rows), 'filled' => $filled, 'placeholder' => $placeholder],
|
||||
'products' => $rows,
|
||||
];
|
||||
}
|
||||
@@ -191,9 +203,9 @@ final class ProductCheckService
|
||||
}
|
||||
|
||||
/** @return array{ok: bool, count: int} */
|
||||
private function check(int $count): array
|
||||
private function check(int $count, int $placeholder = 0): array
|
||||
{
|
||||
return ['ok' => $count > 0, 'count' => $count];
|
||||
return ['ok' => $count > 0, 'count' => $count] + ($placeholder > 0 ? ['placeholder' => $placeholder] : []);
|
||||
}
|
||||
|
||||
private function filled(mixed $value): bool
|
||||
@@ -218,18 +230,24 @@ final class ProductCheckService
|
||||
* @param int[] $uids
|
||||
* @return array<int, array<string, int>> [productUid][fieldname] = count
|
||||
*/
|
||||
private function fileCounts(array $uids): array
|
||||
private function fileCounts(array $uids, bool $placeholdersOnly = false): array
|
||||
{
|
||||
if ($uids === []) {
|
||||
return [];
|
||||
}
|
||||
$rows = $this->connection()->executeQuery(
|
||||
"SELECT uid_foreign, fieldname, COUNT(*) AS n FROM sys_file_reference
|
||||
WHERE deleted = 0 AND hidden = 0 AND tablenames = 'tx_vitec_domain_model_product'
|
||||
AND fieldname IN ('heroimage','productimage','ogimage','videofile')
|
||||
AND uid_foreign IN (" . implode(',', array_map('intval', $uids)) . ')
|
||||
GROUP BY uid_foreign, fieldname'
|
||||
)->fetchAllAssociative();
|
||||
$in = implode(',', array_map('intval', $uids));
|
||||
$sql = $placeholdersOnly
|
||||
? "SELECT r.uid_foreign, r.fieldname, COUNT(*) AS n 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')
|
||||
AND r.uid_foreign IN (" . $in . ') AND ' . PageCheckService::PLACEHOLDER_SQL . '
|
||||
GROUP BY r.uid_foreign, r.fieldname'
|
||||
: "SELECT uid_foreign, fieldname, COUNT(*) AS n FROM sys_file_reference
|
||||
WHERE deleted = 0 AND hidden = 0 AND tablenames = 'tx_vitec_domain_model_product'
|
||||
AND fieldname IN ('heroimage','productimage','ogimage','videofile')
|
||||
AND uid_foreign IN (" . $in . ')
|
||||
GROUP BY uid_foreign, fieldname';
|
||||
$rows = $this->connection()->executeQuery($sql)->fetchAllAssociative();
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$out[(int)$r['uid_foreign']][(string)$r['fieldname']] = (int)$r['n'];
|
||||
@@ -241,7 +259,7 @@ final class ProductCheckService
|
||||
* Files of one product with original URL and a ~480 px thumbnail
|
||||
* (SVG and non-images keep the original as thumbnail).
|
||||
*
|
||||
* @return array<string, array<int, array{url: string, thumb: string, name: string, mime: string}>>
|
||||
* @return array<string, array<int, array{url: string, thumb: string, name: string, mime: string, placeholder: bool}>>
|
||||
*/
|
||||
private function files(int $uid): array
|
||||
{
|
||||
@@ -261,6 +279,7 @@ final class ProductCheckService
|
||||
'thumb' => $this->thumbnail((int)$r['uid_local'], (string)$r['mime_type'], $url),
|
||||
'name' => (string)$r['name'],
|
||||
'mime' => (string)$r['mime_type'],
|
||||
'placeholder' => PageCheckService::isPlaceholder((string)$r['identifier'], (string)$r['name']),
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
|
||||
Reference in new issue
Block a user