Stadionzeitung: Ausgabe 19.09., Gegnerseiten ohne Fest, Gegner-Kurzname

Gegnerseiten lassen sich jetzt ueber `gegner_teams` auch ohne Veranstaltung
fuer weitere Mannschaften anlegen (Fenster zwei Tage vor bis drei Tage nach
dem Ausgabedatum, auch auswaerts), und `opponent_short` liefert den
gedruckten Anzeigenamen, waehrend `opponent` die BFV-Schreibweise fuer den
Namensvergleich behaelt.

Dazu die Ausgabe zum Heimspiel gegen Wirsberg/Kupferberg als Entwurf samt
Seitenbildern. Die gemeinsamen QR-Codes sind neu erzeugt (die vendorte
Bibliothek arbeitet nicht reproduzierbar, das Maskenmuster wechselt).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hv9SGDu3m3diiPrW9Fmj3v
This commit is contained in:
2026-09-19 10:53:56 +02:00
parent fef80bf669
commit 8f864368f0
25 changed files with 636 additions and 22 deletions

View File

@@ -992,6 +992,22 @@ function stadionzeitung_teaser(array $issue): string
return trim((string) ($issue['interview']['teaser'] ?? ''));
}
/**
* Der Name des Gegners, wie er GEDRUCKT wird — Cover, Archivzeile, Kopf, Meta.
*
* `opponent` muss die BFV-Schreibweise tragen, weil die Gegnerseite damit ihre
* Tabellenzeile und ihre Begegnung in matchcenter.json findet (exakter
* Namensvergleich). Bei Spielgemeinschaften ist das eine Zeile wie
* „(SG1) TSV Wirsberg I/ FC Kupferberg I", die auf einem Cover nichts zu
* suchen hat. `opponent_short` ist deshalb der im Chat gepflegte Anzeigename
* („Wirsberg/Kupferberg"); fehlt er, bleibt es beim vollen Namen.
*/
function stadionzeitung_opponent_display(array $issue): string
{
$short = trim((string) ($issue['opponent_short'] ?? ''));
return $short !== '' ? $short : trim((string) ($issue['opponent'] ?? ''));
}
/**
* Welche Mannschaften bekommen in einer Ausgabe eine Gegnerseite — die EINE
* Logik dafür, benutzt von bin/stadionzeitung-add.php (Seitenplanung) UND
@@ -999,10 +1015,19 @@ function stadionzeitung_teaser(array $issue): string
* oder Daten ohne Seite entstehen.
*
* Immer: die tragende Mannschaft (Gegner = `opponent`, Tag = `date`).
* Zusätzlich, wenn die Ausgabe eine `veranstaltung` (Slug) mit Zeitraum trägt
* (Sportfest-Ausgabe): jede weitere Mannschaft aus matchcenter.json mit einem
* HEIMspiel in diesem Zeitraum — drei Heimspiele am Fest, drei Gegnerseiten.
* Der Gegner ist dann der andere Verein der Begegnung. Reihenfolge wie in
* Zusätzlich auf zwei Wegen, die sich nicht in die Quere kommen:
*
* 1. `veranstaltung` (Slug) mit Zeitraum — die Sportfest-Ausgabe: jede weitere
* Mannschaft aus matchcenter.json mit einem HEIMspiel in diesem Zeitraum.
* Am Fest spielen alle zuhause, das ist der Aufhänger.
* 2. `gegner_teams` (Liste von Team-Keys, im Chat gepflegt) — für ein normales
* Spielwochenende, an dem auch die anderen Mannschaften antreten. Genommen
* wird ihr nächstes Spiel im Fenster von zwei Tagen vor bis drei Tage nach
* dem Ausgabedatum, AUCH auswärts: der Gegner der Frauen ist auch dann eine
* Seite wert, wenn wir zu ihm fahren. Ohne Spiel in diesem Fenster entsteht
* keine Seite; bin/stadionzeitung-add.php meldet das.
*
* Der Gegner ist jeweils der andere Verein der Begegnung. Reihenfolge wie in
* matchcenter.json (= config 'matchcenter.teams').
*
* Rückgabe: [team_key => ['opponent' => string, 'date' => 'YYYY-MM-DD']]
@@ -1013,14 +1038,19 @@ function stadionzeitung_gegner_fixtures(array $issue): array
$issueTeam = (string) ($issue['team_key'] ?? '');
$issueOpponent = trim((string) ($issue['opponent'] ?? ''));
$von = '';
$bis = '';
$eventVon = '';
$eventBis = '';
$veranstaltung = veranstaltung_find((string) ($issue['veranstaltung'] ?? ''));
if ($veranstaltung !== null) {
$von = (string) ($veranstaltung['date_start'] ?? '');
$bis = (string) ($veranstaltung['date_end'] ?? $von);
$eventVon = (string) ($veranstaltung['date_start'] ?? '');
$eventBis = (string) ($veranstaltung['date_end'] ?? $eventVon);
}
$extraTeams = array_values(array_filter(array_map(
static fn ($k): string => trim((string) $k),
is_array($issue['gegner_teams'] ?? null) ? $issue['gegner_teams'] : []
)));
$fixtures = [];
foreach (json_load('matchcenter')['teams'] ?? [] as $team) {
$key = (string) ($team['key'] ?? '');
@@ -1033,18 +1063,32 @@ function stadionzeitung_gegner_fixtures(array $issue): array
}
continue;
}
// Weg 2: ausdrücklich benannte Mannschaft, Fenster ums Ausgabedatum,
// Heim wie auswärts. Steht sie in `gegner_teams`, gilt dieses Fenster
// und nicht der Veranstaltungszeitraum.
if (in_array($key, $extraTeams, true) && $date !== '') {
$von = date('Y-m-d', strtotime($date . ' -2 days'));
$bis = date('Y-m-d', strtotime($date . ' +3 days'));
$nurHeim = false;
} else {
$von = $eventVon;
$bis = $eventBis;
$nurHeim = true;
}
if ($von === '') {
continue;
}
foreach ($team['next_matches'] ?? [] as $match) {
if (empty($match['is_home'])) {
$istHeim = !empty($match['is_home']);
if ($nurHeim && !$istHeim) {
continue;
}
$matchDate = substr((string) ($match['kickoff'] ?? ''), 0, 10);
if ($matchDate < $von || $matchDate > $bis) {
continue;
}
$opponent = trim((string) ($match['away'] ?? ''));
$opponent = trim((string) ($istHeim ? ($match['away'] ?? '') : ($match['home'] ?? '')));
if ($opponent === '' || $opponent === 'SPIELFREI') {
continue;
}
@@ -1140,14 +1184,53 @@ function stadionzeitung_build_snapshot(array $issue): array
}
}
// `opponent` behält die BFV-Schreibweise (der Namensvergleich oben
// hängt daran), gedruckt wird `opponent_display`: bei der tragenden
// Mannschaft der Kurzname der Ausgabe, sonst der volle Name.
$gegnerDisplay = strcasecmp($gegnerName, trim((string) ($issue['opponent'] ?? ''))) === 0
? stadionzeitung_opponent_display($issue)
: $gegnerName;
// Tabellenausschnitt statt zweier loser Platz-Ziffern im Kopf: ein
// Fenster um beide Mannschaften herum (mit einer Zeile Luft davor
// und dahinter). Liegen sie weit auseinander, werden es zwei
// Blöcke — gerendert wird beides mit der geteilten
// league-table-Komponente, nie mit einer zweiten Tabelle.
$ausschnitt = [];
if ($ownRow !== null && $opponentRow !== null && $table !== []) {
$ranks = [(int) ($ownRow['rank'] ?? 0), (int) ($opponentRow['rank'] ?? 0)];
sort($ranks);
$fenster = static function (int $von, int $bis) use ($table): array {
return array_values(array_filter($table, static function (array $row) use ($von, $bis): bool {
$rank = (int) ($row['rank'] ?? 0);
return $rank >= $von && $rank <= $bis;
}));
};
if ($ranks[1] - $ranks[0] <= 5) {
$block = $fenster($ranks[0], $ranks[1]);
if ($block !== []) {
$ausschnitt[] = $block;
}
} else {
foreach ($ranks as $rank) {
$block = $fenster($rank, $rank + 1);
if ($block !== []) {
$ausschnitt[] = $block;
}
}
}
}
$gegner[$key] = [
'team_name' => (string) ($team['name'] ?? ''),
'opponent' => $gegnerName,
'opponent_display' => $gegnerDisplay,
'league' => (string) ($team['league'] ?? ''),
'season' => (string) ($team['season'] ?? ''),
'fixture' => $fixture,
'own' => $ownRow,
'opponent_stats' => $opponentRow,
'table_excerpt' => $ausschnitt,
];
}