
Contao 5 DCA: Perfekte Backend-Masken für Entities erstellen

Das Contao 5 DCA (Data Container Array) ist der Schlüssel, um deine Datenbank-Entities im Backend bearbeitbar zu machen. Deine Datenbank steht zwar dank Doctrine aus Teil 2, aber wie kommen die Daten da rein? Sicher nicht über phpMyAdmin. Dein Kunde – der Strandkorb-Vermieter in Binz – braucht eine saubere Oberfläche.
Hier zeigt das Contao 5 DCA seine Stärke. Es ist eine der mächtigsten Funktionen des CMS und generiert aus einem PHP-Array vollautomatisch Formulare, Listen, Sortierfunktionen und Suchfilter.
Der Gamechanger im Contao 5 DCA: Früher hast du im DCA auch die Datenbank-Felder definiert ('sql' => "varchar(255)..."). Das machen wir nicht mehr! Da wir Entities nutzen, kümmert sich Doctrine um die Datenbank. Das Contao 5 DCA kümmert sich nur noch um die Optik (UI).
Das ist der Plan für dein Contao 5 DCA:
Die Datei am richtigen Ort anlegen.
Die Listenansicht konfigurieren.
Die Paletten und Felder definieren.
Cache leeren und das Ergebnis feiern.
1. Die Datei: Wo gehört das DCA hin?
Contao sucht in Bundles automatisch im Ordner contao/dca. Damit dein Contao 5 DCA geladen wird, musst du die Datei korrekt benennen. Erstelle diesen Ordner in deinem Bundle. Der Dateiname muss exakt so heißen wie deine Tabelle.
Pfad: /src/BeachsideBundle/contao/dca/tl_beach_chair.php
2. Die Grundkonfiguration (Config & List)
Wir starten mit dem Rahmen für das Contao 5 DCA. Kopiere diesen Code in die Datei. Wir sagen dem System, dass es sich um eine Tabelle handelt (Table) und wie die Datensätze aufgelistet werden sollen.
1<?php
2
3use Contao\DC_Table;
4
5$GLOBALS['TL_DCA']['tl_beach_chair'] = [
6 // 1. Konfiguration
7 'config' => [
8 'dataContainer' => DC_Table::class,
9 'enableVersioning' => true,
10 'sql' => [
11 'keys' => [
12 'id' => 'primary',
13 ],
14 ],
15 ],
16
17 // 2. Listenansicht (Das Grid)
18 'list' => [
19 'sorting' => [
20 'mode' => 1, // Sortierung nach einem festen Feld
21 'fields' => ['chairNumber'], // Sortiere nach Nummer
22 'flag' => 1, // Aufsteigend
23 'panelLayout' => 'filter;search,limit', // Suchfeld und Filter oben anzeigen
24 ],
25 'label' => [
26 'fields' => ['chairNumber', 'title', 'pricePerDay'],
27 'showColumns' => true, // Tabellarische Ansicht
28 'format' => '%s - %s [%s Cent]', // Platzhalter für die Ausgabe
29 ],
30 'global_operations' => [
31 'all' => [
32 'label' => &$GLOBALS['TL_LANG']['MSC']['all'],
33 'href' => 'act=select',
34 'class' => 'header_edit_all',
35 'attributes' => 'onclick="Backend.getScrollOffset()" accesskey="e"'
36 ],
37 ],
38 'operations' => [
39 'edit' => [
40 'href' => 'act=edit',
41 'icon' => 'edit.svg',
42 ],
43 'delete' => [
44 'href' => 'act=delete',
45 'icon' => 'delete.svg',
46 'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? null) . '\'))return false;Backend.getScrollOffset()"',
47 ],
48 'show' => [
49 'href' => 'act=show',
50 'icon' => 'show.svg',
51 ],
52 ],
53 ],
54
55 // ... Palettes und Fields kommen gleich hier drunter
56];Wichtig: Auch wenn wir Doctrine nutzen, benötigt das Contao 5 DCA im config-Bereich noch die Info über den Primary Key (id). Aber wir definieren keine Spalten-Definitionen mehr hier!
3. Palettes & Fields: Das Formular bauen
Jetzt definieren wir, was editierbar ist. Das Contao 5 DCA nutzt "Palettes" für das Layout und "Fields" für die Logik. Füge diesen Teil unter das list-Array in dieselbe Datei ein.

1// 3. Paletten (Das Layout des Formulars)
2 'palettes' => [
3 'default' => '{title_legend},title,chairNumber;{details_legend},pricePerDay,description',
4 ],
5
6 // 4. Felder (Die Eingabemöglichkeiten)
7 'fields' => [
8 'id' => [
9 'sql' => "int(10) unsigned NOT NULL auto_increment" // ID braucht SQL definition für Contao Core Logik
10 ],
11 'tstamp' => [
12 'sql' => "int(10) unsigned NOT NULL default '0'"
13 ],
14 'title' => [
15 'inputType' => 'text',
16 'search' => true,
17 'eval' => ['mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50'],
18 // KEIN 'sql' hier! Das macht die Entity via Doctrine.
19 ],
20 'chairNumber' => [
21 'inputType' => 'text',
22 'search' => true,
23 'sorting' => true,
24 'eval' => ['mandatory' => true, 'maxlength' => 10, 'unique' => true, 'tl_class' => 'w50'],
25 ],
26 'pricePerDay' => [
27 'inputType' => 'text',
28 'eval' => ['mandatory' => true, 'rgxp' => 'digit', 'tl_class' => 'w50'],
29 'label' => ['Preis pro Tag (in Cent)', 'Bitte Preis in Cent eingeben (1000 = 10,00€)'],
30 ],
31 'description' => [
32 'inputType' => 'textarea',
33 'eval' => ['rte' => 'tinyMCE', 'tl_class' => 'clr'], // Rich Text Editor
34 ],
35 ],Analyse des Codes:
Kein SQL: Bei
title,chairNumberusw. fehlt dersql-Key. Das ist korrekt! Contao schaut in die Datenbank, sieht dass die Spalte (dank Doctrine) existiert, und ist zufrieden.rgxp=>digit: Wir zwingen den User, nur Zahlen einzugeben (für unseren Cent-Preis).rte=>tinyMCE: Wir binden mit einer Zeile den kompletten HTML-Editor ein.
4. Der letzte Schliff: Menü-Eintrag
Das DCA ist da, aber man sieht es im Backend-Menü noch nicht. Wir müssen Contao sagen: "Zeig einen Link zu tl_beach_chair an."
Da wir in Contao 4.13+ / 5 arbeiten, machen wir das modern über eine Konfigurationsdatei, nicht mehr über die config.php. Da wir aber gerade ein Bundle bauen, ist der klassische Weg über die config.php innerhalb des Bundles immer noch der Standard, um Backend-Module zu registrieren.
Erstelle /src/BeachsideBundle/contao/config/config.php:
1<?php
2
3use Acme\BeachsideBundle\Model\BeachChairModel; // Kommt gleich!
4
5// Backend Modul registrieren
6$GLOBALS['BE_MOD']['content']['beach_chairs'] = [
7 'tables' => ['tl_beach_chair'],
8];5. Cache leeren & Testen
Führe im Terminal aus:
vendor/bin/contao-console cache:clearLogge dich ins Contao Backend ein. Links im Menü unter "Inhalte" solltest du jetzt "beach_chairs" sehen. (Der Name ist noch klein geschrieben, weil wir keine Übersetzungen angelegt haben – das machen wir beim Polish).

Klicke drauf. Klicke auf "Neu". Siehst du die Maske? Titel, Nummer, Preis, Beschreibung? Speichere einen Strandkorb. Wenn er in der Liste erscheint: BÄM! Du hast gerade eine volle CRUD-Applikation (Create, Read, Update, Delete) gebaut.
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
Häufig gestellte Fragen (FAQ)
Zusammenfassung & Ausblick
Du hast gelernt, wie man ein Contao 5 DCA erstellt, das auf Doctrine Entities basiert.
DCA-Datei angelegt.
Listenansicht konfiguriert.
Eingabefelder ohne SQL-Redundanz definiert.
Jetzt haben wir Strandkörbe im System. Aber ein Buchungssystem braucht mehr als nur Möbel. Es braucht Buchungen, Zeiträume und Relationen. Im nächsten Teil wird es technisch anspruchsvoll: Wir verknüpfen Tabellen miteinander (1-zu-n).
Nächster Artikel: Die Buchungs-Entität & Relationen

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.


