2.2 Eigene Daten im Backend verwalten
Nachdem wir im letzten Beitrag eine Hilfeseite für das Backend erstellt haben, wollen wir nun unsere erste eigene Tabelle für die Verwaltung von benutzerdefinierten Daten erstellen.
Später werden wir noch Inhaltselemente und Module implementieren. All dies benötigt aber in der Regel Anpassungen am DCAs (Data Container Array). Es gibt ein DCA pro Tabelle und es ermöglicht uns nicht nur unsere eigenen Tabellen in Contao zu integrieren, sondern auch die internen Tabellen von Contao zu verändern und zu erweitern. Es ist also ein sehr mächtiges Werkzeug.
Auch wenn dieser Artikel für alle weiteren Kapitel sehr wichtig ist, wird es nicht möglich sein, alle Teile des DCA hier zu beleuchten. Wir werden in den weiteren Beiträgen noch auf zusätzliche Aspekte eingehen. Es sei aber schon einmal die Dokumentation des DCA empfohlen.
(Bitte auch in diesem Artikel wieder den Vendor-Namespace (oder entsprechenden Ordner)
durch Euren eignen ersetzen und nicht Ctocb
verwenden! Danke!)
Listing der Dateien
Hier wieder das Listing der verwendeten Dateien.
src/
└── Ctocb
└── Example
├── Classes
│ └── Contao
│ └── Manager
│ └── Plugin.php
├── Resources
│ └── contao
│ ├── config
│ │ └── config.php
│ ├── dca
│ │ └── tl_testtable.php
│ └── languages
│ └── de
│ ├── modules.php
│ └── tl_testtable.php
├── composer.json
└── CtocbExampleBundle.php
Ich werde ab jetzt nur noch auf das Manager Plugin und die composer.json
eingehen, wenn dort neue Einträge vorhanden
sind. Wenn man von den Standarddateien absieht, besteht unsere Erweiterung damit aus der DCA-Datei und der Sprachdatei
der Tabelle tl_testtable
.
Config
In der Datei /src/Ctocb/Example/Resources/contao/config/config.php
geben wir unsere Tabelle für Contao bekannt.
<?php declare(strict_types=1);
$GLOBALS['BE_MOD']['module_test_group'] = [
'module_test_table' => [
'tables' => ['tl_testtable']
]
];
module_test_group
ist unsere Gruppe, genau wie im letzten Artikel. Unser Menüpunkt heißt diesmal module_test_table
.
Die meisten einfachen Tabellen haben einen eigenen Menüpunkt. Anders ist es bei Kindtabellen, die über Ihre
Elterntabelle aufgerufen werden. Aber diese behandeln wir später.
Mit tables
geben wir an, welche Tabellen unsere Erweiterung nutzt. Es muss ein Array zugewiesen werden, da eine
Erweiterung mehrere Tabellen verwenden kann (z. B. bei Kindtabellen).
DCA
Da das DCA sehr mächtig ist, ist es auch entsprechend umfangreich. Ich werde im weiteren Verlauf auf einzelne Abschnitte eingehen und diese separat zeigen. Zur besseren Übersicht wird am Ende das komplette DCA noch einmal als komplettes Listing zu sehen sein.
Die Datei muss unter /src/Ctocb/Example/Resources/contao/dca/tl_testtable.php
gespeichert werden. Wichtig ist, dass
sie den Namen der Tabelle trägt, da Contao sie sonst nicht finden kann. Der Grundaufbau sieht so aus:
<?php declare(strict_types=1);
$table = 'tl_testtable';
$GLOBALS['TL_DCA'][$table] = [
// ... Abschnitte
];
Da der Name der Tabelle sehr oft verwendet wird, habe ich ihn in eine Variable ausgelagert.
DCA > config
Im Abschnitt config
wird die Tabelle konfiguriert. Hier können u. a. auch load
- und submit
-Callbacks definiert
werden (worauf wir in einem der nächsten Beiträge eingehen). Hier ein einfaches Beispiel für den Abschnitt config
:
// Config
'config' => [
'dataContainer' => \Contao\DC_Table::class,
'enableVersioning' => true,
'sql' => ['keys' => ['id' => 'primary']]
],
Alle Details findet man in der Referenz des Handbuchs, im Abschnit Config.
DCA > list
Im Abschnitt list
wird das Aussehen der Auflistung der Datensätze im Backend festgelegt. Unter
sorting wird mit mode
und flag
festgelegt, wie die
Datensätze sortiert werden sollen. Mit fields
werden die Felder angegeben, nach denen sortiert wird. panelLayout
legt das Aussehen der Such- und Filterleiste fest.
Im Abschnitt label wird die Benennung der Datensätze in der
Liste konfiguriert. fields
gibt die Felder an, die Angezeigt werden sollen und format
das Format
(s. sprintf für Details).
Daneben gibt es noch globale Operationen, die oben rechts angezeigt werden und die ganze Tabelle betreffen. Ein Beispiel hierfür ist "Mehrere Bearbeiten". Außerdem gibt es "noramle" Operationen, die für einen Datensatz durchgeführt werden (z. B. Bearbeiten, Kopieren, ...). In einem späteren Kapitel werden wir eine eigene Toggle-Funktion mit einer solchen Operation erstellen.
'list' => [
'sorting' => [
'mode' => 1,
'fields' => ['title'],
'panelLayout' => 'sort,filter;search,limit',
'flag' => 1
],
'label' => [
'fields' => ['title'],
'format' => '%s'
],
'global_operations' => [
'all' => [
'label' => &$GLOBALS['TL_LANG']['MSC']['all'],
'href' => 'act=select',
'class' => 'header_edit_all',
'attributes' => 'onclick="Backend.getScrollOffset();" accesskey="e"'
]
],
'operations' => [
'edit' => [
'label' => &$GLOBALS['TL_LANG'][$table]['edit'],
'href' => 'act=edit',
'icon' => 'edit.svg'
],
'copy' => [
'label' => &$GLOBALS['TL_LANG'][$table]['copy'],
'href' => 'act=copy',
'icon' => 'copy.svg'
],
'delete' => [
'label' => &$GLOBALS['TL_LANG'][$table]['delete'],
'href' => 'act=delete',
'icon' => 'delete.svg',
'attributes' => 'onclick="if(!confirm(\'' . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] . '\'))return false;Backend.getScrollOffset()"'
],
'show' => [
'label' => &$GLOBALS['TL_LANG'][$table]['show'],
'href' => 'act=show',
'icon' => 'show.svg'
]
]
],
Will man bestimmte Opterationen nicht zur Verfügung stellen, z. B. weil man das Kopieren von Datensätzen unterbinden möchte, können die entsprechenden Operationen einfach aus dem Abschnitt entfernt werden.
DCA > palettes
Die Paletten beinhalten die Felder, die im Backend angezeigt werden sollen. In unserem Beispiel gibt es nur die
Palette default
, dies ist die Standardpalette. Paletten können aber auch anhand eines Feldes "gewählt" werden.
Ein Beispiel ist die Tabelle tl_content
, hier wird anhand des Typs des Inhaltselements die Palette gewählt und dann
nur die entsprechenden Felder angezeigt.
'palettes' => [
'default' => '{title_legend},title,content;'
]
Die Strings in den geschweiften Klammern, sind Gruppen. Diese werden im Backend grau dargestellt und können zu geklappt werden. Jeder Gruppe wird mit einem Semikolon nach den Feldern geschlossen. Die Felder werden einfach durch ein Komma getrennt. Es ergibt sich also folgendes Format:
'{Gruppe_01},Feld_01,Feld_02;{Gruppe_02},Feld_03,Feld_04;'
DCA > fields
Nun kommen wir endlich zu den eigentlichen Feldern unserer Tabelle. Ich habe in diesem Beispiel ein Feld für den Titel
und eins für den Inhalt vorgesehen. Zusätzlich muss jede Tabelle noch das Feld id
haben. Das Feld tstamp
enthält
das Änderungsdatum des Datensatzes. Ist es nicht vorhanden, löscht Contao die Datensätze, weil davon ausgegangen wird,
dass sie nicht mehr benötigt werden. (Etwas vereinfacht ausgedrückt.) Das Feld title
ist ein einfaches Textfeld,
mit einer Maximallänge von 255. Das Feld content
ist ein Textarea mit TinyMCE-Editor.
'fields' => [
'id' => [
'sql' => 'int(10) unsigned NOT NULL auto_increment'
],
'tstamp' => [
'sql' => "int(10) unsigned NOT NULL default '0'"
],
'title' => [
'exclude' => true,
'inputType' => 'text',
'eval' => ['mandatory'=>true, 'maxlength'=>255, 'tl_class' => 'w50'],
'sql' => "varchar(255) NOT NULL default ''"
],
'content' => [
'exclude' => true,
'inputType' => 'textarea',
'eval' => ['mandatory'=>true, 'tl_class' => 'clr long', 'rte'=>'tinyMCE'],
'sql' => "text NULL"
]
]
DCA komplett
Hier noch einmal das komplette DCA.
<?php declare(strict_types=1);
$table = 'tl_testtable';
$GLOBALS['TL_DCA'][$table] = [
// Config
'config' => [
'dataContainer' => \Contao\DC_Table::class,
'enableVersioning' => true,
'sql' => ['keys' => ['id' => 'primary']]
],
// List
'list' => [
'sorting' => [
'mode' => 1,
'fields' => ['title'],
'panelLayout' => 'sort,filter;search,limit',
'flag' => 1
],
'label' => [
'fields' => ['title'],
'format' => '%s'
],
'global_operations' => [
'all' => [
'label' => &$GLOBALS['TL_LANG']['MSC']['all'],
'href' => 'act=select',
'class' => 'header_edit_all',
'attributes' => 'onclick="Backend.getScrollOffset();" accesskey="e"'
]
],
'operations' => [
'edit' => [
'label' => &$GLOBALS['TL_LANG'][$table]['edit'],
'href' => 'act=edit',
'icon' => 'edit.svg'
],
'copy' => [
'label' => &$GLOBALS['TL_LANG'][$table]['copy'],
'href' => 'act=copy',
'icon' => 'copy.svg'
],
'delete' => [
'label' => &$GLOBALS['TL_LANG'][$table]['delete'],
'href' => 'act=delete',
'icon' => 'delete.svg',
'attributes' => 'onclick="if(!confirm(\'' . ($GLOBALS['TL_LANG']['MSC']['deleteConfirm'] ?? '') . '\'))return false;Backend.getScrollOffset()"'
],
'show' => [
'label' => &$GLOBALS['TL_LANG'][$table]['show'],
'href' => 'act=show',
'icon' => 'show.svg'
]
]
],
// Palettes
'palettes' => [
'default' => '{title_legend},title,content;'
],
// Fields
'fields' => [
'id' => [
'sql' => 'int(10) unsigned NOT NULL auto_increment'
],
'tstamp' => [
'sql' => "int(10) unsigned NOT NULL default '0'"
],
'title' => [
'exclude' => true,
'inputType' => 'text',
'eval' => ['mandatory'=>true, 'maxlength'=>255, 'tl_class' => 'w50'],
'sql' => "varchar(255) NOT NULL default ''"
],
'content' => [
'exclude' => true,
'inputType' => 'textarea',
'eval' => ['mandatory'=>true, 'tl_class' => 'clr long', 'rte'=>'tinyMCE'],
'sql' => "text NULL"
]
]
];
Sprachdateien
Sprachdatei für die Tabelle
Damit unsere Felder richtig beschriftet werden, legen wir noch eine Sprachdatei für unsere Tabelle an. Sie muss
ebenfalls den Namen der Tabelle tragen, damit Contao sie findet und wird unter
/src/Ctocb/Example/Resources/contao/languages/de/tl_testtable.php
gespeichert.
<?php declare(strict_types=1);
// Tablename
$table = 'tl_testtable';
// Elementname
$element = 'Datensatz';
// Fields
$GLOBALS['TL_LANG'][$table]['title'] = ['Title', 'Bitte geben Sie den Titel ein.'];
$GLOBALS['TL_LANG'][$table]['content'] = ['Inhalt', 'Bitte geben Sie den Inhalt ein.'];
// Legends (Einträge in palettes in den geschweiften Klammern)
$GLOBALS['TL_LANG'][$table]['title_legend'] = 'Title';
// Beschriftung der Buttons
$GLOBALS['TL_LANG'][$table]['new'] = ['Neuen ' . $element, 'Neuen ' . $element . ' anlegen'];
$GLOBALS['TL_LANG'][$table]['edit'] = [$element . ' bearbeiten', $element . ' mit der ID %s bearbeiten'];
$GLOBALS['TL_LANG'][$table]['copy'] = [$element . ' kopieren', $element . ' mit der ID %s kopieren'];
$GLOBALS['TL_LANG'][$table]['delete'] = [$element . ' löschen', $element . ' mit der ID %s löschen'];
$GLOBALS['TL_LANG'][$table]['show'] = [$element . ' anzeigen', 'Details des ' . $element . 'es mit der ID %s anzeigen'];
Die Übersetzung für die Felder kann im DCA mit 'label' angegeben werden. Dies ist aber nicht mehr nötig, wenn der
Eintrag denselben Namen wie das Feld trägt. Für das Feld content
sieht der Eintrag entsprechend so aus:
'label' => &$GLOBALS['TL_LANG'][$table]['content'],
Dies soll hier nur der Vollständigkeit halber erwähnt werden.
Sprachdatei für das Modul
Unsere Erweiterung stellt ein Backendmodul bereit. Dies ist der Menüpunkt im Backend, mit dem wir unsere Daten
verwalten. Damit auch die Menüpunkte eine sinnvolle Beschriftung erhalten, legen wir noch eine Sprachdatei mit dem
Namen /src/Ctocb/Example/Resources/contao/languages/de/modules.php
und dem folgenden Inhalt an.
<?php declare(strict_types=1);
// Kategorie
$GLOBALS['TL_LANG']['MOD']['module_test_group'] = ['Daten', 'Daten'];
// Eintrag
$GLOBALS['TL_LANG']['MOD']['module_test_table'] = ['Testtabelle', 'Testtabelle'];
Datenbank aktualisieren
Da wir eine Tabelle erstellt haben, müssen wir nun noch die Datenbank aktualisieren. Dies geschieht entweder über den Manager, oder über die Konsole mit folgendem Befehl:
vendor/bin/contao-console contao:migrate
Fertig
Nun haben wir unsere erste Tabelle, in der wir Daten speichern können. Wir können die Datensätze anlegen, bearbeiten, ansehen und löschen. In den folgenden Kapiteln werden wir diese Tabelle erweitern und verbessern.
Im Backend könnte die Liste dann in etwa so aussehen:
Die Eingabemaske sähe dann so aus: