2.5 Eine Togglefunktion für eine eigene Liste
Im letzten Abschnitt haben wir einen Button für die Liste im Backend erstellt. Wenn man sich nun überlegt, was eine Toogle-Funktion ist, kommt man zu dem Schluss, dass es ein Button ist, der nicht einfach einen Wert einträgt, sondern abhängig vom Zustand hin und her schaltet. Der Status wird dann durch das Aussehen des Buttons angezeigt. Wir müssen also nur das Aussehen des Buttons anhand des Status anpassen.
(Bitte auch in diesem Artikel wieder den Vendor-Namespace (oder entsprechenden Ordner)
durch Euren eignen ersetzen und nicht Ctocb
verwenden! Danke!)
Mathis Völkert hat mich darauf hingewiesen, dass man diese Funktion auch einfacher umsetzen kann. Ich denke, es ist trotzdem ein gutes Beispiel und lasse es so stehen. Seine Lösung findet Ihr aber am Ende des Beitrags.
Erweiterung des DCA
Hier das entsprechende DCA (/src/Ctocb/Example/Resources/contao/dca/tl_testtable.php
):
<?php declare(strict_types=1);
$table = 'tl_testtable';
$GLOBALS['TL_DCA'][$table] = [
// Config
'config' => [
// ...
],
// List
'list' => [
'sorting' => [
// ...
],
'label' => [
// ...
],
'global_operations' => [
// ...
],
'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'
],
'toggle' => [
'label' => &$GLOBALS['TL_LANG'][$table]['toggle'],
'icon' => 'visible.svg',
'attributes' => 'onclick="Backend.getScrollOffset();return AjaxRequest.toggleVisibility(this,%s)"',
'button_callback' => [\Ctocb\Example\Classes\Contao\Operations\TlTesttable::class, 'toggleIcon'],
'showInHeader' => true
]
]
],
// Palettes
'palettes' => [
'default' => '{title_legend},title;{publish_legend},published;'
],
// Fields
'fields' => [
'id' => [
'sql' => 'int(10) unsigned NOT NULL auto_increment'
],
'tstamp' => [
'sql' => "int(10) unsigned NOT NULL default '0'"
],
'title' => [
'label' => &$GLOBALS['TL_LANG'][$table]['title'],
'exclude' => true,
'inputType' => 'text',
'eval' => ['mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50'],
'sql' => "varchar(255) NOT NULL default ''"
],
'content' => [
'label' => &$GLOBALS['TL_LANG'][$table]['content'],
'exclude' => true,
'inputType' => 'textarea',
'eval' => ['mandatory'=>true, 'tl_class' => 'clr long', 'rte'=>'tinyMCE'],
'sql' => "text NULL"
],
'published' => [
'label' => &$GLOBALS['TL_LANG'][$table]['published'],
'exclude' => true,
'inputType' => 'checkbox',
'eval' => ['doNotCopy'=>true],
'sql' => "char(1) NOT NULL default ''"
]
]
];
Neu ist hier das Feld published
, die Aktion wurde von resettime
in toggle
umbenannt und etwas angepasst. Sie
ruft nun ein Ajax-Request auf und hat kein key
-Attribut mehr. Es gibt jetzt aber einen button_callback
, der das
Aussehen des Buttons anpasst.
Sprachdatei
Wir ergänzen die Datei /src/Ctocb/Example/Resources/contao/languages/de/tl_testtable.php
, bis sie so aussieht:
<?php declare(strict_types=1);
$table = 'tl_testtable';
// Fields
$GLOBALS['TL_LANG'][$table]['title'] = ['Titel', 'Bitte geben Sie den Titel ein.'];
$GLOBALS['TL_LANG'][$table]['content'] = ['Inhalt', 'Bitte geben Sie den Inhalt ein.'];
$GLOBALS['TL_LANG'][$table]['published'] = ['Veröffentlichen', 'Daten veröffentlichen']; // neu
// Legends
$GLOBALS['TL_LANG'][$table]['title_legend'] = 'Titel';
$GLOBALS['TL_LANG'][$table]['publish_legend'] = 'Veröffentlichen'; // neu
// Operations
$GLOBALS['TL_LANG'][$table]['toggle'] = 'Toggle';
// Buttons
// ...
Operation anlegen
Die Klasse der Operation ruf hier nur eine Hilfsklasse (/src/Ctocb/Example/Classes/Contao/Operations/TlTesttable
) auf:
<?php declare(strict_types=1);
namespace Ctocb\Example\Classes\Contao\Operations;
use Ctocb\Example\Classes\Helper\ToggleHelper;
class TlTesttable
{
private $table = 'tl_testtable';
public function toggleIcon($row, $href, $label, $title, $icon, $attributes): string {
$helper = new ToggleHelper($this->table);
return $helper->toggleIcon($row, $href, $label, $title, $icon, $attributes);
}
}
Bei jedem Ajax-Request wird die Funktion toggleIcon
aufgerufen. Diese ruft ihrerseits in Zeile 14 eine Hilfklasse
auf. Ich habe die eigentliche Logik ausgelagert, da man sie häufig an mehreren Stellen in einem Projekt verwerden will.
Außerdem lässt sich die Hilfsklasse leichter adaptieren und für eigene Zwecke erweitern.
(Normalerweise würde ich die Hilfklasse nicht mit new
erstellen, sondern per Dependency Injection oder über eine
Factory beziehen. Auf diese Konzepte gehe ich später noch ein. Der Einfachheithalber soll es so aber erst einmal
genügen.)
Hilfsklasse
Wir erstellen unsere Hilfsklasse ToggleHelper
unter /src/Ctocb/Example/Classes/Helper/ToggleHelper.php
.
<?php declare(strict_types=1);
namespace Ctocb\Example\Classes\Helper;
use Contao\BackendUser;
use Contao\Controller;
use Contao\CoreBundle\Exception\AccessDeniedException;
use Contao\Database;
use Contao\DataContainer;
use Contao\Image;
use Contao\Input;
use Contao\StringUtil;
use Contao\Versions;
class ToggleHelper
{
private $table = '';
public function __construct(string $table)
{
$this->table = $table;
}
public function toggleIcon($row, $href, $label, $title, $icon, $attributes): string {
$tid = Input::get('tid');
$state = Input::get('state') === (string)1;
$dc = func_num_args() <= 12 ? null : func_get_arg(12);
$referer = Controller::getReferer();
$title = StringUtil::specialchars($title);
$href .= '&tid=' . $row['id'] . '&state=' . ($row['published'] ? '' : 1);
$url = Controller::addToUrl($href);
if (null !== $tid) {
$this->toggleVisibility((int)$tid, $state, $dc); // Ändern der Sichtbarkeit
Controller::redirect($referer);
}
if (!$row['published']) {
$icon = 'invisible.svg';
}
$img = Image::getHtml($icon, $label, 'data-state="' . ($row['published'] ? 1 : 0) . '"');
return '<a href="' . $url . '" title="' . $title . '"' . $attributes . '>' . $img . '</a> ';
}
private function toggleVisibility(int $intId, bool $blnVisible, DataContainer $dc=null): void
{
Input::setGet('id', $intId);
Input::setGet('act', 'toggle');
if (null !== $dc) {
$dc->id = $intId;
}
// Check the field access
$user = BackendUser::getInstance();
if (false === $user->hasAccess($this->table . '::published', 'alexf')) {
throw new AccessDeniedException('Not enough permissions to publish/unpublish data ID "' . $intId . '".');
}
$db = Database::getInstance();
$objRow = $db->prepare('SELECT * FROM ' . $this->table . ' WHERE id=?')->limit(1)->execute($intId);
if ($objRow->numRows < 1) {
throw new AccessDeniedException('Invalid Id "' . $intId . '".');
}
if (null !== $dc) {
$dc->activeRecord = $objRow;
}
$objVersions = new Versions($this->table, $intId);
$objVersions->initialize();
$time = time();
$published = ($blnVisible ? '1' : '');
$qury = "UPDATE " . $this->table . " SET tstamp=?, published='" . $published . "' WHERE id=?";
$db->prepare($qury)->execute($time, $intId);
if ($dc) {
$dc->activeRecord->tstamp = $time;
$dc->activeRecord->published = $published;
}
$objVersions->create();
if (null !== $dc) {
$dc->invalidateCacheTags();
}
}
}
Bei jedem Ajax-Request wird von der Klasse mit unserer Operation die Funktion toggleIcon
aufgerufen. Diese ruft
ihrerseits in Zeile 37 die Funktion toggleVisibility
auf. toggleIcon
ist für die Anzeige des Icons da und
entscheidet, welches Icon angezeigt wird. toggleVisibility
kümmert sich um das Ändern des Werts in der Datenbank.
Es werden hier noch einige Abläufe von Contao erledigt, wie das Erstellen einer neuen Version. Diese Dinge werde ich hier erst einmal vernachlässigen. Ich wollte sie aber wenigstens zeigen und eine gute Klasse für die Verwendung in eingenen Projekten zur Verfügung stellen.
Wichtig ist die Erstellung des Icons in den Zeilen 41 - 47 und das Ändern des Werts in der Datenbank in den Zeilen 81 - 84. Die Hilfsklasse kann so in eingenen Projekten mit beliebigen Tabellen verwendet werden.
Fertig
Zum Abschluss müssen wir noch den Cache leeren und die Datenbank aktualisieren.
Unser Toogle-Icon sieht dann so aus:
Ergänzung
Mittlerweile ist es möglich, die Toogle-Funktion einfach im DCA zu konfigurieren. Dies funktioniert, wenn man die Standard-Icons verwendet und keine zusätzliche Funktionalität benötigt.
Man kann einfach den oben gezeigten Eintrag unter operations
durch folgende Zeilen ersetzen. Der Rest wird dann nicht
benötigt, da Contao alles übernimmt.
<?php declare(strict_types=1);
$table = 'tl_testtable';
$GLOBALS['TL_DCA'][$table] = [
// Config
'config' => [
// ...
],
// List
'list' => [
'sorting' => [
// ...
],
'label' => [
// ...
],
'global_operations' => [
// ...
],
'operations' => [
// ...
'toggle' => [
'href' => 'act=toggle&field=published',
'icon' => 'published.svg',
],
]
],
// ...
Danke an Mathis Völkert für den Hinweis.