Contao: Eigenen Backend-Menüpunkt

von Patrick Froch (Kommentare: 3)

Anspruch 3/5 Contao 3.x PHP 5.6

In diesem Beitrag geht es darum, wie man in Contao kundenspezifische Daten verwaltet. Wir werden eine neue Rubrik und einen neuen Menüpunkt im Backend anlegen. Des Weiteren werden wir eine neue Tabelle für unsere Daten erstellen. Als Beispiel soll eine einfache Produktdatenbank dienen.

Ordnerstruktur

Wie immer wird zuerst die obligatorische Ordnerstruktur der Erweiterung unter TL_ROOT/system/modules/ angelegt:

easy_extension/
├── assets
│   └── img
├── config
├── dca
└── languages
    └── de

Icon

Damit das Icon unserer Erweiterung später auch im Backend angezeigt werden kann, legen wir gleich noch eine .htaccess im Ordner assets/ an:

<IfModule !mod_authz_core.c>
  Order allow,deny
  Allow from all
</IfModule>
<IfModule mod_authz_core.c>
  Require all granted
</IfModule>

Wo wir gerade dabei sind, speichern wir auch gleich ein beliebiges Icon unter assets/img/products.png. Es sollte die Größe 16x16 Pixel haben.

Konfiguration

Nun legen wir die Konfiguration für unsere Erweiterung in der Datei config/config.php an:

<?php
$GLOBALS['BE_MOD']['myextension']['products'] = array(
    'tables'    => array('tl_products'),
    'icon'      => 'system/modules/easy_extension/assets/img/products.png'
);

In Zeile 3 legen wir den Namen unserer Tabelle fest. Für diese erstellen wir später ein entsprechendes DCA. In Zeile 4 sagen wir Contao, dass es unser Icon im Backen-Menü verwenden soll.

Update: 16.04.2016: In Zeile 3 war ein Tippfelher! Es muss "tables" heißen und nicht "tabels"! (Vielen Dank an Thomas und Nikolai für den Hinweis!)

Wenn wir uns das Ergebnis ansehen, sollte es ungefähr so aussehen:

Backend

Sprachdatei

Damit es etwas schöner aussieht, legen wir nun das Spracharray für den Menüpunkt an. Dies hat noch nichts mit dem Spracharray für das DCA zu tun. Darum kümmern wir uns später. Wir erstellen erst einmal die Datei languages/de/modules.php:

<?php
$GLOBALS['TL_LANG']['MOD']['myextension']   = array('Meine Erweiterung', 'Kategorie der Erweiterung');
$GLOBALS['TL_LANG']['MOD']['products']      = array('Produkte', 'Mein Produktkatalog');

In Zeile 1 wird der Name für die Kategorie vergeben und in Zeile 2 der Name des Menüpunkts. Das Ergebnis kann sich schon sehen lassen:

Backend

Leider passiert bei einem Klick auf den Menüpunkt noch nicht viel, da es noch kein DCA gibt. Dies werden wir aber jetzt ändern.

DCA

Wir erstellen die Datei dca/tl_products.php mit folgendem Inhalt:

<?php
/**
 * Set Tablename
 */
$strName = 'tl_products';


/**
 * Table tl_products
 */
$GLOBALS['TL_DCA'][$strName] = array
(

    // Config
    'config' => array
    (
        'dataContainer'               => 'Table',
        'enableVersioning'            => true,
        'sql' => array
        (
            'keys' => array
            (
                'id' => 'primary'
            )
        )
    ),

    // List
    'list' => array
    (
        'sorting' => array
        (
            'mode'                    => 1,
            'fields'                  => array('title'),
            'panelLayout'             => 'sort,filter;search,limit',
            'flag'                    => 1
        ),
        'label' => array
        (
            'fields'                  => array('title'),
            'format'                  => '%s'
        ),
        'global_operations' => array
        (
            'all' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['MSC']['all'],
                'href'                => 'act=select',
                'class'               => 'header_edit_all',
                'attributes'          => 'onclick="Backend.getScrollOffset();" accesskey="e"'
            )
        ),
        'operations' => array
        (
            'edit' => array
            (
                'label'               => &$GLOBALS['TL_LANG'][$strName]['edit'],
                'href'                => 'act=edit',
                'icon'                => 'edit.gif'
            ),
            'copy' => array
            (
                'label'               => &$GLOBALS['TL_LANG'][$strName]['copy'],
                'href'                => 'act=copy',
                'icon'                => 'copy.gif'
            ),
            'delete' => array
            (
                'label'               => &$GLOBALS['TL_LANG'][$strName]['delete'],
                'href'                => 'act=delete',
                'icon'                => 'delete.gif',
                'attributes'          => 'onclick="if(!confirm(\'' . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] . '\'))return false;Backend.getScrollOffset()"'
            ),
            'show' => array
            (
                'label'               => &$GLOBALS['TL_LANG'][$strName]['show'],
                'href'                => 'act=show',
                'icon'                => 'show.gif'
            )
        )
    ),

    // Select
    'select' => array
    (
        'buttons_callback' => array()
    ),

    // Edit
    'edit' => array
    (
        'buttons_callback' => array()
    ),

    // Palettes
    'palettes' => array
    (
        '__selector__'                => array(''),
        'default'                     => '{title_legend},title, price;{description_legend},description;'
    ),

    // Subpalettes
    'subpalettes' => array
    (
        ''                            => ''
    ),

    // Fields
    'fields' => array
    (
        'id' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL auto_increment"
        ),
        'tstamp' => array
        (
            'sql'                     => "int(10) unsigned NOT NULL default '0'"
        ),
        'title' => array
        (
            'label'                   => &$GLOBALS['TL_LANG'][$strName]['title'],
            'exclude'                 => true,
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>255),
            'sql'                     => "varchar(255) NOT NULL default ''"
        ),
        'price' => array
        (
            'label'                   => &$GLOBALS['TL_LANG'][$strName]['price'],
            'exclude'                 => true,
            'inputType'               => 'text',
            'eval'                    => array('mandatory'=>true, 'maxlength'=>255),
            'sql'                     => "varchar(255) NOT NULL default ''"
        ),
        'description' => array
        (
            'label'                   => &$GLOBALS['TL_LANG'][$strName]['description'],
            'exclude'                 => true,
            'search'                  => true,
            'inputType'               => 'textarea',
            'eval'                    => array('rte'=>'tinyMCE'),
            'sql'                     => "mediumtext NULL"
        )
    )
);

Das ist eine ganze Menge, aber das DCA bietet auch viel Flexibilität. Für uns sind die folgenden Zeilen am interessantesten:

  • In Zeile 34 legen wir fest nach welcher Spalte sortiert werden soll. Wir wählen dort einfach den Titel.
  • In Zeile 40 legen wir das Label für die Anzeige in der Liste im Backend fest. Auch hier reicht uns der Titel.
  • In Zeile 99 erstellen wir unsere Palette. Diese legt fest in welcher Reihenfolge die Felder in der Bearbeitungsmaske im Backend angezeigt werden.
  • In den Zeilen 119, 127, 135 definieren wir unsere Felder. Es soll im Augenblick nur die Felder title, price und description geben.
Da wir neue Felder angelegt haben, müssen wir nun die Datenbank aktualisieren!

Sprachdatei - Die Zweite

Jetzt ist es an der Zeit das Spracharray für unser DCA anzulegen. Wir erstellen die Datei languages/de/tl_products.php:

<?php
/**
 * Set Tablename
 */
$strName = 'tl_products';

/**
 * Fields
 */
$GLOBALS['TL_LANG'][$strName]['title']              = array('Titel', 'Bitte geben Sie den Titel ein.');
$GLOBALS['TL_LANG'][$strName]['price']              = array('Preis', 'Bitte geben Sie den Preis ein.');
$GLOBALS['TL_LANG'][$strName]['description']        = array('Beschreibung', 'Bitte geben Sie die Beschreibung ein.');


/**
 * Legends
 */
$GLOBALS['TL_LANG'][$strName]['title_legend']       = 'Titel';
$GLOBALS['TL_LANG'][$strName]['description_legend'] = 'Beschreibung';


/**
 * Buttons
 */
$GLOBALS['TL_LANG'][$strName]['new']        = array('Neues Produkt', 'Neues Produkt anlegen');
$GLOBALS['TL_LANG'][$strName]['edit']       = array('Produkt bearbeiten', 'Produkt mit der ID %s bearbeiten');
$GLOBALS['TL_LANG'][$strName]['copy']       = array('Produkt kopieren', 'Produkt mit der ID %s kopieren');
$GLOBALS['TL_LANG'][$strName]['delete']     = array('Produkt löschen', 'Produkt mit der ID %s löschen');
$GLOBALS['TL_LANG'][$strName]['show']       = array('Produkt anzeigen', 'Details des Produkt mit der ID %s anzeigen');

Ergebnis

Nun können wir Daten eingeben und speichern:

Backend

In der Liste könnte es dann z.B. so aussehen:

Backend

Erweiterung

Will man nun z.B. zusätzlich zum Titel noch den Preis anzeigen, ändert man einfach in der Datei dca/tl_products.php die Zeilen 40 und 41:

            // ...
            'fields'                  => array('title', 'price'),
            'format'                  => '%s [%s €]'
            // ...

Nun wird auch der Preis angezeigt:

Backend

Der Übersicht halber wurde eine heller Schriftfarbe verwendet.

Fazit

Selbstverständlich bietet das DCA noch erheblich mehr Möglichkeiten. Als kleine Einführung soll es aber erst einmal reichen.

Zurück

Einen Kommentar schreiben

Kommentar von Nikolai |

Hi,

danke erst einmal für die Arbeit dieses Wissen zu publizieren.
Gerade diese Infos zu Contao sind selten.

Ich habe die Beschreibung zu »Contao: Ein eigenes Inhaltselement« problemlos umgesetzt. Wenn ich diesen Artikel umsetze »Eigenen Backend-Menüpunkt«, erscheint bei mir zwar im Backend der neuen Menüpunkt, aber wenn man draufklickt, sieht man oben nur den Titel »Produkte«. Alles andere fehlt. Umgesetzt mit Contao 3.5.9

Antwort von Patrick Froch

Hallo Nikolai,

danke für den Hinweis. In der Datei "config/config.php" war ein Tippfehler. Ich habe den Artikel entsprechend bearbeitet. Nun sollte es funktionieren.

Viele Grüße,
Patrick

Kommentar von Nikolai |

Danke, jetzt funktioniert es.

Kommentar von Fredi Gut |

Ein grossartige und sehr hilfreiche Anleitung. Alles funktioniert beim ersten Mal!

Antwort von Patrick Froch

Vielen Dank für das Lob. Es freut mich, wenn ich helfen kann.