Product import
This commit is contained in:
1 parent
7442d10e68
commit
7833e1b668
21 files changed
+763
-185
No files matched your search
@@ -144,7 +144,7 @@ final class ProductXlsxReader
|
||||
*/
|
||||
public function sources(array $parsed): array
|
||||
{
|
||||
$groups = ['Meta' => [], 'Components' => [], 'Page SEO Check' => []];
|
||||
$groups = ['Meta' => [], 'Components' => [], 'Composed' => [], 'Page SEO Check' => []];
|
||||
|
||||
foreach ($parsed['meta'] ?? [] as $key => $value) {
|
||||
$groups['Meta'][] = $this->entry('m:' . $key, 'Meta · ' . $key, (string)$value);
|
||||
@@ -169,6 +169,47 @@ final class ProductXlsxReader
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
// Section-intro rows ("... — H2") additionally as ONE ready RTE
|
||||
// value: heading plus paragraph in the exact markup the finished
|
||||
// MGW-Diamond product stores in `textrelatedproducts` - imported
|
||||
// and hand-written intros then render identically.
|
||||
if (str_ends_with($name, '— H2')) {
|
||||
$pair = $this->composePair($component);
|
||||
if ($pair !== null) {
|
||||
$groups['Composed'][] = $this->entry(
|
||||
'p:' . $name . '#' . $component['occurrence'],
|
||||
$name . $suffix . ' · Title+Body as HTML',
|
||||
$pair
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Composed sources: a component occurring several times can be pulled
|
||||
// as ONE value - every occurrence as <h3> + bullets/paragraph, prefixed
|
||||
// with the section heading. Built for the Key Capability Groups, whose
|
||||
// three rows land in the single `capabilities` field; that is the shape
|
||||
// the finished product pages already use.
|
||||
$counts = [];
|
||||
foreach ($parsed['components'] ?? [] as $component) {
|
||||
$counts[(string)$component['component']] = ((int)($component['occurrence'] ?? 1));
|
||||
}
|
||||
foreach ($counts as $name => $count) {
|
||||
if ($count < 2 || in_array($name, self::HIDDEN_COMPONENTS, true)) {
|
||||
continue;
|
||||
}
|
||||
$html = $this->composeGroup($parsed, $name);
|
||||
if ($html !== null) {
|
||||
$groups['Composed'][] = $this->entry('g:' . $name, $name . ' · all ' . $count . ' as HTML', $html);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-component composite for the SEO title: hero title, em dash,
|
||||
// introduction heading - "Aligo — High-performance AV over IP and KVM".
|
||||
$seoTitle = $this->composeSeoTitle($parsed);
|
||||
if ($seoTitle !== null) {
|
||||
$groups['Composed'][] = $this->entry('x:seotitle', 'Hero title — Intro heading (SEO title)', $seoTitle);
|
||||
}
|
||||
|
||||
foreach ($parsed['seo'] ?? [] as $header => $value) {
|
||||
@@ -191,6 +232,20 @@ final class ProductXlsxReader
|
||||
$value = $parsed['meta'][substr($selector, 2)] ?? null;
|
||||
return is_string($value) && $value !== '' ? $value : null;
|
||||
}
|
||||
if (str_starts_with($selector, 'g:')) {
|
||||
return $this->composeGroup($parsed, substr($selector, 2));
|
||||
}
|
||||
if (str_starts_with($selector, 'p:') && preg_match('/^p:(.+)#(\d+)$/', $selector, $m)) {
|
||||
foreach ($parsed['components'] ?? [] as $component) {
|
||||
if ((string)$component['component'] === $m[1] && (int)$component['occurrence'] === (int)$m[2]) {
|
||||
return $this->composePair($component);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ($selector === 'x:seotitle') {
|
||||
return $this->composeSeoTitle($parsed);
|
||||
}
|
||||
if (str_starts_with($selector, 's:')) {
|
||||
$value = $parsed['seo'][substr($selector, 2)] ?? null;
|
||||
return is_string($value) && $value !== '' ? $value : null;
|
||||
@@ -206,6 +261,121 @@ final class ProductXlsxReader
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* SEO title composite: the hero title (the product name), an em dash, and
|
||||
* the introduction heading. Plain text - `seotitle` is a plain input, no
|
||||
* escaping wanted here.
|
||||
*
|
||||
* @param array<string,mixed> $parsed
|
||||
*/
|
||||
private function composeSeoTitle(array $parsed): ?string
|
||||
{
|
||||
$hero = null;
|
||||
$intro = null;
|
||||
foreach ($parsed['components'] ?? [] as $component) {
|
||||
$name = (string)($component['component'] ?? '');
|
||||
if ($name === 'Hero — H1' && (int)($component['occurrence'] ?? 1) === 1) {
|
||||
$hero = trim((string)($component['title'] ?? ''));
|
||||
} elseif ($name === 'Product Introduction' && (int)($component['occurrence'] ?? 1) === 1) {
|
||||
$intro = trim((string)($component['title'] ?? ''));
|
||||
}
|
||||
}
|
||||
if ($hero === null || $hero === '' || $intro === null || $intro === '') {
|
||||
return null;
|
||||
}
|
||||
return $hero . ' — ' . $intro;
|
||||
}
|
||||
|
||||
/**
|
||||
* One component row as a heading/paragraph pair. The markup - centred h3
|
||||
* with an inner span, centred p - is copied verbatim from what the
|
||||
* finished MGW-Diamond product carries in `textrelatedproducts`, so the
|
||||
* front end renders imported intros exactly like the hand-made one.
|
||||
*
|
||||
* @param array<string,mixed> $component
|
||||
*/
|
||||
private function composePair(array $component): ?string
|
||||
{
|
||||
$title = trim((string)($component['title'] ?? ''));
|
||||
$body = trim((string)($component['body'] ?? ''));
|
||||
if ($body === '') {
|
||||
$body = trim((string)($component['cta'] ?? ''));
|
||||
}
|
||||
$body = trim((string)preg_replace('/\|\s*CTA:.*$/su', '', $body));
|
||||
if ($title === '' || $body === '') {
|
||||
return null;
|
||||
}
|
||||
return '<h3 class="text-center"><span>' . htmlspecialchars($title, ENT_QUOTES) . '</span></h3>' . "\n"
|
||||
. '<p class="text-center">' . htmlspecialchars($body, ENT_QUOTES) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* All occurrences of one component, composed into a single HTML block in
|
||||
* the exact markup the hand-made MGW-Diamond `capabilities` field uses
|
||||
* (the front end styles only that shape): the immediately preceding
|
||||
* "... — H2" row (found by ORDER, not by name - "Key Capability Group" vs
|
||||
* "Key Capabilities — H2" makes name matching fragile) becomes
|
||||
* <h3 class="text-center"><span>, each occurrence's title a
|
||||
* <p><strong> group heading, and a body whose text uses "•" bullets
|
||||
* becomes a <ul>. Plain text otherwise.
|
||||
*
|
||||
* @param array<string,mixed> $parsed
|
||||
*/
|
||||
private function composeGroup(array $parsed, string $name): ?string
|
||||
{
|
||||
$components = $parsed['components'] ?? [];
|
||||
$members = array_values(array_filter(
|
||||
$components,
|
||||
static fn(array $c): bool => (string)($c['component'] ?? '') === $name
|
||||
));
|
||||
if ($members === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$html = '';
|
||||
$firstOrder = (int)($members[0]['order'] ?? 0);
|
||||
foreach ($components as $component) {
|
||||
if ((int)($component['order'] ?? 0) === $firstOrder - 1
|
||||
&& str_ends_with((string)($component['component'] ?? ''), '— H2')
|
||||
&& trim((string)($component['title'] ?? '')) !== ''
|
||||
) {
|
||||
$html .= '<h3 class="text-center"><span>'
|
||||
. htmlspecialchars(trim((string)$component['title']), ENT_QUOTES)
|
||||
. '</span></h3>' . "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($members as $member) {
|
||||
$title = trim((string)($member['title'] ?? ''));
|
||||
$body = trim((string)($member['body'] ?? ''));
|
||||
if ($body === '') {
|
||||
$body = trim((string)($member['cta'] ?? ''));
|
||||
}
|
||||
// Card copy carries a "| CTA: <label>" suffix; the label is
|
||||
// display-only everywhere else and would be junk inside a <p>.
|
||||
$body = trim((string)preg_replace('/\|\s*CTA:.*$/su', '', $body));
|
||||
if ($title !== '') {
|
||||
$html .= '<p><strong>' . htmlspecialchars($title, ENT_QUOTES) . '</strong></p>';
|
||||
}
|
||||
if ($body === '') {
|
||||
continue;
|
||||
}
|
||||
if (str_contains($body, '•')) {
|
||||
$items = array_filter(array_map('trim', explode('•', $body)));
|
||||
$html .= '<ul>';
|
||||
foreach ($items as $item) {
|
||||
$html .= '<li>' . htmlspecialchars($item, ENT_QUOTES) . '</li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
} else {
|
||||
$html .= '<p>' . htmlspecialchars($body, ENT_QUOTES) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
return $html !== '' ? $html : null;
|
||||
}
|
||||
|
||||
/** @param array<int,array<string,mixed>> $components */
|
||||
private function detectPageType(array $components): string
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user