[4869 Aufrufe]

2.7 Ein eigenes Widget für das Backend erstellen

In diesem Beitrag erstellen wir ein eigenes Formularfeld für das Backend von Contao. Auch wenn Contao im Backend viele Formularfelder bietet, ist es machmal nötig ein eigenes Feld zu erstellen. Wir werden deshalb eine etwas vereinfachte Form des eS-IT/selectwizard erstellen. Es wird eine Liste von Auswahlfeldern, ähnlich dem listwizard, nur mit Auswahlfeldern, statt den Textfeldern.

(Bitte auch in diesem Artikel wieder den Vendor-Namespace (oder entsprechenden Ordner) durch Euren eignen ersetzen und nicht Ctocb verwenden! Danke!)

Helper

Zunächste erstellen wir zwei Helper. Es ist nicht zwingend erforderlich diese Funktionalität auszulagern, macht die Sache aber übersichtlicher.

Mit dem ersten Helper fügen wir Assets (wie CSS- und JS-Dateien) zu Contao hinzu.

/src/Ctocb/Example/Classes/Helper/AssetHandler.php:

<?php declare(strict_types=1);

namespace Ctocb\Example\Classes\Helper;

class AssetHandler
{

    public function insertAsset(string $path, string $type): void
    {
        if (!empty($path) && !empty($type) &&
            (
                !\array_key_exists($type, $GLOBALS) ||
                !\is_array($GLOBALS[$type]) ||
                !\in_array($path, $GLOBALS[$type], true)
            )
        ) {
            $GLOBALS[$type][] = $path;
        }
    }
}

Der zweite Helper erstellt uns ein SelectMenu, also ein Auswahlfeld. Da wir eine Liste von Auswahlfeldern haben, benotigen wir dies mehrfach und lagern es deshalb aus.

/src/Ctocb/Example/Classes/Helper/SelectHandler.php:

<?php declare(strict_types=1);

namespace Ctocb\Example\Classes\Helper;

use Contao\SelectMenu;

class SelectHandler
{

    public function createSelect(string $cssId, array $config, $value = null): SelectMenu
    {
        $select         = new SelectMenu();
        $select->addAttributes($config);
        $select->name   = $cssId . '[]';
        $select->id     = $cssId;
        $select->class  = 'selectmenuwizard';

        if (!empty($value)) {
            $select->value = $value;
        }

        return $select;
    }
}

Widget

Nun folgt das eigentliche Widget, also die Klassen, die für die Erzeugung und Verwaltung der Eingabefelder zuständig ist. Da diese Klasse etwas länger ist, habe ich die Kommentare diesmal nicht entfernt.

/src/Ctocb/Example/Classes/Contao/Backend/Widgets/SelectMenuWizard.php:

<?php declare(strict_types=1);

namespace Ctocb\Example\Classes\Contao\Backend\Widgets;

use Contao\BackendTemplate;
use Contao\Widget;
use Ctocb\Example\Classes\Helper\AssetHandler;
use Ctocb\Example\Classes\Helper\SelectHandler;

class SelectMenuWizard extends Widget
{
    /**
     * Submit user input
     * @var bool
     */
    protected $blnSubmitInput = true;

    /**
     * Template
     * @var string
     */
    protected $strTemplate = 'be_widget';

    /**
     * Name des Ausgabetemplates
     * @var string
     */
    protected string $widgetTemplate = 'be_select_menu_wizard';

    private AssetHandler $assetHandler;

    public function __construct($arrAttributes = null) {
        parent::__construct($arrAttributes);
        $this->assetHandler = new AssetHandler();
    }

    /**
     * Erzeugt die Ausgabe.
     * @return string
     */
    public function generate(): string
    {
        $this->insertCss();
        $this->insertJs();
        $selected = $this->generateWidgets();

        return $this->createTemplate($selected)->parse();
    }

    /**
     * Fügt das CSS ein.
     */
    private function insertCss(): void
    {
        $this->assetHandler->insertAsset('bundles/Ctocbexample/css/selectlistfix.css', 'TL_CSS');
    }

    /**
     * Fügt das JavaScript ein.
     */
    private function insertJs(): void
    {
        $this->assetHandler->insertAsset('bundles/Ctocbexample/js/ListInitializer.js', 'TL_JAVASCRIPT');
    }

    /**
     * Erstellt die Widgets.
     * @return array
     */
    private function generateWidgets(): array
    {
        $selects        = [];
        $config         = $this->arrConfiguration;
        $id             = (string)$this->strId;
        $values         = (array)$this->varValue;
        $selectHandler  = new SelectHandler();

        if (!empty($values)) {
            foreach ($values as $value) {
                $selects[] = $selectHandler->createSelect($id, $config, $value);
            }
        } else {
            $selects[] = $selectHandler->createSelect($id, $config);
        }

        return $selects;
    }

    /**
     * Erstellt eine Instanz des Ausgabetemplates.
     * @param array $selects
     * @return BackendTemplate
     */
    private function createTemplate(array $selects): BackendTemplate
    {
        $template = new BackendTemplate($this->widgetTemplate);
        $template->setData($this->arrConfiguration);
        $template->strId       = \str_replace('ctrl_', '', (string)$this->strId) . '_list';
        $template->label       = $this->strLabel;
        $template->lang        = $GLOBALS['TL_LANG']['MSC'];
        $template->selectBoxes = $selects;

        return $template;
    }
}

Die generate-Methode ruft die privaten Methoden auf, die sich um die eigentliche Logik kümmern.

Konfiguration für das Widget erstellen

Wir müssen Contao noch mitteilen, dass wir ein Widget erstellt haben, da es sonst nicht geladen wird.

/src/Ctocb/Example/Resources/contao/config/config.php:

<?php
$GLOBALS['BE_FFL']['selectmenuwizard'] = \Ctocb\Example\Classes\Contao\Backend\Widgets\SelectMenuWizard::class;

Assets

Damit alles funktioniert und schön aussieht, benötigen wir noch etwas CSS und JS. Das CSS passt die Länge der Auswahlfelder an, damit die Icons dahinder passen.

/src/Ctocb/Example/Resources/public/css/selectlistfix.css:

.w50 .tl_selectmenuwizard .selectmenuwizard{
    width: 85%;
}

.long .tl_selectmenuwizard .selectmenuwizard {
    width: 95%;
}

Das JavaScript initialisiert die Liste, damit die Operationen verarbeitet werden.

/src/Ctocb/Example/Resources/public/js/ListInitializer.js:

'use strict';

window.addEventListener("load", function(event) {
    let elements = document.getElementsByClassName('tl_selectmenuwizard');

    for (let elem of elements) {
        Backend.listWizard(elem.id);
    }
});

Wenn die Assets nicht gefunden werden, hilfen u.U. folgende Kommandos:

console contao:install-web-dir
console contao:symlinks

Template

Für die Ausgabe benötigen wir noch ein Template, dass die einzelenen Ausgabefelder zu einer Liste zusammenfasst.

/src/Ctocb/Example/Resources/contao/templates/be_select_menu_wizard.html5:

<ul id="ctrl_<?= $this->strId; ?>" class="tl_listwizard tl_selectmenuwizard">
    <?php if (\is_array($this->selectBoxes)): ?>
        <?php foreach($this->selectBoxes as $select): ?>
            <li>
                <?= $select->generateWithError(); ?>

                <button type="button" data-command="copy" title="<?= \Contao\StringUtil::specialchars($this->lang['lw_copy']); ?>">
                    <?= \Contao\Image::getHtml('copy.svg'); ?>
                </button>

                <button type="button" data-command="delete" title="<?= \Contao\StringUtil::specialchars($this->lang['lw_delete']); ?>">
                    <?= \Contao\Image::getHtml('delete.svg'); ?>
                </button>

                <button type="button" class="drag-handle" title="<?= \Contao\StringUtil::specialchars($this->lang['move']); ?>" aria-hidden="true">
                    <?= \Contao\Image::getHtml('drag.svg'); ?>
                </button>
            </li>
        <?php endforeach; ?>
    <?php endif; ?>
</ul>

Test

Um das Widget zu testen, können wir einfach ein beliebieges DCA (z. B. aus den vorherigen Texten) mit der folgenden Felddefinition ergänzen:

<?php

$table = 'tl_testtable';

// Sonstige Einstellungen des DCAs ...

$GLOBALS['TL_DCA'][$table]['palettes']['default'].= '{testfield_legend},testfield;';

$GLOBALS['TL_DCA'][$table]['fields']['testfield'] = [
    'inputType' => 'selectmenuwizard',
    'options'   => [1 => 'Test 001', 2 => 'Test 002'],
    'eval'      => ['tl_class'=>'w50', 'includeBlankOption'=>true],
    'sql'       => 'text NOT NULL'
];

Fazit

Dies ist eine etwas vereinfachte Darstellung der Erweiterung eS-IT/selectwizard. Wer wissen möchte, wie ich eine solche Erweiterung normalerweise aufbauen würde, kann sich gerne das Repository auf Github ansehen: eS-IT/selectwizard

Globale_Aktion