SEO-Schema-Helper: Breadcrumb, FAQPage, SportsTeam
breadcrumb_schema(), faq_schema(), page_schema() und team_schema() bauen JSON-LD-Knoten für $meta['schema']. team_schema verweist via #club auf den Organization-Knoten. Leere FAQ-Paare werden übersprungen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
This commit is contained in:
@@ -143,3 +143,86 @@ function form_token_valid(string $token, int $min = 3, int $max = 7200): bool
|
|||||||
$age = time() - (int) $ts;
|
$age = time() - (int) $ts;
|
||||||
return $age >= $min && $age <= $max;
|
return $age >= $min && $age <= $max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BreadcrumbList-Knoten: $items = [['name'=>…, 'slug'=>…], …] (Reihenfolge = Pfad).
|
||||||
|
*/
|
||||||
|
function breadcrumb_schema(array $items): array
|
||||||
|
{
|
||||||
|
$list = [];
|
||||||
|
foreach (array_values($items) as $i => $item) {
|
||||||
|
$list[] = [
|
||||||
|
'@type' => 'ListItem',
|
||||||
|
'position' => $i + 1,
|
||||||
|
'name' => $item['name'],
|
||||||
|
'item' => abs_url($item['slug']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return ['@type' => 'BreadcrumbList', 'itemListElement' => $list];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAQPage-Knoten aus [['q'=>…, 'a'=>…], …]. Leere Paare werden übersprungen;
|
||||||
|
* ohne Fragen wird ein leerer Array zurückgegeben (Aufrufer filtert das raus).
|
||||||
|
*/
|
||||||
|
function faq_schema(array $faq): array
|
||||||
|
{
|
||||||
|
$questions = [];
|
||||||
|
foreach ($faq as $item) {
|
||||||
|
if (empty($item['q']) || empty($item['a'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$questions[] = [
|
||||||
|
'@type' => 'Question',
|
||||||
|
'name' => $item['q'],
|
||||||
|
'acceptedAnswer' => ['@type' => 'Answer', 'text' => $item['a']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $questions === [] ? [] : ['@type' => 'FAQPage', 'mainEntity' => $questions];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seiten-Schema-Knoten zusammenstellen (für $meta['schema']):
|
||||||
|
* Breadcrumb + optional FAQPage. Generisch für Übersichts-/Jugendseiten.
|
||||||
|
*/
|
||||||
|
function page_schema(array $breadcrumb, array $faq = []): array
|
||||||
|
{
|
||||||
|
$nodes = [];
|
||||||
|
if ($breadcrumb !== []) {
|
||||||
|
$nodes[] = breadcrumb_schema($breadcrumb);
|
||||||
|
}
|
||||||
|
if ($faq !== [] && ($faqNode = faq_schema($faq)) !== []) {
|
||||||
|
$nodes[] = $faqNode;
|
||||||
|
}
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schema-Knoten für eine Mannschafts-Seite: SportsTeam (verweist auf den Club
|
||||||
|
* via #club) + Breadcrump (Start → Fußball → Team) + optional FAQPage.
|
||||||
|
* $team: Eintrag aus data/teams.json; $slug: voller Seiten-Slug.
|
||||||
|
*/
|
||||||
|
function team_schema(array $team, string $slug): array
|
||||||
|
{
|
||||||
|
$teamNode = [
|
||||||
|
'@type' => 'SportsTeam',
|
||||||
|
'name' => $team['name'],
|
||||||
|
'sport' => 'Fußball',
|
||||||
|
'url' => abs_url($slug),
|
||||||
|
'memberOf' => ['@id' => abs_url() . '#club'],
|
||||||
|
];
|
||||||
|
if (!empty($team['hero']['text'])) {
|
||||||
|
$teamNode['description'] = $team['hero']['text'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodes = [$teamNode];
|
||||||
|
$nodes = array_merge($nodes, page_schema(
|
||||||
|
[
|
||||||
|
['name' => 'Startseite', 'slug' => ''],
|
||||||
|
['name' => 'Fußball', 'slug' => 'fussball'],
|
||||||
|
['name' => $team['name'], 'slug' => $slug],
|
||||||
|
],
|
||||||
|
$team['faq'] ?? []
|
||||||
|
));
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user