Beiträge mit Archiven, Beitragsliste und Feed (Arbeitspaket B)

Migration 20260912000600 ergänzt page.excerpt, author,
featured_media_id und published_at sowie die Tabellen term und
page_term. Beitragsmetadaten beschreiben die Seite, nicht ihren
Inhalt, und liegen deshalb als Spalten statt im Blockdokument: das
Archiv sortiert und paginiert damit in SQL, statt für jede
Auflistung alle Dokumente zu dekodieren.

Kategorien und Schlagwörter teilen sich eine Tabelle mit einem
kind-Feld. Beim Löschen einer Seite verschwinden ihre Zuordnungen,
die Begriffe bleiben als wiederverwendbare Etiketten; ein leer
gewordenes Archiv antwortet weiter mit 200 und leerer Liste.

Der Block postlist (Dokumentschema v5) speichert nur Anzahl und
Filter. Die Beiträge stellt der Controller beim Ausliefern zusammen,
damit ein gespeichertes Dokument keinen unveröffentlichten Beitrag
zeigen kann. Archive unter /beitraege, /kategorie/{slug} und
/schlagwort/{slug}, Feed unter /feed.xml; alle vier Segmente sind
für Wurzelseiten reserviert.

Geprüft: 91 PHP-Tests / 347 Assertions unter PHP 8.2.12 und 8.4.22,
12 JavaScript-Modelltests, Lint/Format/Container, Migration zuerst
gegen eine Datenbankkopie. Keine Browser- oder Lighthouse-Läufe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-12 11:25:09 +02:00
1 parent de9d6e3da8
commit ebd5883593
25 files changed
+808 -21

No files matched your search

+14
View File
@@ -148,3 +148,17 @@ Die Verwaltung arbeitet mit einer flachen, nummerierten Zeilenliste statt mit Dr
Das Testkernel läuft mit `debug = false`. Symfony prüft dann weder den kompilierten Container noch die kompilierten Twig-Vorlagen auf Aktualität. Zweimal hat das während der Entwicklung ein falsches Ergebnis erzeugt: eine geänderte Vorlage wurde aus dem Cache beantwortet und ein Test bestand fälschlich, später schlug ein geänderter Konstruktor mit einem `TypeError` aus einem veralteten Container fehl.
`tests/bootstrap.php` löscht deshalb `var/cache/test` einmal pro Lauf. Das kostet rund eine Sekunde und schließt diese Fehlerklasse aus. Zusätzlich lädt Twig in der Testumgebung Vorlagen automatisch neu, und das Login-Throttling zählt dort in einem Array-Cache statt in einem dauerhaften Pool – beide Einstellungen betreffen ausschließlich die Testumgebung, die geprüfte Drosselung selbst bleibt aktiv.
## ADR 014 – Beiträge: Metadaten als Seitenspalten, Beitragsliste als Renderer-Block (12.09.2026)
Beitragsdatum, Autor, Auszug und Beitragsbild beschreiben die Seite, nicht ihren Inhalt. Sie liegen deshalb als Spalten auf `page` und nicht im Blockdokument. Das hält Dokumentschema und Editor unverändert, und vor allem kann das Archiv in SQL sortieren und paginieren, statt für jede Auflistung sämtliche Dokumente zu dekodieren.
`published_at` wird beim Veröffentlichen gesetzt, falls noch keins gepflegt ist. Es ist damit kein zweiter Wahrheitsort, sondern die eine gepflegte Angabe; die Sortierung stützt sich auf den Index `idx_page_published_at`. Seiten werden nie gestempelt – nur Beiträge.
Kategorien und Schlagwörter teilen sich die Tabelle `term` mit einem Feld `kind`, weil sie sich bis auf ihr Archiv identisch verhalten. Namen werden beim Speichern auf ihren Slug abgebildet und bei Bedarf angelegt; die Zuordnung in `page_term` wird bei jedem Speichern ersetzt statt ergänzt. Beim Löschen einer Seite verschwinden ihre Zuordnungen, die Begriffe selbst bleiben: sie sind wiederverwendbare Etiketten, kein Inhalt eines einzelnen Beitrags. Ein leer gewordenes Archiv antwortet deshalb mit HTTP 200 und einer leeren Liste, nicht mit 404.
**Der Block `postlist` (Dokumentschema v5) speichert keine Beiträge, sondern nur Anzahl und Filter.** Die Beiträge stellt der Controller beim Ausliefern zusammen und übergibt sie getrennt an die Vorlage. Ein gespeichertes Dokument kann damit keinen Beitrag beanspruchen, den es nicht zeigen darf – insbesondere keinen unveröffentlichten. Dasselbe gilt in der Editorvorschau.
Archive liegen unter `/beitraege`, `/kategorie/{slug}` und `/schlagwort/{slug}`, der Feed unter `/feed.xml`; alle vier Segmente sind für Wurzelseiten reserviert und aus dem öffentlichen Auffangmuster ausgenommen. Paginierung über `?seite=N`, mit `rel="prev"`/`rel="next"` und Begrenzung auf den gültigen Bereich statt einer leeren Seite bei zu großen Zahlen.
Die Beitragsdaten werden über ein Formular in der Seitenübersicht gepflegt, nicht im Editor: es sind Seitendaten, kein Blockinhalt, und so bleibt die Bedienung ohne JavaScript möglich. Das Formular nutzt dieselbe optimistische Konflikterkennung wie jeder andere Schreibzugriff auf eine Seite.