TEE-12 Backend Callback-Magie

Aus Contao Community Documentation

MsgError.png Unvollständiger Artikel: dieser Artikel ist noch nicht sauber bearbeitet.

Bitte erweitere ihn und entferne erst anschliessend diesen Hinweis.

Tagebuch einer Extension-Entwicklung

betrifft
TYPOlight Version ab TL 2.8
Extensions Extension Creator


Callback-Magie

Ja, nach langer Pause geht es wirklich weiter. Diesmal wird die Backendpflege der "Meldungen"-Tabelle komplettiert. Gegenüber dem letzten Schritt haben sich nach fruchtbarer Diskussion einige Änderungen ergeben.

Zunächst die Datenbankdefinition der Tabelle tl_gw_meldungen in system/modules/tl_gw_turnierpaare/config/database.sql:

-- 
-- Table `tl_gw_meldungen`
-- 
CREATE TABLE `tl_gw_meldungen` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `pid` int(10) unsigned NOT NULL default '0',
  `sorting` int(10) unsigned NOT NULL default '0',
  `tstamp` int(10) unsigned NOT NULL default '0',
  `datum` varchar(10) NOT NULL default '',
  `startgruppe` varchar(32) NOT NULL default '',
  `startklasse` varchar(12) NOT NULL default '',
  `lat_std` char(32) NOT NULL default '',
  `turnierort` varchar(128) NOT NULL default '',
  `turnierart` varchar(64) NULL default NULL,
  `anzahlpaare` int(4) NULL default NULL,
  `platz_von` int(4) NULL default NULL,
  `platz_bis` int(4) NULL default NULL,
  `bemerkung` text NULL,
  PRIMARY KEY  (`id`),
  KEY `pid` (`pid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Das Feld datum war vorher vom Typ date, aber das funktioniert so nicht, wenn man es als Datumsfeld im Backend verwenden will. Darum jetzt ein varchar(10). Der Rest blieb unverändert.

Auch das Einfügen der Backendmodule in die Auswahlleiste auf der linken Seite des Backends ändere ich in /system/modules/tl_gw_turnierpaare/config/config.php ab:

// Back end module 
array_insert($GLOBALS['BE_MOD'], 0, array( 
    'gw_paarverwaltung' => array( 
      'gw_turnierpaare' => array 
      ( 
        'tables' => array('tl_gw_turnierpaare'), 
        'icon'   => 'system/modules/gw_turnierpaare/icons/turnierpaare.png' 
      ), 
      'gw_meldungen' => array 
      ( 
        'tables' => array('tl_gw_meldungen'), 
        'icon'   => 'system/modules/gw_turnierpaare/icons/meldeliste.png' 
      ) 
    ) 
  ) 
);

Durch das array_insert kann ich mit zweiten Parameter steuern, an welcher Stelle das als letzter Parameter angegebene Array eingefügt werden soll. Bei einer "0" wie hier wandern meine Arrayeinträge ganz nach oben,bei einer "1" an zweite Stelle, usw. Da in der Praxis auf der Webseite häufiger neue Turnierpaarmeldungen eingegeben werden als neue Artikel, möchte ich die Turnierpaarverwaltung gerne ganz oben haben.

Und dadurch, dass ich in dem Array was ich einfüge eine zusätzliche Ebene gw_paarverwaltung habe, gruppiere ich die beiden Einträge gw_turnierpaare und gw_meldungen in eine Untergruppe innerhalb des Menüs. Das schafft mehr Übersicht.

Um die Language-Datei für diese Einträge muss ich mich jetzt auch einmal kümmern (/system/modules/tl_gw_turnierpaare/languages/modules.php):

/** 
 * Back end modules 
 */ 
$GLOBALS['TL_LANG']['MOD']['gw_turnierpaare'] = array('Turnierpaare', 'Verwaltung der Turnierpaare.'); 
$GLOBALS['TL_LANG']['MOD']['gw_meldungen'] = array('Meldungen', 'Verwaltung der Turniermeldungen (Meldeliste).'); 
$GLOBALS['TL_LANG']['MOD']['gw_paarverwaltung'] = 'Paarverwaltung';

ACHTUNG: Während die Language-Einträge für die Module selbst ein Array bestehend aus dem Label des Menüeintrags und dem Beschreibungstext des Moduls sein müssen, ist der Label für die Überschrift der Gruppe im Menü (gw_paarverwaltung) nur ein einfacher String!

Wie sieht das nun aus?

Backend Tunierpaar Verwaltung

Wo wir schon mal bei den Language-Dateien sind, machen wir auch noch die Label für die Felder des Backendmoduls (/system/modules/tl_gw_turnierpaare/languages/de/tl_gw_meldungen.php):

<?php if (!defined('TL_ROOT')) die('You can not access this file directly!'); 
/** 
 * Fields 
 */ 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['pid'] = array('Paar', 'Turnierpaar auswählen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['datum'] = array('Turnierdatum', 'Turnierdatum eingeben'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['turnierort'] = array('Turnierort', 'Turnierort eingeben'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['turnierart'] = array('Turnierform', 'Turnierform auswählen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['startgruppe'] = array('Startgruppe', 'Startgruppe auswählen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['startklasse'] = array('Startklasse', 'Startklasse auswählen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['lat_std'] = array('Latein/Standard', 'Tanzart auswählen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['anzahlpaare'] = array('Anzahl gestarteter Paare', 'Die Paaranzahl des Turniers eingeben'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['platz_von'] = array('Platz', 'Erzielter Platz'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['platz_bis'] = array('Platz bis', 'Geteilter Platz: Platz bis... - sonst leer lassen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['bemerkung'] = array('Bemerkung', 'Bemerkung eingeben'); 
/** 
 * Reference 
 */ 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['couple_legend'] = 'Paar'; 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['tournament_legend'] = 'Turnier'; 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['result_legend'] = 'Ergebnis'; 
/** 
 * Buttons 
 */ 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['new']    = array('Neue Meldung', 'Eine neue Turniermeldung anlegen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['edit']   = array('Editieren', 'Die Turniermeldung editieren'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['copy']   = array('Meldung kopieren', 'Die Turniermeldung in die Zwischenablage kopieren'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['delete'] = array('Meldung löschen', 'Die Turniermeldung aus der Liste entfernen'); 
$GLOBALS['TL_LANG']['tl_gw_meldungen']['show']   = array('Details', 'Die Detailansicht der Turniermeldung anzeigen'); 
?>

Da muss ich wohl nicht mehr viel zu schreiben: Der erste Block sind Label und Beschreibungen für die Eingabefelder, der zweite Block sind die Label für die Palettenüberschriften, und die dritte Gruppe schließlich die Label und Beschreibungen für die "Aktionsbuttons".

Kommen wir zum spannenden Teil, mit dem ich im Schritt 11 ja noch einen Kampf zu fechten hatte: der DCA-Record (/system/modules/tl_gw_turnierpaare/dca/tl_gw_meldungen.php):

<?php if (!defined('TL_ROOT')) die('You can not access this file directly!'); 
/** 
 * Table tl_gw_meldungen  
 */ 
$GLOBALS['TL_DCA']['tl_gw_meldungen'] = array 
( 
    // Config 
    'config' => array 
    ( 
        'dataContainer'               => 'Table', 
        'enableVersioning'            => true, 
    ),

Hier so weit nichts Spannendes: Weiterhin wird eine Tabelle bearbeitet, und ich aktiviere Versionierung.

    // List 
    'list' => array 
    ( 
        'sorting' => array 
        ( 
            'mode'                    => 2, 
            'fields'                  => array('datum DESC', 'turnierort', 'pid'), 
            'panelLayout'             => 'filter;sort,search,limit' 
        ),

mode = 2 bedeutet, dass das Sortierfeld im Header der Tabelle wählbar ist, defaultmäßig sortiere ich nach Turnierdatum (Jüngste ganz oben), dann dem Ort, und schließlich der Paar-ID.

Mit panelLayout aktiviere ich die Paletten oben am Kopf der Tabelle, um das Sortierfeld wählbar zu machen, das Suchfeld zu aktivieren und die Anzahl der Treffer limitieren zu können.

        'label' => array 
        ( 
            'fields'                  => array('datum','turnierort', 'turnierart', 'startgruppe','startklasse','lat_std'), 
            'format'                  => '%s - #name# - <span style="font-weight: bold;">%s</span> - <span style="color: #section_colour#;">%s %s %s %s</span>', 
            'label_callback'          => array('tl_gw_meldungen', 'lookup_pid') 
        ),

So, hier beginnt etwas die Magie...ich bastele mir das Format der Ausgabezeilen für die Backendansicht.

Leider habe ich hier nur die Felder der aktuellen Tabelle (tl_gw_meldungen) zur Verfügung, ich möchte aber gerne den Namen des Paares anzeigen, was zur pid gehört, die im Datensatz steht (pid ist der Foreign Key in die tl_gw_turnierpaare-Tabelle. Im klassischen SQL würde ich hier also einen JOIN machen).

So direkt geht das leider nicht, darum definiere ich einen label_callback, der weiter unten in dieser Datei als Funktion lookup_pid() in der Klasse tl_gw_meldungen definiert wird. Die Felder, die ich jetzt schon habe, definiere ich auch direkt im String. Für den Namen des Paares setze ich zunächst einen Platzhalter "#name#" in den String.

Außerdem gibt es noch einen Platzhalter "#section_colour#". Ich möchte Turnierstarts in der Tanzform Standardtänze in orange darstellen, und solche in der Tanzform Lateinamerikanische Tänze in rot. Das kann ich an dieser Stelle im DCA-Record nicht entscheiden, weil es vom Inhalt des Felds lat_std abhängt. Auch das wird im label_callback geregelt.

Die Platzhalter werden im label_callback durch ein str_replace mit den gewünschten Werten ersetzt. Schön ist das nicht, aber funktioniert.

        'global_operations' => array 
        ( 
            'all' => array 
            ( 
                'label'               => &$GLOBALS['TL_LANG']['MSC']['all'], 
                'href'                => 'act=select', 
                'class'               => 'header_edit_all', 
                'attributes'          => 'onclick="Backend.getScrollOffset();"' 
            ) 
        ), 
        'operations' => array 
        ( 
            'edit' => array 
            ( 
                'label'               => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['edit'], 
                'href'                => 'act=edit', 
                'icon'                => 'edit.gif' 
            ), 
            'copy' => array 
            ( 
                'label'               => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['copy'], 
                'href'                => 'act=copy', 
                'icon'                => 'copy.gif' 
            ), 
            'delete' => array 
            ( 
                'label'               => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['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']['tl_gw_meldungen']['show'], 
                'href'                => 'act=show', 
                'icon'                => 'show.gif' 
            ) 
        ) 
    ),

Zu den globalen Operationen gibt es eigentlich nichts zu sagen. Die bleiben so, wie der Extension-Creator sie angelegt hat...

    // Palettes 
    'palettes' => array 
    ( 
        '__selector__'                => array(''), 
        'default'                     => '{couple_legend},pid;{tournament_legend},datum,turnierort,turnierart,startgruppe,startklasse
                                    .'{result_legend},anzahlpaare,plabis,bemerkung;' 
    ), 
    // Subpalettes 
    'subpalettes' => array 
    ( 
        ''                            => '' 
    ),

Die Palettendefinition hat sich nicht geändert, und Subpaletten gibt es weiterhin nicht.

    // Fields 
    'fields' => array 
    ( 
        'pid' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['pid'], 
            'inputType'               => 'select', 
            'options_callback'        => array('tl_gw_meldungen', 'getActiveCouples'), 
            'search'                  => true, 
            'sorting'                 => true, 
            'eval'                    => array('mandatory'=>true) 
        ),

Das Feld pid war bisher ein foreignKey-Feld, aber damit konnte ich z.B. nur mit dem Partner-Nachnamen verknüpfen, nicht mit dem Paarnamen. Außerdem wurden hier immer ALLE Paare angezeigt, hier sollen aber nur AKTIVE Paare auswählbar sein. Um das besser machen zu können, definiere ich die Möglichkeiten in der Dropdown-Box selbst durch den options_callback getActiveCouples() in der Klasse tl_gw_meldungen, die gleich noch folgt.

        'datum' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['datum'], 
            'inputType'               => 'text', 
            'search'                  => true, 
            'sorting'                 => true, 
            'flag'                    => 6,
            'eval'                    => array('mandatory'=>true, 'datepicker'=>$this->getDatePickerString(), 'tl_class'=>'w50 wizard', 'minlength' => 1, 'maxlength'=>10, 'rgxp' => 'date') 
        ),

flag => 6 ist hier der große Trick, damit das Timestamp-Format von "datum" richtig als Datum im Format TT.MM.JJJJ angezeigt wird. Abgeguckt habe ich das übrigens im Backend in /system/modules/backend/dca/tl_log.php.

        'turnierort' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['turnierort'], 
            'inputType'               => 'text', 
            'search'                  => true, 
            'sorting'                 => true, 
            'flag'                    => 11, 
            'eval'                    => array('mandatory'=>true, 'minlength' => 1, 'maxlength'=>128, 'tl_class' => 'w50') 
        ), 
        'turnierart' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['turnierart'], 
            'inputType'               => 'select', 
            'search'                  => true, 
            'sorting'                 => true, 
            'flag'                    => 11, 
            'options'                 => gwTurnierpaarliste::$TurnierArten, 
            'eval'                    => array('mandatory'=>false, 'tl_class' => 'w50') 
        ), 
        'startgruppe' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['startgruppe'], 
            'inputType'               => 'select', 
            'search'                  => true, 
            'sorting'                 => true, 
            'flag'                    => 11, 
            'options'                 => gwTurnierpaarliste::$StartGruppen, 
            'eval'                    => array('mandatory'=>false, 'includeBlankOption' => true, 'tl_class' => 'w50') 
        ), 
        'startklasse' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['startklasse'], 
            'inputType'               => 'select', 
            'search'                  => true, 
            'sorting'                 => true, 
            'flag'                    => 11, 
            'options'                 => gwTurnierpaarliste::$StartKlassen, 
            'eval'                    => array('mandatory'=>true, 'tl_class' => 'w50') 
        ), 
        'lat_std' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['lat_std'], 
            'inputType'               => 'select', 
            'sorting'                 => false, 
            'options'                 => gwTurnierpaarliste::$TanzArten, 
            'eval'                    => array('mandatory'=>true, 'tl_class' => 'w50') 
        ), 
        'anzahlpaare' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['anzahlpaare'], 
            'inputType'               => 'text', 
            'eval'                    => array('mandatory'=>false, 'minlength' => 1, 'maxlength'=>4, 'rgxp' => 'digit') 
        ), 
        'platz_von' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['platz_von'], 
            'inputType'               => 'text', 
            'eval'                    => array('mandatory'=>false, 'minlength' => 1, 'maxlength'=>4, 'rgxp' => 'digit', 'tl_class' => 'w50') 
        ), 
        'platz_bis' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['platz_bis'], 
            'inputType'               => 'text', 
            'eval'                    => array('mandatory'=>false, 'minlength' => 1, 'maxlength'=>4, 'rgxp' => 'digit', 'tl_class' => 'w50') 
        ), 
        'bemerkung' => array 
        ( 
            'label'                   => &$GLOBALS['TL_LANG']['tl_gw_meldungen']['bemerkung'], 
            'inputType'               => 'textarea', 
            'eval'                    => array('mandatory'=>false, 'cols' => 80, 'rows' => 20, 'allowHtml' => false) 
        ), 
    ) 
);

Hier gibt es keine großen Besonderheiten mehr. Gegenüber Schritt 11 habe ich die durchsuchbaren und sortierbaren Felder etwas erweitert bzw. geändert.

Nun kommt die Backendklasse, in der ich die Callbacks unterbringe:

class tl_gw_meldungen extends Backend 
{ 
    /** 
     * Import the back end user object 
     */ 
    public function __construct() 
    { 
        parent::__construct(); 
        $this->import('BackendUser', 'User'); 
    }

Auftaktgeplänkel...

Zunächst der options_callback, der mir die Liste der aktiven Turnierpaare für die Auswahl in der Dropdown-Box liefert:

    public function getActiveCouples() 
    { 
        $couples = array(); 
        // Get all the active couples 		  
        $objCouples = $this->Database->prepare("SELECT id,partnernachname,partnervorname,partnerinnachname,partnerinvorname 
                                                FROM tl_gw_turnierpaare 
                                                WHERE aktiv='1' 
                                                ORDER by partnernachname, partnervorname, partnerinnachname, partnerinvorname") 
                                      ->execute(); 
        while ($objCouples->next()) 
        { 
            $k = $objCouples->id; 
            $v = $objCouples->partnernachname; 
 
            if($objCouples->partnervorname) 
            { 
                $v .= ', '.$objCouples->partnervorname; 
            }
 
            if($objCouples->partnerinnachname) 
            { 
                $v .= ' und '.$objCouples->partnerinnachname; 
 
                if($objCouples->partnerinvorname) 
                { 
                    $v .= ', '.$objCouples->partnerinvorname; 
                } 
            } 
 
            $couples[$k] =$v; 
        } 
 
        return $couples; 
    }

Der größte Teil der Funktion ist eigentlich das "Zusammenbasteln" des Paarnamens aus den Einzelbestandteilen der Namen von Partner und Partnerin. Zunächst werden aus der tl_gw_turnierpaare-Tabelle die Namen der aktiven Paare selektiert und der Ergebnisstring daraus zusammengesetzt. Das Ergebnis wird dann einem Array zugewiesen, wobei die ID des Paares der Array-Key ist, und der Paarname der Value - also z.B. $couples[47] = "Wupp, Willi und Wupp, Sieglinde".

In der Dropdown-Box steht der String, und wenn ich den dort auswähle, landet die ID 47 als pid in der Datenbanktabelle tl_gw_meldungen. Und, was noch vieeeeel cooler ist: Wenn ich nach dem pid-Feld sortiere, dann werden auch die Sortierheader durch die entsprechenden Strings ersetzt, statt dass dort der numerische pid-Wert steht. Leider erfolgt die Sortierung natürlich nach dem Key, nicht nach dem Value...aber das wäre ja fast zuviel verlangt :-).

Weiter geht es mit label_callback. Als erstes Argument bekommt der die aktuelle Zeile aus der Datenbank, als zweites Argument den Label, wie er weiter oben im DCA-Record definiert wurde, hier also mit dem Platzhaltern #name# und #section_colour#.

    public function lookup_pid($row, $label) 
    { 
        $pid = $row['pid']; 
 
        // Datensatz mit ID pid aus Tabelle tl_gw_turnierpaare holen 
        $prow = $this->Database->prepare("SELECT * FROM tl_gw_turnierpaare WHERE id=?") 
                               ->execute($pid);

Mit der pid aus dem aktuellen Datensatz holen wir den Datensatz des Turnierpaares...

    $name = '<span style="font-weight: bold;">'.$prow->partnernachname.'</span>'; 
    if($prow->partnervorname) 
    { 
      $name .= ', '.$prow->partnervorname; 
    }; 
    if($prow->partnerinnachname) 
    { 
      $name .= ' und <span style="font-weight: bold;">'.$prow->partnerinnachname.'</span>'; 
      if($prow->partnerinvorname) 
      { 
        $name .= ', '.$prow->partnerinvorname; 
      } 
    };

...und bauen aus den Namensbestandteilen den Paarnamen zusammen, wobei die Nachnamen fett erscheinen sollen.

    $colour = 'black'; 
    switch($row['lat_std']) 
    { 
      case 'Std': 
        $colour = 'orange'; 
        break; 
      case 'Lat': 
        $colour = 'red'; 
        break; 
    }

In Abhängigkeit vom Inhalt des lat_std-Feldes wird eine Farbe zugewiesen. Falls der Inhalt unbekannt ist, bleibt die Schrift schwarz.

    $label = str_replace('#section_colour#', $colour, $label); 
    $label = str_replace('#name#', $name, $label); 
    return $label; 
  } 
};   
?>

Wir ersetzen die Platzhalter #name# und #section_colour# im Label-String durch die neu berechneten Werte, und geben den Label zurück.

Was haben wir damit nun erreicht:

Backend Meldungen

Unsere Startmeldungen sind nach dem Datum sortiert, die Nachnamen im Paarnamen sind fett, die Stadt auch, und die "Kategorisierung" des Turniers ist orange oder rot, je nachdem ob es um Standardtänze oder lateinamerikanische Tänze geht.

Backend Meldungen

Sortieren wir nach den Paaren, dann wird die numerische pid in den Sortierheader durch unseren options_callback in die entsprechenden Strings umgesetzt - sehr nett. Leider erfolgt die Sortierung weiterhin numerisch nach der pid, und nicht alphabetisch nach den Strings. Vielleicht hat jemand noch eine Idee, wie man das lösen könnte?

Backend Paarauswahl

Und bei Neuanlage einer Turniermeldung werden in der Dropdown-Liste nur noch die aktiven Paare aufgeführt, nicht mehr ALLE.

Im nächsten Schritt geht es ans Frontendmodul für die Meldeliste...

Ich bin gerade in der DCA-Referenz über das Feld findInSet unter eval gestolpert: "Sort by the actual option values instead of their labels (available from version 2.7.RC1)."

Das klingt wie das, was ich gerne für mein pid-Feld hätte, d.h. Sortierung nach den Paarnamen, die sich aus der pid ergeben anstatt numerisch nach dem pid-Wert. Leider wirft das einen Fehler in DC_Table.php (Zeile 3468 bei TL 2.8.3), wo $keys nicht definiert ist. Wenn ich das richtig sehe, funktioniert das nämlich nur mit hardcodierten "options" im DCA-Record (Zeile 3461), ein options_callback wird dort nicht ausgewertet.

Kann das einer der "Profis" bestätigen? Wäre das dann nicht evtl. ein kleiner lohnenswerter Punkt für einen Feature-Request? Sieht für mich nach einem Zweizeiler der Marke "Falls options nicht gesetzt ist options_callback ausführen, dann weitermachen" aus.

EDIT: Ich habe mal ein Ticket dafür gemacht: http://dev.typolight.org/issues/1914 Habe meine DC_Table.php entsprechend angepasst, und damit funktioniert es bei mir wie gewünscht.

Diskussion aus dem Forum

Hinweis.png Hinweis: Die Wiedergabe folgt dem Forums-Thread, der verschiedene Thematiken aufwirft. Sie muss daher noch umgearbeitet werden.


user: Setsunaa

Ich Entwickel seit zwei Monaten an einem Webseiten Katalog und wusste echt nicht was da auf mich zu kommt. Leider sind solche Art von "Katalogen" eher weniger vertreten und auch die catalouge extension für meine Dienste doch etwas zu überladen ist(da ich nix verkaufe) musste ich mich mit den gleichen Sachen asueinandersetzen und dein Tagebuch war eine große hilfe.

auch wenn ich oft geflucht ahbe, denn bei dir funktionierten Sachen, die ich etwas umständlciher machen musste. Was aber auch an meiner Datenbank struktur liegt. Hällt man sich nicht an die gedachten Strukturen, muss man einiges nachimplementieren. Um genauer zu sein ist es für mein vorhaben nicht so simpel nur 1:n beziheungen zu finden.

Zum Verständniss: eine Webseite hat einen ersteller/empfehler und einen Webmaster/Kontaktperson. Diese können gleich aber auch unterschiedlcih sein, sind aber beide in der tl_member gespeichert. Klar könnte ich miene Tabelle noch etwas "zerlegen" und schauen das ich den webmaster in eine kleine Tabelle auslagere. Bloß die Frage ist wie sinnvoll es für mich ist. Ich fahre nebenbei kleine Geschwindigkeitstests, da ich die komplette Seite dynamisch lassen muss...naja egal man könnte darauf eingehen, wenn es gewollt ist.

ich wollte darauf hinaus, je spezieller der wunsch ist, desto mehr eigenarbeit ist dabei.

Als Beispiel nehme man das save_callback für ein field. Funktioniert einwandfrei, außer ich definiere vorher ein input_field_callback, dann wird dieses Callback nicht mehr angerührt und ich muss einen Globalen callback ausführen. Und das nur, weil es eigentlich nur ein "anzeigefeld" sein sollte, was automatisch befüllt wird und auch nicht änderbar ist. (Vielleicht ist aber auch genau das der Knackpunkt warum es nicht geht)

so aber lange rede kurzer sinn

Zitat von dl1ely   

Das klingt wie das, was ich gerne für mein "pid"-Feld hätte, d.h. Sortierung nach den Paarnamen, die sich aus der pid ergeben anstatt numerisch nach dem pid-Wert. Leider wirft das einen Fehler in DC_Table.php (Zeile 3468 bei TL 2.8.3), wo $keys nicht definiert ist. Wenn ich das richtig sehe, funktioniert das nämlich nur mit hardcodierten "options" im DCA-Record (Zeile 3461), ein options_callback wird dort nicht ausgewertet.

mir ist auch aufgefallen und ich hoffe ich lehne mich nicht zu weit aus dem fenster, aber dieser Treiber scheint älter zu sein. Denn ich finde man merkt bei Typolight die struktur und die üerlegungen dahinter. Es gibt zwei möglichkeiten die DC_table zu erweitern. du kannst(solange es keine sortierung oder filter sein soll) kannst du dort wo du das label definierts und die Felder dafür, Tabellenübergreifend arbeiten.

Denn im Code wird der String nochmal zerlegt. du kannst sozusagen <feldname>:<table_name>.<table_field> sagen, dann macht er nochmal einen DB aufruf und holt diese Information für dich.

Willst du es beim Sortieren haben, gibt es auch dort ein hook, welcher aber leider erst aufgerufen wurde nachdem die DB abgefragt wurde(und auch leider schon sortiert). Nun bekomme ich schon das Ergebniss und kann dieses abändern. An sich ja auch nicht schlecht immerhin noch besser als ids dort stehen zu haben, aber dadurch ist es nur eine "gruppierung" und keine Sortierung mehr. Aber bei genauem überlegen(da ich mir dachte ok...dann halt mein eigener Treiber) glaube ich, das es leider dann nicht mehr trivial und performant sortiert werden kann...ist aber noch nicht ins detail durchdacht.

also bei mir sieht es nun so aus:

'label' => array ( 
            'fields'            => array ('id', 'title', 'url', 'ersteller_id:tl_member.username'), 
            'format'            => '<span style="font-weight: bold;">#%s</span> WCard von <span style="font-weight: bold;">%s</span>' 
                                    . ' (%s) Empfohlen von: <span style="font-weight: bold;">%s</span>', 
            'group_callback'    => array('WCards', 'listViewGroupCallback') 
        ),

dass Callback ist für die Nachbearbeitung wenn Sortiert wird. Obwohl ich deine Lösung auch sehr interessant finde und auch heute gleich mal testen werde, ob ich es auch nicht so lösen kann.

Nochmal Danke für deine mühe...auch wenn es schade ist, dass nun "erst"(ist nicht abwertend gemeint) dort bist, da ich gerade nachlesen wollte, was du bezüglich des frontend Inputs tust, da ich mir dort gerade den Kopf zerbreche.

user: dl1ely

Leider kann ich dir mit deinen Ausführungen in der zweiten Hälfte eines Posts nicht so ganz folgen.

Die Notation "feld_A:Tabelle_B.feld_B" im DCA-Record für Tabelle A ist mir gestern erst versehentlich unter die Augen gekommen (Ticket-ID #88). In meinem Fall würde es mir nicht soviel helfen, da ich einen String brauche, der sich aus mehreren Feldern aus Tabelle B zusammensetzt, und das auch noch abhängig von den Inhalten. Ohne eigenen PHP-Code geht es da nicht, darum muss der Label-Callback bleiben.

Bezüglich der Sortierung nach den Values aus dem options_callback habe ich ein Ticket aufgemacht (#1914), und den dort von mir vorgeschlagenene Code in meine eigene DC_Table.php übernommen. Funktioniert bei mir einwandfrei. Ich hoffe, dass Leo meinen Codevorschlag übernimmt und die nächste TL-Version das kann.

Mit meiner Entwicklung wirds noch weitergehen, auch der User-Input im Frontend kommt noch. Leider ist die Freizeit begrenzt, und es ist auch nur ein Freizeitprojekt. Von daher kann ich es leider nicht "runterrattern". Außerdem dauert das Erstellen jedes Schrittes ca. 1h, mit Tippen, Pasten vom Code in den Post und dem Anfertigen der Screenshots. Aber egal, da muss ich jetzt durch. Das Ende ist in Sicht :-).

user: Setsunaa

sorry das ich mich unverständlich ausgedrückt habe. Dies ist ein Problem, was häufiger auftritt, da ich ein, wie sagt man, "ein in ein schwarzes Loch hineindenker" bin . Aber ich versuche mein bestes, also bitte nicht übel nehmen...

Stimmt du setzt deinen String zusammen, dies dachte ich mir auch, als ich dir den Vorschlag geschrieben hatt, aber wollte diesen nicht rausnehmen, da es ja vielleicht anderen helfen kann.

Das mit dem Hook ist glaube ich eher etwas, was nur auftritt, wenn man nach etwas sortieren möchte, wo der eigentliche wert aber in einer anderen Tabelle steht. Wie es bei mir der Fall ist, da es gewünscht ist. Also zumindest wenn man sich nicht an die notation hällt. Da ich dies nicht tun kann(zumindest nicht in der Form, die ich jetzt habe), da ich zwei mal die id eines members in der Tabelle habe.

Also in "meiner" Tabelle steht ein ersteller für die Webseiten Karte, dies ist aber nur die id der tl_member. Wenn ich danach sortieren möchte, wird nur die Zahl aus "meiner" tabelle sortiert und ich habe keine möglichkeit gefunden zu sagen, das er diesen wert vorher mit dem usernamen aus tl_member zu ersetzen.

Leider nur danach, dadurch ist es halt nur eine Gruppierung.

Die Stelle in der DC_Table wo du gerne die Änderung hättest hatte ich schon gesehen, aber als für mich unbrauchbar abgestempelt, da dies mir nicht weiterhilft. Aber vielleicht habe ich auch nur etwas übersehen...

Naja ich muss eh viel selber schreiben, dank (an manchen stellen wirklich unnötige) Abhängigkeiten und Wünsche.

user: dl1ely

Zitat von Setsunaa   

Das mit dem Hook ist glaube ich eher etwas, was nur auftritt, wenn man nach etwas sortieren möchte, wo der eigentliche wert aber in einer anderen Tabelle steht. Wie es bei mir der Fall ist, da es gewünscht ist. Also zumindest wenn man sich nicht an die notation hällt. Da ich dies nicht tun kann(zumindest nicht in der Form, die ich jetzt habe), da ich zwei mal die id eines members in der Tabelle habe.

Also in "meiner" Tabelle steht ein ersteller für die Webseiten Karte, dies ist aber nur die id der tl_member. Wenn ich danach sortieren möchte, wird nur die Zahl aus "meiner" tabelle sortiert und ich habe keine möglichkeit gefunden zu sagen, das er diesen wert vorher mit dem usernamen aus tl_member zu ersetzen. Leider nur danach, dadurch ist es halt nur eine Gruppierung. Und das findInSet zusammen mit einem Options-Callback hilft dir nicht? Kann deinen konkreten Fall da gerade nicht überschauen. Wie gesagt, bei mir löst es mein Problem...

user: Setsunaa

hmm...wäre möglich, aber ich empfinde es als unschön und mein erster Gedanke dabei war, wie performant ist es, ab einer gewissen User Zahl. Denn dieses Callback würde wenn dann bei jedem einzelnen ausgeführt werden müssen. Aber es könnte auch ganz einfach sein, ich schau mir deine Lösung noch an. Danke für den Hinweiß.

Naja ist ersteinmal nicht so wichtig, das kann ich noch später ändern...und wenn es soll eh mehr über das Frontend gemacht werden, dafür muss ich eh noch einen "Admin" Bereich bauen...

Welches mir heute wirklich Kopfschmerzen bereitet....vorallem durch den Spruch "keep it simpel" und dann hat man kleinigkeiten, die echt nicht leicht zu lösen sind...naja einfach nur speziell

user: dl1ely

Zitat von dl1ely   

Bezüglich der Sortierung nach den Values aus dem options_callback habe ich ein Ticket aufgemacht (#1914), und den dort von mir vorgeschlagenene Code in meine eigene DC_Table.php übernommen. Funktioniert bei mir einwandfrei. Ich hoffe, dass Leo meinen Codevorschlag übernimmt und die nächste TL-Version das kann.

Juchu:

  • Status wurde geändert von New zu Accepted.
  • Zielversion wurde gesetzt auf 2.9.0.
Ansichten
Meine Werkzeuge

Contao Community Documentation

... aber beim nächsten Mal nehm ich einfach den Catalog... da hab ich weniger Arbeit mit.

MacKP
Navigation
Verstehen
Verwenden
Entwickeln
Verschiedenes
Werkzeuge