
Contao 5 Erweiterung veröffentlichen: Packagist & Open Source

Teil 1: Vorbereitung, Code-Isolierung und Metadaten-Upgrade
Willkommen zum großen Finale unserer Entwicklungsreise! Wenn wir eine professionelle Contao 5 Erweiterung veröffentlichen möchten, reicht es nicht mehr aus, dass unser Strandkorb-Buchungssystem (acme/beachside-bundle) sicher und versteckt als lokales Paket innerhalb unserer spezifischen Installation läuft. Für die reine Entwicklung war dieser Ansatz perfekt. Doch Open Source bedeutet, dass wir den Code so verpacken, dass jeder Entwickler auf der Welt ihn mit einem simplen Konsolenbefehl (composer require acme/beachside-bundle) in sein eigenes Projekt integrieren kann. In diesem Schritt lösen wir unsere Nabelschnur zum Hauptprojekt und bereiten die fundamentale Architektur für Packagist vor.
In diesem ersten Schritt lösen wir unsere Nabelschnur zum Hauptprojekt und bereiten die fundamentale Architektur unserer Erweiterung für Packagist vor.
Schritt 1: Die physische Isolierung des Codes
Wenn du dein Modul bisher über ein lokales "Path-Repository" in der composer.json deiner Contao-App eingebunden hast, liegt der Code meist in einem Unterordner wie /packages/beachside-bundle/ innerhalb des Hauptprojekts.
Für ein Open-Source-Release muss die Erweiterung ein völlig eigenständiges Softwarepaket (Stand-alone) werden.
Erstelle einen komplett neuen, leeren Ordner auf deiner Festplatte, weit weg von deiner Contao-Installation (z. B. auf deinem Desktop unter
~/Entwicklung/beachside-bundle).Verschiebe alle relevanten Dateien deines Bundles dorthin. Das umfasst in der Regel den
src/Ordner, diecomposer.json, eventuell vorhandenetests/, sowie denpublic/odercontao/Ordner (falls du klassische DCA-Konfigurationen nutzt).
Deine lokale Contao-Website wird jetzt Fehler werfen, wenn du sie aufrufst, weil der Code plötzlich fehlt. Das ist absolut korrekt so! Wir bauen das Bundle nun isoliert auf.
Schritt 2: Die composer.json für Packagist perfektionieren
Die composer.json ist die Visitenkarte deiner Erweiterung. Packagist (die zentrale Paketverwaltung für PHP) liest diese Datei aus, um dein Modul zu kategorisieren, Versionen zu erkennen und es im Contao Manager auffindbar zu machen.
Deine bisherige Konfiguration sah so aus:
1{
2 "name": "acme/beachside-bundle",
3 "description": "Ein professionelles Strandkorb-Buchungssystem für Contao 5",
4 "type": "contao-bundle",
5 "license": "MIT",
6 "require": {
7 "php": "^8.1",
8 "contao/core-bundle": "^5.0",
9 "symfony/framework-bundle": "^6.0"
10 },
11 "autoload": {
12 "psr-4": {
13 "Acme\\BeachsideBundle\\": "src/"
14 }
15 },
16 "extra": {
17 "contao-manager-plugin": "Acme\\BeachsideBundle\\ContaoManager\\Plugin"
18 }
19}Das ist ein hervorragender Start. Der Typ "contao-bundle" ist extrem wichtig, damit Composer und der Contao Manager wissen, wie sie das Paket behandeln müssen (es wird im Contao-Ökosystem registriert). Die Abhängigkeiten unter require stellen sicher, dass niemand das Bundle fälschlicherweise in einem alten Contao 4.9 installiert und sich über Fehler wundert.
Für ein echtes Public Release auf Packagist müssen wir diese Datei jedoch um wichtige Metadaten anreichern. Wir fügen die Abschnitte keywords, authors und support hinzu. Das macht dein Paket suchmaschinenfreundlich und signalisiert Professionalität.
Hier ist das Upgrade deiner Datei für den Open-Source-Standard:
1{
2 "name": "acme/beachside-bundle",
3 "description": "Ein professionelles Strandkorb-Buchungssystem für Contao 5",
4 "type": "contao-bundle",
5 "license": "MIT",
6 "keywords": ["contao", "booking", "strandkorb", "ecommerce", "bundle"],
7 "authors": [
8 {
9 "name": "Dein Name",
10 "email": "dev@deinedomain.de",
11 "homepage": "https://deinedomain.de",
12 "role": "Developer"
13 }
14 ],
15 "require": {
16 "php": "^8.1",
17 "contao/core-bundle": "^5.0 || ^6.0",
18 "symfony/framework-bundle": "^6.0 || ^7.0"
19 },
20 "autoload": {
21 "psr-4": {
22 "Acme\\BeachsideBundle\\": "src/"
23 }
24 },
25 "extra": {
26 "contao-manager-plugin": "Acme\\BeachsideBundle\\ContaoManager\\Plugin"
27 },
28 "support": {
29 "issues": "https://github.com/acme/beachside-bundle/issues",
30 "source": "https://github.com/acme/beachside-bundle"
31 }
32}Wichtige Ergänzungen im Detail:
keywords: Packagist nutzt diese Tags für die interne Suche. So finden Agenturen dein Bundle sofort, wenn sie nach "booking" oder "ecommerce" suchen.
require: Wir haben die Abhängigkeiten zukunftssicher gemacht (
^5.0 || ^6.0). Das bedeutet, dein Bundle läuft sowohl unter Contao 5 als auch unter einem zukünftigen Contao 6, sofern sich die von dir genutzten Core-APIs nicht ändern. Das reduziert den Wartungsaufwand enorm.support: Hier verlinkst du später auf dein GitHub-Repository. Open Source lebt von Fehlerberichten (Issues) und Pull Requests der Community. Ein fehlender Support-Block schreckt andere Entwickler ab.
Nachdem unsere Ordnerstruktur und unsere Metadaten nun bereit für die Öffentlichkeit sind, müssen wir im nächsten Schritt sicherstellen, dass unser Code qualitativ hochwertig ist und den strikten Contao Coding Standards entspricht.

Teil 2: Lokales Testen mit Composer Path Repositories
Im vorherigen Schritt haben wir unser Strandkorb-Bundle mutig aus der Hauptinstallation herausgeschnitten und in einen isolierten Ordner verschoben. Das war architektonisch richtig, führt aber zu einem praktischen Problem: Unsere lokale Contao-Website funktioniert jetzt nicht mehr, weil ihr der Code für die Strandkörbe fehlt.
Bevor wir unser Paket jedoch auf GitHub hochladen und für Packagist freigeben, müssen wir es zwingend weiterentwickeln und ausgiebig testen können. Wir brauchen also einen Weg, um unseren isolierten Code temporär wieder in unser Projekt einzubinden, ohne ihn jedes Mal über das Internet herunterladen zu müssen.
Die elegante Lösung der PHP-Welt dafür lautet: Composer Path Repositories (Pfad-Repositories) und Symlinks.
Was ist ein Path Repository?
Normalerweise sucht Composer nach Paketen in der globalen Datenbank Packagist.org. Mit einem Path Repository sagen wir Composer jedoch: "Hey, bevor du im Internet suchst, schau doch bitte mal auf meiner lokalen Festplatte in diesem speziellen Ordner nach!"
Um das einzurichten, wechseln wir nun von unserem isolierten Bundle-Ordner zurück in das Wurzelverzeichnis unseres eigentlichen Contao-Hauptprojekts (dort, wo die Haupt-composer.json deiner Website liegt, nicht die des Bundles!).
Öffne die composer.json deines Contao-Projekts und füge den Block repositories hinzu. Das sieht dann in etwa so aus:
1{
2 "name": "meine-agentur/kundenprojekt",
3 "description": "Die Contao 5 Website des Kunden",
4 "type": "project",
5 "require": {
6 "php": "^8.1",
7 "contao/manager-bundle": "5.3.*",
8 "contao/calendar-bundle": "5.3.*",
9 "contao/news-bundle": "5.3.*"
10 },
11 "repositories": [
12 {
13 "type": "path",
14 "url": "/Users/deinname/Entwicklung/beachside-bundle",
15 "options": {
16 "symlink": true
17 }
18 }
19 ]
20}Wichtige Details zur Konfiguration:
type "path": Signalisiert Composer, dass es sich um einen lokalen Ordner handelt.
url: Hier trägst du den absoluten oder relativen Pfad zu dem Ordner ein, in den wir unser Bundle im ersten Schritt verschoben haben.
symlink "true": Das ist der absolute Gamechanger für die Entwicklung. Composer kopiert die Dateien nicht stumpf herüber, sondern erstellt eine symbolische Verknüpfung (Verweis) von deinem
vendor-Ordner direkt in deinen Entwicklungsordner.
Das Bundle lokal installieren
Nachdem wir der Hauptinstallation beigebracht haben, wo unser lokaler Code liegt, müssen wir das Paket nun regulär über das Terminal anfordern. Öffne dein Terminal im Hauptprojekt und führe folgenden Befehl aus:
composer require acme/beachside-bundle *@devWarum *@dev? Da unser lokales Paket noch keine echten Versionsnummern (Release-Tags wie 1.0.0) besitzt, sagen wir Composer mit diesem Zusatz, dass er einfach den aktuellen Entwicklungsstand (Dev) akzeptieren soll, egal welche Version gerade vorliegt.
Wenn der Befehl durchgelaufen ist, wirst du in der Konsolenausgabe eine Zeile finden, die lautet: Symlinking from /Users/deinname/Entwicklung/beachside-bundle.
Der Workflow-Vorteil
Deine Contao-Installation läuft jetzt wieder fehlerfrei! Aber das Beste daran: Durch den Symlink arbeiten beide Ordner absolut synchron.
Wenn du in deinem isolierten Ordner (~/Entwicklung/beachside-bundle/src/Controller/BookingController.php) eine Zeile PHP-Code änderst und speicherst, ist diese Änderung sofort in deiner Contao-Installation (unter vendor/acme/beachside-bundle/...) wirksam, ohne dass du auch nur einmal composer update ausführen musst. Du hast nun eine perfekt getrennte Architektur, aber denselben flüssigen Entwicklerkomfort wie vorher.
Jetzt, da wir unseren Code sicher isoliert und testbar gemacht haben, wird es Zeit, sich um die Versionskontrolle zu kümmern. Im nächsten Abschnitt initialisieren wir Git, erstellen die wichtigsten Ignore-Regeln und bereiten den Sprung in die Cloud zu GitHub vor.

Teil 3: Versionskontrolle mit Git und der perfekte .gitignore
Nachdem wir im vorherigen Schritt eine komfortable, synchronisierte Entwicklungsumgebung über Symlinks aufgebaut haben, widmen wir uns nun dem Fundament von Open Source: der Versionskontrolle.
Packagist ist kein Dateispeicher. Es hostet deinen Code nicht selbst. Packagist ist lediglich ein intelligentes Adressbuch, das auf ein öffentliches Git-Repository (in den meisten Fällen GitHub) verweist und dessen Struktur (wie Releases und Branches) ausliest. Um unser Paket bei Packagist anmelden zu können, müssen wir es also zuerst mit Git versionieren.
Öffne dein Terminal und navigiere in den isolierten Ordner deiner Erweiterung (z. B. ~/Entwicklung/beachside-bundle). Führe dort git init aus, um ein leeres lokales Repository zu erstellen.
Schritt 1: Der Türsteher – Die .gitignore Datei
Bevor du nun blind alle Dateien mit git add . hinzufügst, müssen wir dringend eine Schutzmaßnahme ergreifen. Es gibt Dateien in deinem Entwicklungsordner, die absolut nichts in einem öffentlichen Open-Source-Repository zu suchen haben.
Laden wir versehentlich den vendor-Ordner hoch, wird das Repository gigantisch groß und unbrauchbar. Laden wir IDE-Einstellungen hoch, zwingen wir anderen Entwicklern unsere lokalen Pfade auf.
Erstelle im Hauptverzeichnis deines Bundles eine Datei namens .gitignore und füge folgende Regeln ein:
1# Abhängigkeiten (werden vom Nutzer über Composer geladen)
2/vendor/
3
4# Lokale Composer Lock-Datei (bei Bundles ignorieren!)
5composer.lock
6
7# IDE und Betriebssystem-Dateien
8.idea/
9.vscode/
10*.swp
11.DS_Store
12Thumbs.db
13
14# Temporäre oder kompilierte Dateien
15/var/
16/public/build/
17node_modules/Der wichtigste Unterschied: composer.lock in Bundles
Vielleicht wunderst du dich, warum wir die composer.lock ignorieren. In einem normalen Contao-Kundenprojekt (einer "Application") ist es Best Practice, die composer.lock mit in Git einzuchecken. So stellt das Team sicher, dass auf dem Live-Server exakt dieselben Paketversionen installiert werden wie lokal.
Bei einer Erweiterung (Bundle/Library) verhält es sich jedoch exakt umgekehrt! Deine Erweiterung wird später in Hunderten verschiedenen Contao-Installationen laufen, die alle leicht unterschiedliche Versionen von Symfony oder anderen Abhängigkeiten installiert haben. Wenn du die composer.lock in dein Bundle eincheckst, schränkst du die Flexibilität von Composer bei der Auflösung der Abhängigkeiten künstlich ein. Die goldene Regel für Packagist lautet daher: In Bibliotheken und Bundles wird die composer.lock immer ignoriert.
Schritt 2: Der initiale Commit
Nachdem unsere .gitignore steht, ist dein Code sauber gefiltert. Nun können wir den aktuellen, aufgeräumten Stand der Strandkorb-Erweiterung einfrieren.
Führe nacheinander diese Befehle in deinem Bundle-Verzeichnis aus:
1# Zeigt dir an, welche Dateien für den Commit bereitstehen
2# (Hier darf der /vendor/ Ordner nicht mehr auftauchen!)
3git status
4
5# Fügt alle sauberen Dateien zum Staging-Bereich hinzu
6git add .
7
8# Erstellt den ersten festen Speicherpunkt
9git commit -m "Initial commit: Basic structure of the beachside booking system"Herzlichen Glückwunsch! Dein Code ist nun sauber versioniert. Das ist ein wichtiger Meilenstein für die professionelle Softwareentwicklung.
Aktuell existiert dieser Code jedoch nur lokal auf deiner eigenen Festplatte. Fällt dein Laptop aus, ist die Arbeit verloren. Und Packagist hat keine Möglichkeit, auf deine lokale Festplatte zuzugreifen. Im nächsten Teil bringen wir deinen Code in die Cloud und richten das GitHub-Repository ein.

Teil 4: Der Weg in die Cloud und Semantische Versionierung
Unser Code ist nun sauber vom Hauptprojekt getrennt, über eine .gitignore Datei geschützt und lokal in einem Git-Repository eingefroren. Doch damit andere Entwickler oder Packagist darauf zugreifen können, muss dieses lokale Repository ins Internet. Der weltweite Standard hierfür ist GitHub.
Schritt 1: Das GitHub-Repository erstellen
Logge dich auf GitHub ein und erstelle ein neues, leeres Repository.
Nenne es idealerweise so, wie du dein Paket in der
composer.jsongenannt hast, jedoch ohne den Vendor-Namen (in unserem Fall alsobeachside-bundle).Wichtig: Initialisiere das Repository nicht mit einer README,
.gitignoreoder Lizenzdatei, da wir diese Dateien bereits lokal in unserem Ordner haben.
Nachdem das leere Repository erstellt ist, zeigt dir GitHub eine URL an (z.B. git@github.com:dein-username/beachside-bundle.git). Kopiere diese.
Öffne nun wieder dein lokales Terminal im Ordner deiner Erweiterung und verknüpfe dein lokales Git mit dem neuen GitHub-Server:
# Füge GitHub als entferntes Ziel (Remote) hinzu
git remote add origin git@github.com:dein-username/beachside-bundle.git
# Lade deinen bisherigen Code in den Haupt-Branch (meist 'main' oder 'master') hoch
git push -u origin mainWenn du nun deine GitHub-Seite im Browser aktualisierst, siehst du deinen gesamten Code online.
Schritt 2: Semantische Versionierung (SemVer) verstehen
Composer und Packagist benötigen eine Möglichkeit zu erkennen, welche Version deines Bundles gerade aktuell ist. Wenn jemand composer require acme/beachside-bundle:^1.0 in sein Terminal tippt, muss Composer exakt wissen, wo der Code für die Version 1.0 zu finden ist.
In der PHP-Welt nutzen wir dafür die Semantische Versionierung (SemVer). Eine Versionsnummer besteht immer aus drei Ziffern: MAJOR.MINOR.PATCH (z.B. 1.2.4).
MAJOR (1.x.x): Wird erhöht, wenn du Änderungen machst, die nicht abwärtskompatibel sind (z.B. wenn du eine PHP-Klasse umbenennst oder Contao 4 Unterstützung streichst und nur noch Contao 5 unterstützt).
MINOR (x.2.x): Wird erhöht, wenn du neue Funktionen hinzufügst, die alte Funktionen nicht kaputt machen (z.B. ein neues Frontend-Modul für die Strandkorb-Ausgabe).
PATCH (x.x.4): Wird erhöht, wenn du lediglich Fehler (Bugs) behebst.
Schritt 3: Den ersten Release (Tag) erstellen
Anstatt diese Versionsnummern irgendwo in eine Datei zu schreiben, nutzt Composer die sogenannten Tags (Etiketten) von Git. Ein Tag ist wie ein Lesezeichen, das an einem ganz bestimmten Commit (Speicherpunkt) in deiner Historie befestigt wird.
Lass uns unsere erste offizielle Version 1.0.0 veröffentlichen. Führe im Terminal deines Bundle-Ordners aus:
# Erstelle das Etikett '1.0.0' an der aktuellen Stelle des Codes
git tag 1.0.0
# Lade dieses Etikett zu GitHub hoch
git push origin 1.0.0Wenn du nun auf GitHub schaust, wirst du rechts im Menü unter "Releases" oder "Tags" deine Version 1.0.0 sehen.
Das war der magische Moment! Dein Code ist nun nicht nur in der Cloud, sondern er hat auch eine saubere, von Maschinen lesbare Versionsnummer. Composer weiß nun exakt, was es herunterladen muss, wenn jemand nach der Version 1.0.0 fragt.
Im nächsten Teil kümmern wir uns um das Herzstück der PHP-Paketverwaltung: Wir registrieren unser neues, versioniertes GitHub-Repository endlich bei Packagist, damit es für die gesamte Contao-Community weltweit über die Kommandozeile verfügbar wird.

Teil 5: Die Veröffentlichung auf Packagist und automatische Updates
Unser Code liegt nun sicher in der Cloud auf GitHub und ist mit einer sauberen Versionsnummer (Tag 1.0.0) versehen. Jetzt kommt der wichtigste Schritt: Wir müssen unser Modul in das weltweite, zentrale Telefonbuch für PHP-Pakete eintragen – Packagist.org.
Erst wenn dein Paket dort registriert ist, versteht der Befehl composer require acme/beachside-bundle auf irgendeinem Server der Welt, woher er den Code eigentlich herunterladen soll.
Schritt 1: Das Paket bei Packagist einreichen
Der Prozess ist glücklicherweise extrem simpel und völlig kostenlos:
Öffne packagist.org in deinem Browser.
Klicke oben rechts auf "Create account" oder "Log in". Am einfachsten ist es, wenn du den Button "Log in with GitHub" nutzt. Das verknüpft dein Packagist-Konto direkt mit deinem GitHub-Profil.
Sobald du eingeloggt bist, klicke oben im Hauptmenü auf den großen Button "Submit" (Einreichen).
Du siehst nun ein einfaches Eingabefeld. Füge hier exakt die URL deines öffentlichen GitHub-Repositories ein (z. B.
https://github.com/dein-username/beachside-bundle).Klicke auf "Check".
Packagist verbindet sich nun mit GitHub, durchsucht deinen Code und sucht nach der magischen Datei: deiner composer.json. Wenn wir im ersten Teil dieses Tutorials alles richtig gemacht haben, liest Packagist nun den Namen (acme/beachside-bundle), die Beschreibung und deine angegebenen Keywords aus. Klicke abschließend auf "Submit", um die Registrierung abzuschließen.
Herzlichen Glückwunsch! Dein Contao-Bundle ist nun offiziell ein Open-Source-Paket!
Schritt 2: Den automatischen Webhook (Auto-Update) einrichten
Aktuell weiß Packagist, dass Version 1.0.0 existiert. Wenn du nun aber morgen einen Fehler findest, ihn in GitHub korrigierst und einen neuen Tag 1.0.1 erstellst, bekommt Packagist davon erst einmal nichts mit. Du müsstest jedes Mal manuell auf Packagist klicken und ein "Update" anfordern.
Um das zu automatisieren, nutzen wir einen sogenannten Webhook. Das ist eine Art Klingeldraht von GitHub zu Packagist, der sagt: "Hey, hier gibt es neuen Code, bitte aktualisiere dein Verzeichnis!"
Wenn du dich über GitHub bei Packagist eingeloggt hast, ist die Einrichtung meist ein Kinderspiel:
Gehe in deinem Packagist-Profil auf dein neu erstelltes Paket.
Oben rechts siehst du oft einen Hinweis, falls die GitHub-Integration noch nicht vollständig ist.
Alternativ gehst du in GitHub auf dein Repository -> Settings -> Webhooks.
Packagist bietet eine offizielle GitHub App an (zu finden unter github.com/apps/packagist). Installiere diese App für dein Repository.
Sobald das erledigt ist, läuft alles vollautomatisch. Sobald du einen neuen git push --tags ausführst, taucht die neue Version Sekunden später weltweit auf Packagist auf.
Schritt 3: Der globale Installationstest
Jetzt wird es Zeit, die Früchte unserer Arbeit zu ernten. Wir simulieren nun einen fremden Entwickler, der dein fantastisches Strandkorb-System in seinem eigenen Projekt nutzen möchte.
Erstelle lokal einen völlig neuen, leeren Ordner (z.B.
contao-test-projekt).Installiere ein frisches Contao 5 über die Kommandozeile.
Wichtig: Achte darauf, dass in der
composer.jsondieses neuen Projekts kein Path-Repository und kein lokaler Symlink konfiguriert ist!Führe den finalen Befehl aus:
composer require acme/beachside-bundleBeobachte dein Terminal. Composer wird sich mit Packagist.org verbinden, dein Paket acme/beachside-bundle in der Version 1.0.0 finden, die Abhängigkeiten (Contao Core) prüfen und den Code direkt in den /vendor/acme/beachside-bundle Ordner deines neuen Projekts herunterladen.
Dein Paket ist nun weltweit verfügbar und installierbar!
Doch einen Haken gibt es noch: Obwohl man es jetzt per Kommandozeile installieren kann, taucht es im Contao Manager (der grafischen Oberfläche) noch nicht in der Suchleiste auf. Wie wir unser Modul für die Contao-spezifische Suche indizieren und mit Meta-Daten (wie einem Logo oder deutschen Texten) anreichern, schauen wir uns im nächsten Teil an.

Teil 6: Sichtbarkeit im Contao Manager (Package Metadata)
Unser Strandkorb-Bundle ist nun auf Packagist registriert und lässt sich von jedem Entwickler per Kommandozeile (composer require acme/beachside-bundle) installieren. Das ist ein großartiger Erfolg!
Wenn du nun aber den grafischen Contao Manager öffnest und in der Suchleiste nach "Strandkorb" suchst, wirst du dein Paket vielleicht finden, aber es sieht extrem unauffällig aus. Es hat kein Logo, die Beschreibung ist starr auf Englisch (aus der composer.json) und es fehlen detaillierte Informationen.
Damit Erweiterungen im Contao Manager optisch ansprechend und mehrsprachig präsentiert werden, nutzt die Contao-Community ein spezielles, zentrales GitHub-Repository: contao/package-metadata.
Warum ein separates Repository?
Packagist.org bietet von Haus aus keine Möglichkeit, Projekt-Logos hochzuladen oder Beschreibungen in verschiedenen Sprachen (wie Deutsch und Französisch) zu hinterlegen. Die composer.json erlaubt nur eine einzige, meist englische description.
Um dieses Problem zu lösen, hat das Contao-Core-Team das Package-Metadata-Repository erschaffen. Der Contao Manager lädt sich in regelmäßigen Abständen die Daten aus diesem Repository herunter und reichert die nackten Packagist-Informationen damit an.
Schritt 1: Das Repository forken
Um deine Metadaten hinzuzufügen, musst du eine Änderung (einen "Pull Request") an diesem offiziellen Repository vornehmen.
Logge dich bei GitHub ein.
Navigiere zu
https://github.com/contao/package-metadata.Klicke oben rechts auf den Button "Fork". Dadurch erstellst du eine exakte Kopie dieses Repositories in deinem eigenen GitHub-Account.
Klone diesen Fork nun auf deinen lokalen Computer (z.B. mit
git clone).
Schritt 2: Deine Metadaten-Ordnerstruktur anlegen
In deinem lokal geklonten Repository findest du einen Ordner namens meta. Hier drin sind die Pakete exakt nach ihren Composer-Namen sortiert.
Erstelle im Ordner
metadeinen Vendor-Ordner (falls er noch nicht existiert), alsoacme.Erstelle darin den Ordner für dein Paket:
beachside-bundle.Dein Pfad lautet nun:
meta/acme/beachside-bundle/.
Schritt 3: Logo und Übersetzungen hinzufügen
In diesem neuen Ordner legst du nun deine Dateien ab.
1. Das Logo: Speichere ein Logo deiner Erweiterung (oder das Logo deiner Agentur) als logo.svg (bevorzugt) oder logo.png in diesem Ordner. Das Logo sollte quadratisch sein oder zumindest gut in einen quadratischen Rahmen passen.
2. Die englische Übersetzung (en.json): Erstelle eine Datei namens en.json für die internationale Standard-Anzeige:
{
"title": "Beachside Booking System",
"description": "A professional and highly customizable beach chair booking system for Contao 5. Includes interactive frontend modules and a robust backend management.",
"keywords": ["beach", "booking", "ecommerce", "rental"]
}3. Die deutsche Übersetzung (de.json): Erstelle eine Datei namens de.json, damit deutsche Nutzer im Contao Manager direkt in ihrer Muttersprache abgeholt werden:
{
"title": "Strandkorb Buchungssystem",
"description": "Ein professionelles und hochgradig anpassbares Strandkorb-Buchungssystem für Contao 5. Inklusive interaktiver Frontend-Module und einer robusten Backend-Verwaltung.",
"keywords": ["strand", "buchung", "ecommerce", "vermietung"]
}Du kannst hier auch noch weitere Links (wie support oder documentation) hinterlegen. Eine genaue Übersicht aller möglichen JSON-Felder findest du in der README.md des Metadata-Repositories.
Schritt 4: Den Pull Request (PR) einreichen
Wenn deine Dateien bereit sind, commitest du sie in deinen Fork und sendest sie an das Haupt-Repository zurück:
git add meta/acme/beachside-bundle/*
git commit -m "Add metadata for acme/beachside-bundle"
git push origin mainGehe nun auf die GitHub-Seite deines Forks. GitHub wird dir einen großen grünen Button "Compare & pull request" anzeigen. Klicke darauf und reiche deinen Code ein.
Im Hintergrund starten nun automatisierte GitHub Actions (Linters und Spellchecker). Sie prüfen, ob dein JSON gültig ist und ob du dich vertippt hast. Sobald diese Checks "grün" sind, wird ein Mitglied des Contao-Teams deinen Pull Request freigeben (mergen).
Kurze Zeit später (meist innerhalb von 30 Minuten, wenn der Manager-Index aktualisiert wird), erstrahlt deine Erweiterung im Contao Manager mit Logo und perfekten deutschen Texten!
Dein Modul ist nun professionell veröffentlicht. Doch wie stellst du sicher, dass dein Code auf Dauer fehlerfrei bleibt und den strengen Contao-Coding-Standards entspricht? Im nächsten Teil widmen wir uns der Code-Qualität und Automatisierungstools wie dem Easy Coding Standard (ECS) und Rector.

Teil 7: Code-Qualität und Automatisierung (CI/CD)
Dein Strandkorb-Bundle ist nun weltweit verfügbar, mit einem Logo im Contao Manager ausgestattet und perfekt übersetzt. Wenn du ein Open-Source-Projekt betreibst, wird früher oder später der Tag kommen, an dem ein anderer Entwickler aus der Community einen Fehler findet, ihn behebt und dir den Code über GitHub als "Pull Request" zurücksendet.
Das ist großartig! Aber woher weißt du, dass dieser fremde Code deinen Qualitätsansprüchen genügt? Nutzt der Entwickler Leerzeichen oder Tabs? Hat er sich an die offiziellen Symfony- und Contao-Konventionen gehalten? Um solche Diskussionen zu vermeiden, automatisieren professionelle Entwickler die Code-Prüfung.
Schritt 1: Der Contao Easy Coding Standard (ECS)
Die Contao-Core-Entwickler haben ein fantastisches Paket namens contao/easy-coding-standard bereitgestellt. Es vereint die besten Code-Analyse-Tools der PHP-Welt und enthält ein vorgefertigtes Regelwerk, das exakt den Contao-Standards entspricht.
Öffne dein lokales Terminal im Verzeichnis deiner Erweiterung und installiere das Tool. Wichtig: Wir nutzen das Flag --dev, da dieses Tool nur für die Entwicklung benötigt wird und nicht auf den Live-Servern der Endkunden landen darf!
composer require contao/easy-coding-standard --devErstelle nun im Hauptverzeichnis deines Bundles eine Datei namens ecs.php. Das ist die Konfigurationsdatei, die dem Tool sagt, welche Ordner geprüft werden sollen:
1<?php
2
3declare(strict_types=1);
4
5use Contao\EasyCodingStandard\Fixer\TypeHintOrderFixer;
6use Symplify\EasyCodingStandard\Config\ECSConfig;
7
8return ECSConfig::configure()
9 ->withSets([__DIR__ . '/vendor/contao/easy-coding-standard/config/contao.php'])
10 ->withPaths([
11 __DIR__ . '/src',
12 __DIR__ . '/tests',
13 ])
14 ->withSkip([
15 // Hier könntest du spezielle Dateien von der Prüfung ausschließen
16 ])
17;Ab sofort kannst du deinen gesamten Code mit einem einfachen Konsolenbefehl auf Herz und Nieren prüfen lassen:
vendor/bin/ecs checkDas Tool wird dir vermutlich beim ersten Mal einige Fehler (wie falsche Einrückungen oder fehlende Typisierungen) um die Ohren hauen. Das Geniale an ECS ist jedoch, dass es 95 % dieser Fehler vollautomatisch für dich korrigieren kann! Hänge einfach den --fix Parameter an:
vendor/bin/ecs check --fixDein Code sieht nun exakt so professionell formatiert aus wie der offizielle Contao-Core!
Schritt 2: Continuous Integration mit GitHub Actions
Es ist mühsam, diesen Befehl vor jedem Upload manuell auszuführen. Wir überlassen diese Arbeit ab sofort GitHub. Mit sogenannten "GitHub Actions" können wir Server in der Cloud anweisen, unseren Code bei jeder Änderung automatisch zu testen.
Erstelle in deinem Bundle-Verzeichnis folgende Ordnerstruktur: .github/workflows/ und darin eine Datei namens ci.yaml:
1name: CI
2
3on:
4 push:
5 branches: [ main ]
6 pull_request:
7 branches: [ main ]
8
9jobs:
10 coding-standard:
11 name: Easy Coding Standard
12 runs-on: ubuntu-latest
13 steps:
14 - name: Checkout Code
15 uses: actions/checkout@v4
16
17 - name: Setup PHP
18 uses: shivammathur/setup-php@v2
19 with:
20 php-version: '8.1'
21 coverage: none
22
23 - name: Install Composer dependencies
24 run: composer install --prefer-dist --no-progress
25
26 - name: Run ECS
27 run: vendor/bin/ecs check --no-progress-barWas passiert hier? Sobald du (oder ein anderer Entwickler) neuen Code zu GitHub pusht, startet GitHub im Hintergrund einen virtuellen Ubuntu-Server. Dieser installiert PHP 8.1, lädt deine Abhängigkeiten über Composer herunter und führt unseren ecs check Befehl aus.
Findet das Tool einen Fehler, wird der Upload (bzw. der Pull Request des fremden Entwicklers) mit einem dicken roten Kreuz (❌) markiert. So gelangt nie wieder schlecht formatierter Code in dein geliebtes Strandkorb-System. Sind alle Checks "grün" (✅), kannst du den Code guten Gewissens freigeben.
Exkurs: Zukunftsfähigkeit mit Rector
Wenn du in Zukunft dein Bundle von Contao 4 auf Contao 5 (oder von 5 auf 6) aktualisieren möchtest, ändern sich oft Core-Klassen oder Parameter. Auch hierfür gibt es ein Automatisierungs-Tool: Rector (contao/contao-rector). Rector analysiert deinen Code und schreibt veraltete Methodenaufrufe (Deprecations) vollautomatisch in die neue Contao-Syntax um. Ein absolutes Muss für Entwickler, die Zeit sparen wollen!
Unsere Erweiterung ist nun auf Enterprise-Niveau: Versioniert, automatisiert, getestet und weltweit verteilt. Im nächsten und abschließenden achten Teil fassen wir den gesamten Workflow zu einer ultimativen Checkliste zusammen und klären, wie du ein erfolgreiches Release-Management betreibst.

Teil 8: Die ultimative Release-Checkliste und Community-Management
Wir haben es geschafft! Was als verstecktes lokales Skript in einem Kundenprojekt begann, ist nun eine weltweit verfügbare, professionell getestete und voll automatisiert verteilte Open-Source-Erweiterung für Contao 5. Dein Code lebt in der Cloud und steht jedem Entwickler zur Verfügung.
Doch die Veröffentlichung auf Packagist ist nicht das Ende der Reise, sondern erst der Anfang des sogenannten "Release Managements". Open-Source-Software lebt von Wartung, Dokumentation und der Interaktion mit der Community. Um sicherzustellen, dass dein Bundle nicht nur gefunden, sondern auch aktiv genutzt und geliebt wird, solltest du diese finale Best-Practice-Checkliste verinnerlichen.
1. Die README.md – Das Schaufenster deines Codes
Entwickler sind ungeduldig. Wenn jemand dein Paket im Contao Manager oder auf GitHub entdeckt, entscheidet er innerhalb von zehn Sekunden, ob er es installiert. Die README.md im Hauptverzeichnis deines Bundles ist dein Verkaufsargument.
Eine professionelle README enthält immer folgende Abschnitte:
Titel & Badges: Nutze kleine Status-Icons (Badges von z.B. shields.io), die anzeigen, dass dein CI/CD-Test grün ist, welche PHP-Version benötigt wird und wie oft das Paket heruntergeladen wurde.
Beschreibung: Ein kurzer Satz, welches Problem das Bundle löst (z. B. "Ein hochgradig anpassbares Strandkorb-Buchungssystem").
Installation: Der exakte Composer-Befehl (
composer require acme/beachside-bundle).Konfiguration: Wie richtet man das Bundle nach der Installation ein? (Welche DCA-Tabellen müssen aktualisiert werden? Gibt es Frontend-Module, die eingebunden werden müssen?)
Nutzung (Usage): Ein kurzes Code- oder Praxisbeispiel.
2. Das Führen eines Changelogs
Wenn du ein Update veröffentlichst (z.B. Version 1.1.0), wollen die Nutzer wissen, was sich geändert hat, bevor sie blind auf den "Update"-Button klicken. Es ist in der PHP-Welt absoluter Standard, eine Datei namens CHANGELOG.md im Hauptverzeichnis zu pflegen.
Halte dich dabei an das Format von Keep a Changelog. Es unterteilt Änderungen logisch:
1## [1.1.0] - 2026-03-15
2### Added
3- Neues Frontend-Modul für die Strandkorb-Verfügbarkeitsanzeige hinzugefügt.
4- Unterstützung für Contao 5.7 LTS.
5
6### Fixed
7- Fehler bei der Preisberechnung an Schaltjahren behoben.3. Strenge Disziplin bei Semantischer Versionierung (SemVer)
Wir haben SemVer (MAJOR.MINOR.PATCH) bereits kennengelernt. Das Wichtigste für den dauerhaften Erfolg deines Bundles ist das Vertrauen der Nutzer.
Wenn du einen Fehler behebst, erhöhe nur die letzte Ziffer (
1.1.1).Wenn du ein neues, cooles Feature einbaust, erhöhe die mittlere Ziffer (
1.2.0).Die goldene Regel: Wenn du eine Funktion entfernst, den Namespace einer Klasse änderst oder die Datenbankstruktur inkompatibel umbaust, musst du die vorderste Ziffer erhöhen (
2.0.0- Breaking Change). Wenn du einen "Breaking Change" in einem Patch-Release versteckst, werden die Websites anderer Entwickler beim nächsten unbedachten Composer-Update abstürzen. Das zerstört das Vertrauen in dein Modul sofort.
4. Issues und Pull Requests professionell managen
Früher oder später wird ein Nutzer auf GitHub ein "Issue" (einen Fehlerbericht) eröffnen.
Sei dankbar: Jemand hat sich die Zeit genommen, deine Software zu testen und einen Fehler zu melden.
Fordere Reproduzierbarkeit: Bitte den Nutzer freundlich, genau zu beschreiben, unter welcher Contao- und PHP-Version der Fehler auftritt. Oft hilft ein kleines Screenshot-Video.
Wenn ein Pull Request kommt: Ein anderer Entwickler hat den Code für dich repariert! Führe einen Code-Review durch, bedanke dich und klicke auf "Merge". Erstelle danach zeitnah ein neues Release (Tag), damit der Fehler auf Packagist behoben wird.

Teil der Serie
Contao 5 Masterclass: The Beachside Project
Contao 5 Bundle Entwicklung: Die Masterclass für echte Entwickler Pillar
Contao 5 Bundle Setup: Das Fundament für professionelle Erweiterungen
Contao 5 Doctrine Entities: Moderne Datenmodellierung statt SQL-Chaos
Contao 5 DCA: Perfekte Backend-Masken für Entities erstellen
Contao 5 Doctrine Relations: Wir bauen die Buchungs-Logik
Contao 5 Service Layer: Business-Logik sauber kapseln
Contao 5 Unit Testing: Code-Absicherung mit PHPUnit & Test-Case
Contao 5 Twig Templates: Frontend-Ausgabe mit Fragment Controllern
Contao 5 Symfony Forms: Professionelle Formularverarbeitung
Contao 5 Notification Center: Zentrale Kommunikation & E-Mail-Workflows
Contao 5 Backend Dashboards & Custom Routing
Contao 5 Console Commands: Automatisierung & Hintergrund-Jobs
Contao 5 Headless CMS API: REST-Schnittstellen bauen
Contao 5 Performance, Caching & Zero-Downtime Deployment
Contao 5 Erweiterung veröffentlichen: Packagist & Open Source
Häufig gestellte Fragen (FAQ)
Willkommen in der Contao Open-Source-Familie!
Willkommen in der Contao Open-Source-Familie! Du hast den kompletten Lifecycle der modernen Softwareentwicklung gemeistert. Wenn du in Zukunft eine weitere Contao 5 Erweiterung veröffentlichen willst, kennst du nun die exakte Architektur: Code isolieren, Git-Versionierung verstehen, CI/CD-Pipelines aufbauen und Metadaten für den Contao Manager bereitstellen.
Durch das Teilen deiner Arbeit auf Packagist sparst du nicht nur dir selbst bei zukünftigen Projekten enorm viel Zeit (ein einfacher composer require-Befehl genügt nun), sondern du gibst der gesamten Contao-Community etwas unglaublich Wertvolles zurück.
Feiere dein erstes Release, teile den GitHub-Link im offiziellen Contao Slack-Kanal oder Forum und sei stolz darauf, nun ein aktiver Contao-Core-Contributor und Open-Source-Entwickler zu sein!

Dietrich Bojko
Senior Webentwickler
Webinteger arbeitet seit vielen Jahren produktiv mit
Linux-basierten Entwicklungsumgebungen unter Windows.
Der Fokus liegt auf
performanten Setups mit WSL 2, Docker, PHP, Node.js und modernen
Build-Tools in realen Projekten –
nicht auf theoretischen Beispielkonfigurationen.
Die Artikel dieser Serie entstehen direkt aus dem täglichen Einsatz in Kunden- und Eigenprojekten und dokumentieren bewusst auch typische Fehler, Engpässe und bewährte Workarounds.


