- JavaScript 95.8%
- PHP 2.4%
- Twig 1%
- CSS 0.7%
- Dockerfile (PHP >= 8.5.12, Apache, APP_ENV=stage) und compose.coolify.yaml mit PostgreSQL 18 - Volumes: var/storage/ (Mediendateien) und Datenbank - Jeder Start: `sulu:build prod`, Konto „admin“ anlegen, falls es fehlt (Passwort aus SULU_ADMIN_PASSWORD) - APP_SECRET und Datenbank-Zugang erzeugt Coolify Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| assets/admin | ||
| bin | ||
| config | ||
| docker | ||
| migrations | ||
| public | ||
| src | ||
| templates | ||
| tests | ||
| translations | ||
| .dockerignore | ||
| .editorconfig | ||
| .env | ||
| .env.dev | ||
| .env.stage | ||
| .env.test | ||
| .gitignore | ||
| .php-cs-fixer.dist.php | ||
| .twig-cs-fixer.dist.php | ||
| compose.coolify.yaml | ||
| compose.yaml | ||
| composer.json | ||
| composer.lock | ||
| Dockerfile | ||
| phpstan.dist.neon | ||
| phpunit.dist.xml | ||
| README.md | ||
| rector.php | ||
| symfony.lock | ||
Website Musikwerk Stuttgart (Sulu-Prototyp)
Prototyp für die öffentliche Website des Musikwerk Stuttgart e. V. auf Basis
von Sulu 3. Nachbau von musikwerk-stuttgart.de mit
Startseite, Events, den Seiten unter „Mitmachen“ und „Verein“ sowie einigen
Bildergalerien im Rückblick. Die Inhalte sind Beispieldaten aus der bisherigen
WordPress-Seite, kein vollständiger Import.
Nicht Teil dieser Website: Webshop, Mitgliederbereich („Interner Bereich“), Ticketverkauf. Dorthin wird nur verlinkt.
Starten
Voraussetzung: PHP ab 8.2 mit gd, intl, curl, dom und pdo_pgsql,
Docker für die Datenbank (PostgreSQL auf Port 5433).
composer install
docker compose up -d
bin/adminconsole sulu:build dev # Datenbank, Rollen, Benutzer admin/admin
composer seed # Beispielinhalte
composer serve
Website: http://127.0.0.1:8002/, Administration: http://127.0.0.1:8002/admin
(Benutzer admin, Passwort admin).
Bildgrößen werden beim ersten Aufruf erzeugt. Mit dem eingebauten PHP-Server dauert der erste Seitenaufruf mit vielen Bildern daher einige Sekunden.
sulu:build dev --destroy setzt die Datenbank vollständig zurück.
Deployment (Coolify)
Ressource aus dem Git-Repository, Build Pack „Docker Compose“, Compose-Datei
/compose.coolify.yaml. Domain beim Dienst web eintragen. Image:
Dockerfile (PHP ≥ 8.5.12, Apache), Umgebung stage.
- Dienste:
webunddb(PostgreSQL 18). Volumes:storage(Mediendateien,var/storage/) unddatabase. - Coolify erzeugt
APP_SECRET, Datenbank-Zugang und das Passwort des Kontosadmin(SERVICE_PASSWORD_SULUADMIN, unter Environment Variables). - Jeder Start führt
sulu:build prodaus (Schema aktualisieren, Grunddaten) und legtadminan, falls es fehlt. - Beispielinhalte im Terminal des Containers
web:runuser -u www-data -- bin/adminconsole app:seed
Beispielinhalte
composer seed # anlegen/aktualisieren
bin/adminconsole app:seed --refresh # Texte neu von der alten Seite laden
bin/adminconsole app:seed --alle-bilder # Galerien vollständig statt 16 Bilder
Der Befehl liest die WordPress-REST-API und die Event-Seiten der alten
Website und legt einen Cache unter var/seed-cache/ an. Er ist wiederholbar:
Seiten, Events und Personen bekommen eine aus ihrer Adresse abgeleitete UUID
und werden beim nächsten Lauf aktualisiert, Medien werden über Sammlung und
Dateinamen wiedererkannt und nicht erneut hochgeladen. Texte werden bei jedem
Lauf überschrieben.
Angelegt wird über dieselben Nachrichten, die auch die Administration
verschickt (CreatePageMessage, ApplyWorkflowTransitionPageMessage …),
nicht über Doctrine-Fixtures – die würden die Datenbank samt Benutzer leeren.
Was es in WordPress nicht strukturiert gibt, steht von Hand in
src/Command/SeedCommand.php und ist dort markiert: Probenzeiten und -orte,
Leitung und Farbe der Chorgruppen, Zuordnung von Events zu Chorgruppen, die
Einzeltermine des Familiensingens, die Videos im Rückblick, die
Kurzbeschreibungen.
Aufbau
| Pfad | Inhalt |
|---|---|
config/webspaces/musikwerk.xml |
Webspace: Sprache de, Navigationskontexte Haupt- und Fußmenü |
config/templates/pages/ |
Seitenvorlagen (Inhaltsmodell der Seiten) |
config/templates/fragments/ |
gemeinsame Felder, per XInclude eingebunden (Kopf, Inhalt, Farbe) |
config/templates/articles/event.xml |
Events als Artikel |
config/templates/snippets/ |
Personen und Website-Einstellungen |
config/image-formats.xml |
Bildgrößen für srcset |
templates/ |
Twig: Seitengerüst (base.html.twig), Bausteine (macros.html.twig), je Vorlage eine Datei |
src/Twig/MusikwerkExtension.php |
Events nach Terminen, Unterseiten, Rückwärtsbeziehungen, deutsche Datumsangaben |
src/Command/SeedCommand.php, src/Seed/ |
Import der Beispielinhalte aus WordPress |
public/assets/ |
CSS, JavaScript, Schriften, Logo – alles lokal |
Code, installierte Pakete, Inhalte
| Was | Wo | Git |
|---|---|---|
| Eigener Code | src/, config/, templates/, translations/, migrations/, public/ (ohne die Zeilen unten) |
ja |
| Abhängigkeiten | vendor/ (Versionen in composer.lock), public/bundles/ (assets:install) |
nein |
| Inhalte | Datenbank, var/storage/ (Mediendateien) |
nein, Backup |
| Erzeugt, entbehrlich | var/cache/, var/log/, var/share/, public/uploads/ (Bildgrößen) |
nein |
| Geheimnisse | .env.local bzw. .env.$APP_ENV.local oder Umgebungsvariablen |
nein |
APP_SECRET steht nur in .env.dev (lokal). Jede andere Umgebung muss ihn
selbst setzen, z. B. in .env.stage.local.
Inhaltsmodell
/ homepage Startseite
├── events/ events
│ └── <event> Artikel „event“, ein oder mehrere Termine
├── mitmachen/ abschnitt
│ ├── chorgruppen/ chorgruppen
│ │ └── <chorgruppe>/ chorgruppe kinderchor, pop-chor, familiensingen, offener-chor
│ ├── foerdermoeglichkeiten/ seite
│ └── mitgliedsantrag Verknüpfung → verein/dokumente
├── rueckblick/ rueckblick nach Jahren gruppiert
│ ├── fotos Verknüpfung → rueckblick
│ ├── videos externer Link → YouTube
│ └── <projekt>/ projekt Galerie und Videos
├── verein/ abschnitt
│ ├── kuenstlerische-leitung/ leitung wählt Personen (Snippets) aus
│ ├── impressum/ seite auch im Fußmenü
│ ├── foerdermoeglichkeiten Verknüpfung → mitmachen/foerdermoeglichkeiten
│ └── dokumente/ dokumente PDFs als Blöcke
├── shop externer Link (bleibt vorerst in WordPress)
└── datenschutz/ seite nur im Fußmenü
Snippets: person Name, Rolle, Porträt, Kontakt, Kurzbiografie
einstellungen Beschreibung, Social-Media-Links (Snippet-Bereich „einstellungen“)
Mediathek: Sammlungen je Chorgruppe und je Rückblick-Projekt, dazu Seiten,
Events, Personen, Dokumente
Grundsätze:
- Listen erzeugt die Vorlage. Die Übersicht der Chorgruppen, die Termine, der Rückblick nach Jahren und die Kacheln der Abschnitte entstehen aus den Unterseiten bzw. Artikeln. Niemand pflegt dieselbe Liste zweimal.
- Beziehungen statt Fließtext. Events und Projekte wählen Chorgruppen
aus, Chorgruppen ihre Leitung (Personen-Snippets). Daraus entstehen auf der
Chorgruppenseite „Nächste Termine“ und „Zuletzt dabei“ und auf der
Leitungsseite die Zuordnung der Personen. Die Rückwärtsrichtung berechnet
MusikwerkExtension. - Events sind Artikel mit Terminen, nicht mit einem Datum. Der Block
termineerlaubt Reihen (Familiensingen) und abgesagte Einzeltermine. Sortiert und angezeigt wird nach dem nächsten Termin, der stattfindet. Vergangene Events verschwinden von selbst aus der Übersicht; die des letzten Jahres stehen darunter. - Galerie = Medienauswahl. Jede Galerie hat ihre eigene Sammlung in der Mediathek; die Reihenfolge der Auswahl ist die der Galerie. Beschreibung wird Bildunterschrift und Alternativtext, das Feld „Copyright“ der Fotonachweis.
- Fließtext als Blöcke mit bewusst wenigen Typen (Text, Bild, Video). Links auf andere Seiten und Dateien sind Sulu-Links und bleiben beim Verschieben gültig.
Navigation
Haupt- und Fußmenü entstehen aus dem Seitenbaum: Jede Seite gibt unter Einstellungen → Navigationskontexte an, ob sie im Haupt- oder Fußmenü steht. Dass dieselbe Seite unter zwei Punkten erscheint (Fördermöglichkeiten unter Mitmachen und Verein) oder ein Punkt nach außen zeigt (Videos, Shop), erledigen Verknüpfungsseiten (interner bzw. externer Link). Die Farbe eines Hauptpunkts ist das Feld „Farbe“ der Abschnittsseite; externe Links haben keine Felder und bekommen die Akzentfarbe.
Frontend
Gleiches Markup, CSS und JavaScript wie der Kirby-Prototyp.
- Keine Ressourcen von fremden Servern: Schriften (Montserrat, AntiqueOlive),
Logo und Skripte liegen unter
public/assets/. YouTube-Videos laden erst nach einem Klick überyoutube-nocookie.com. Deshalb kein Cookie-Banner. - Eigenes, schlankes CSS statt Bootstrap. Bis 991 px Breite Burger-Menü.
- Bilder als WebP in mehreren Größen (
srcset), Lightbox ohne Bibliothek.
Einschränkungen des Prototyps
- Suche: Der Suchindex liegt nur im Speicher (
SEAL_DSN=memory://), weil das Standard-Backend Loupepdo_sqlitebraucht. Für den Betrieb Meilisearch o. Ä. einrichten. - HTTP-Cache: Die eigenen Listen (Events, Unterseiten) melden ihre Inhalte dem Reference Store, sind aber nicht unter Last getestet.
- Die Administration erscheint für den Benutzer
adminauf Englisch; die Feldbezeichnungen gibt es auch auf Deutsch (Benutzersprache umstellen). - Neues Event: Die Adresse (
page_tree_route) braucht als Elternseite „Events“, die sich nicht vorbelegen lässt. Im Test blieb „Bestätigen“ in der Seitenauswahl ausgegraut; über die API (POST /admin/api/articles) angelegte Events erscheinen korrekt. Vor einer Entscheidung für Sulu klären, ob das ein Fehler in Sulu 3.0 ist.