Ergebnisse laufen als Anzeigetafel (Wappen und Name je Seite, Ergebnis gross in der Mitte) statt als Listenzeilen. Verworfen und im CSS begruendet: Tore am rechten Blattrand, getoente Karten je Spiel, roter Strich unter der Mannschaftszeile, graue Verlierer-Ziffern. Vereinsnamen einzeilig, sonst ausgepunktet. Die Gegnerseite bekommt ein symmetrisches Duell, eine aus den Tabellenwerten abgeleitete Aufmacher-Zeile, den Vergleich als Balkenpaar aus der Mitte und einen Tabellenausschnitt ueber die geteilte league-table-Komponente. Die Balkenlaengen kommen aus Klassen in 5-Prozent-Stufen, nie aus einem style-Attribut (die CSP verwirft inline styles stillschweigend). Das Mitglied-werden-Poster sagt jetzt "Werde Teil unseres Vereins". Alle geaenderten Seiten auf A5-Hoehe nachgemessen, kein Ueberstand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hv9SGDu3m3diiPrW9Fmj3v
178 lines
9.4 KiB
PHP
178 lines
9.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* Gegnerstatistik der Stadionzeitung. Alle Werte stammen aus dem bereits
|
||
* aufgebauten Stadionzeitungs-Snapshot; die Komponente rendert nur. Dadurch
|
||
* sind Entwurf und veröffentlichte Druckausgabe konsistent und archivfest.
|
||
*
|
||
* Props: $info stadionzeitung_live_data($issue)['gegner'][team] — ein Eintrag der
|
||
* Map je Team-Key (team_name/opponent/league/season/fixture/own/
|
||
* opponent_stats). Normal nur die tragende Mannschaft; in der
|
||
* Sportfest-Ausgabe eine Seite je Heimspiel am Fest, deshalb
|
||
* nennt der Kicker die Mannschaft.
|
||
*/
|
||
$info = is_array($info ?? null) ? $info : [];
|
||
$teamName = trim((string) ($info['team_name'] ?? ''));
|
||
$fixture = is_array($info['fixture'] ?? null) ? $info['fixture'] : [];
|
||
$own = is_array($info['own'] ?? null) ? $info['own'] : null;
|
||
$opponent = is_array($info['opponent_stats'] ?? null) ? $info['opponent_stats'] : null;
|
||
// Gedruckt wird der Anzeigename (Kurzname der Ausgabe, wenn gepflegt) — die
|
||
// BFV-Schreibweise „(SG1) TSV Wirsberg I/ FC Kupferberg I" ist ein Suchschlüssel,
|
||
// keine Überschrift.
|
||
$ausschnitt = is_array($info['table_excerpt'] ?? null) ? $info['table_excerpt'] : [];
|
||
$opponentName = (string) ($info['opponent_display'] ?? $info['opponent'] ?? 'Unser Gegner');
|
||
|
||
// Links steht IMMER unsere Mannschaft (Klasse --own hebt sie hervor), rechts der
|
||
// Gegner. Bei einem Heimspiel ist das ohnehin die Reihenfolge Heim/Gast; fährt die
|
||
// Mannschaft aber auswärts, trüge die Hervorhebung sonst der gegnerische Verein.
|
||
$istHeim = !empty($fixture['is_home']);
|
||
$ownName = (string) (($istHeim ? ($fixture['home'] ?? null) : ($fixture['away'] ?? null)) ?? 'TSV 08 Kulmbach');
|
||
$ownCrest = ($istHeim ? ($fixture['home_crest'] ?? null) : ($fixture['away_crest'] ?? null)) ?? $own['crest'] ?? null;
|
||
$gegnerLabel = $opponentName;
|
||
$gegnerCrest = ($istHeim ? ($fixture['away_crest'] ?? null) : ($fixture['home_crest'] ?? null)) ?? $opponent['crest'] ?? null;
|
||
|
||
$kickoff = (string) ($fixture['kickoff'] ?? '');
|
||
$kickoffDate = $kickoff !== '' ? substr($kickoff, 0, 10) : '';
|
||
$kickoffTime = $kickoff !== '' ? substr($kickoff, 11, 5) : '';
|
||
|
||
// Der Tabellenplatz steht NICHT in der Balkenliste: klein ist dort besser,
|
||
// ein Balken läge genau falsch herum. Er gehört nach oben neben die Wappen.
|
||
// „Spiele“ fällt ebenfalls raus (für beide gleich, steht in der Kopfzeile).
|
||
$rows = [
|
||
['label' => 'Siege', 'key' => 'won'],
|
||
['label' => 'Unentschieden', 'key' => 'drawn'],
|
||
['label' => 'Niederlagen', 'key' => 'lost'],
|
||
['label' => 'Tordifferenz', 'key' => 'goal_diff', 'signed' => true],
|
||
['label' => 'Punkte', 'key' => 'points'],
|
||
];
|
||
|
||
$format = static function (?array $row, string $key, bool $signed = false): string {
|
||
if ($row === null || !array_key_exists($key, $row)) {
|
||
return '–';
|
||
}
|
||
$value = (int) $row[$key];
|
||
return $signed && $value > 0 ? '+' . $value : (string) $value;
|
||
};
|
||
|
||
// Balkenlänge als KLASSE, nie als style-Attribut: die CSP verwirft inline
|
||
// styles stillschweigend (siehe CLAUDE.md), der Balken wäre in Produktion
|
||
// unsichtbar. Deshalb 5-Prozent-Stufen, die components.css ausdefiniert.
|
||
$barStep = static function (?array $row, ?array $other, string $key): int {
|
||
if ($row === null) {
|
||
return 0;
|
||
}
|
||
$value = abs((int) ($row[$key] ?? 0));
|
||
$scale = max($value, $other === null ? 0 : abs((int) ($other[$key] ?? 0)));
|
||
if ($scale <= 0) {
|
||
return 0;
|
||
}
|
||
return (int) round($value / $scale * 20);
|
||
};
|
||
|
||
// Ein Satz statt einer Zahlenkolonne: derselbe Bestand, nur in Sprache. Die
|
||
// erste zutreffende Regel gewinnt, damit immer genau eine Aussage dasteht.
|
||
$zahlwort = static function (int $n): string {
|
||
$woerter = [0 => 'Kein', 1 => 'Ein', 2 => 'Zwei', 3 => 'Drei', 4 => 'Vier', 5 => 'Fünf',
|
||
6 => 'Sechs', 7 => 'Sieben', 8 => 'Acht', 9 => 'Neun', 10 => 'Zehn', 11 => 'Elf',
|
||
12 => 'Zwölf', 13 => 'Dreizehn', 14 => 'Vierzehn', 15 => 'Fünfzehn'];
|
||
return $woerter[$n] ?? (string) $n;
|
||
};
|
||
$aufmacher = '';
|
||
if ($opponent !== null) {
|
||
$played = (int) ($opponent['played'] ?? 0);
|
||
$won = (int) ($opponent['won'] ?? 0);
|
||
$lost = (int) ($opponent['lost'] ?? 0);
|
||
$rank = (int) ($opponent['rank'] ?? 0);
|
||
$points = (int) ($opponent['points'] ?? 0);
|
||
$diff = (int) ($opponent['goal_diff'] ?? 0);
|
||
if ($played > 0 && $lost === 0) {
|
||
$aufmacher = 'Noch ohne Niederlage';
|
||
} elseif ($played > 0 && $won === 0) {
|
||
$aufmacher = 'Noch ohne Sieg';
|
||
} elseif ($played > 0 && $won / $played >= 0.7) {
|
||
$aufmacher = $zahlwort($won) . ' Siege aus ' . mb_strtolower($zahlwort($played)) . ' Spielen';
|
||
} elseif ($rank === 1) {
|
||
$aufmacher = 'Tabellenführer mit ' . $points . ' Punkten';
|
||
} elseif ($played > 0 && $diff <= -8) {
|
||
$aufmacher = $zahlwort(abs($diff)) . ' Tore mehr kassiert als geschossen';
|
||
} elseif ($played > 0 && $diff >= 8) {
|
||
$aufmacher = $zahlwort($diff) . ' Tore mehr geschossen als kassiert';
|
||
} elseif ($played > 0) {
|
||
$aufmacher = $points . ' Punkte aus ' . $played . ' Spielen';
|
||
}
|
||
}
|
||
$spieltagInfo = $opponent !== null && (int) ($opponent['played'] ?? 0) > 0
|
||
? 'Stand nach ' . (int) $opponent['played'] . ' Spieltagen'
|
||
: '';
|
||
?>
|
||
<div class="stadionzeitung-gegner">
|
||
<header class="stadionzeitung-page__head">
|
||
<p class="stadionzeitung-page__kicker"><?= e($teamName !== '' ? 'Unser Gegner · ' . $teamName : 'Unser Gegner') ?></p>
|
||
<h2 class="stadionzeitung-page__heading"><?= e($opponentName) ?></h2>
|
||
<?php if (!empty($info['league']) || !empty($info['season'])): ?>
|
||
<p class="stadionzeitung-page__sub"><?= e(trim(implode(' · ', array_filter([(string) ($info['league'] ?? ''), (string) ($info['season'] ?? '')])))) ?></p>
|
||
<?php endif; ?>
|
||
</header>
|
||
<span class="stadionzeitung-page__rule" aria-hidden="true"></span>
|
||
|
||
<div class="stadionzeitung-gegner__duell">
|
||
<div class="stadionzeitung-gegner__club">
|
||
<?php component('crest', ['src' => $ownCrest, 'name' => $ownName]); ?>
|
||
<span class="stadionzeitung-gegner__club-name"><?= e($ownName) ?></span>
|
||
</div>
|
||
<div class="stadionzeitung-gegner__vs">
|
||
<p class="stadionzeitung-gegner__vs-wort"><?= $istHeim ? 'gegen' : 'bei' ?></p>
|
||
<?php if ($kickoffDate !== ''): ?>
|
||
<p class="stadionzeitung-gegner__anstoss"><?= e(german_date($kickoffDate)) ?><?php if ($kickoffTime !== ''): ?><br><?= e($kickoffTime) ?> Uhr<?php endif; ?></p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="stadionzeitung-gegner__club">
|
||
<?php component('crest', ['src' => $gegnerCrest, 'name' => $gegnerLabel]); ?>
|
||
<span class="stadionzeitung-gegner__club-name"><?= e($gegnerLabel) ?></span>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($aufmacher !== ''): ?>
|
||
<p class="stadionzeitung-gegner__aufmacher"><?= e($aufmacher) ?></p>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($own !== null && $opponent !== null): ?>
|
||
<div class="stadionzeitung-gegner__vergleich" role="table" aria-label="Tabellenvergleich">
|
||
<div class="stadionzeitung-gegner__vergleich-head" role="row">
|
||
<?php /* Spaltenköpfe generisch: vorher standen hier „TSV 08" und „ASV" fest im
|
||
Markup — das Kürzel des Marktschorgast-Gegners wäre in jeder Ausgabe
|
||
mitgedruckt worden. Die Überschrift nennt den Gegner ohnehin. */ ?>
|
||
<span role="columnheader">Wir</span>
|
||
<span role="columnheader"><?= e($spieltagInfo !== '' ? $spieltagInfo : 'Vergleich') ?></span>
|
||
<span role="columnheader">Gegner</span>
|
||
</div>
|
||
<?php foreach ($rows as $row): ?>
|
||
<div class="stadionzeitung-gegner__vergleich-row" role="row">
|
||
<strong class="stadionzeitung-gegner__wert" role="cell"><?= e($format($own, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||
<span class="stadionzeitung-gegner__balken stadionzeitung-gegner__balken--own" aria-hidden="true"><span class="stadionzeitung-gegner__balken-fuellung stadionzeitung-gegner__balken-fuellung--s<?= $barStep($own, $opponent, (string) $row['key']) ?>"></span></span>
|
||
<span class="stadionzeitung-gegner__label" role="rowheader"><?= e((string) $row['label']) ?></span>
|
||
<span class="stadionzeitung-gegner__balken" aria-hidden="true"><span class="stadionzeitung-gegner__balken-fuellung stadionzeitung-gegner__balken-fuellung--s<?= $barStep($opponent, $own, (string) $row['key']) ?>"></span></span>
|
||
<strong class="stadionzeitung-gegner__wert" role="cell"><?= e($format($opponent, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php else: ?>
|
||
<p class="stadionzeitung-gegner__empty">Die aktuellen Vergleichsdaten waren beim letzten Abruf nicht verfügbar.</p>
|
||
<?php endif; ?>
|
||
|
||
<?php /* Der Tabellenplatz stand kurz als zwei lose Ziffern oben im Duell und
|
||
wirkte dort unruhig (Felix, 19.09.2026). Hier zeigt ihn der Ausschnitt
|
||
im Zusammenhang: wer steht zwischen uns und ihnen. Gerendert mit der
|
||
geteilten league-table-Komponente, nie mit einer zweiten Tabelle. */ ?>
|
||
<?php if ($ausschnitt !== []): ?>
|
||
<div class="stadionzeitung-gegner__ausschnitt">
|
||
<p class="stadionzeitung-gegner__ausschnitt-titel">Die Tabelle dazwischen</p>
|
||
<?php foreach ($ausschnitt as $block): ?>
|
||
<?php component('league-table', ['table' => $block, 'caption' => 'Tabellenausschnitt ' . (string) ($info['league'] ?? '')]); ?>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|