Liste der Anhänge anzeigen (Anzahl: 4)
Schritt 12: 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:
Code:
--
-- 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:
PHP-Code:
// 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):
PHP-Code:
/**
* 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?
https://community.contao.org/de/atta...1&d=1272743117
Wo wir schonmal 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-Code:
<?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, mir 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-Code:
<?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.
PHP-Code:
// 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.
PHP-Code:
'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.
PHP-Code:
'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 Extensioncreator sie angelegt hat...
PHP-Code:
// Palettes
'palettes' => array
(
'__selector__' => array(''),
'default' => '{couple_legend},pid;{tournament_legend},datum,turnierort,turnierart,startgruppe,startklasse,lat_std;'
.'{result_legend},anzahlpaare,platz_von,platz_bis,bemerkung;'
),
// Subpalettes
'subpalettes' => array
(
'' => ''
),
Die Palettendefinition hat sich nicht geändert, und Subpaletten gibt es weiterhin nicht.
PHP-Code:
// 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.
PHP-Code:
'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 .
PHP-Code:
'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:
PHP-Code:
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:
PHP-Code:
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 Dropdownbox 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#.
PHP-Code:
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...
PHP-Code:
$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.
PHP-Code:
$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.
PHP-Code:
$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:
https://community.contao.org/de/atta...1&d=1272746013
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.
https://community.contao.org/de/atta...1&d=1272746078
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?
https://community.contao.org/de/atta...1&d=1272746218
Und bei Neuanlage einer Turniermeldung werden in der Dropdown-Liste nurnoch die aktiven Paare aufgeführt, nicht mehr ALLE.
Im nächsten Schritt geht es ans Frontendmodul für die Meldeliste...
Liste der Anhänge anzeigen (Anzahl: 4)
Schritt 13: Frontendmodul Meldeliste
So, weiter geht es mit dem vorerst letzten Schritt: Das Frontendmodul für die Meldeliste.
Besondere Herausforderungen sind hier die Möglichkeit der Verknüpfung einzelner Einträge mit dem Frontendmodul für die Turnierpaardetails, und eine konfigurierbare seitenweise Anzeige der Einträge der Meldeliste (Da diese im Lauf der Zeit sehr umfangreich werden kann).
Die Klasse des Frontendmoduls heisst "gwMeldeliste", und da ich sie schon bei der Erstellung im Extension Creator angegeben hatte, ist diese schon als Frontendmodul registriert. Trotzdem checken wir das nochmal in /system/modules/gw_turnierpaare/config.php :
PHP-Code:
// Front end module
array_insert($GLOBALS['FE_MOD']['turnierpaare'], 0, array
(
'gw_turnierpaarliste' => 'gwTurnierpaarliste',
'gw_meldeliste' => 'gwMeldeliste'
));
Für die Konfiguration des Frontend-Moduls für die Darstellung benötige ich zwei Parameter: Die Anzahl der Datensätze pro Seite und die URL, auf die weitergeleitet werden soll, wenn man auf einen Tanzpaarnamen klickt (Das sollte eine URL sein, auf der das Frontendmodul der Turnierpaarliste eingebunden ist). Darum muss /system/modules/gw_turnierpaare/config/database.sql erweitert werden:
Code:
--
-- Extend table 'tl_module'
--
CREATE TABLE `tl_module` (
`gw_tp_showonlyactive` char(1) NOT NULL default 'B',
`gw_tp_couplesorting` char(1) NOT NULL default 'A',
`gw_ml_pagesize` int(4) NOT NULL default '50',
`gw_ml_coupledetails` varchar(255) NULL default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
gw_ml_pagesize ist also ein int mit maximal 4 Stellen, gw_ml_coupledetails ein string.
Damit die beim Anlegen des Moduls auch angezeigt und editiert werden können, müssen wir an das DCA von tl_module ran, also in /system/modules/gw_turnierpaare/dca/tl_module.php einfügen:
PHP-Code:
$GLOBALS['TL_DCA']['tl_module']['palettes']['gw_meldeliste'] = '{title_legend},name,headline,type;{size_legend},gw_ml_pagesize, gw_ml_coupledetails;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID,space';
$GLOBALS['TL_DCA']['tl_module']['fields']['gw_ml_pagesize'] = array
(
'label' => &$GLOBALS['TL_LANG']['tl_module']['gw_ml_pagesize'],
'default' => '50',
'exclude' => true,
'inputType' => 'text',
'eval' => array('mandatory'=>true, 'minlength' => 1, 'maxlength'=>4, 'rgxp' => 'digit', 'tl_class' => 'w50')
);
$GLOBALS['TL_DCA']['tl_module']['fields']['gw_ml_coupledetails'] = array
(
'label' => &$GLOBALS['TL_LANG']['tl_module']['gw_ml_coupledetails'],
'exclude' => true,
'inputType' => 'text',
'eval' => array('mandatory'=>false, 'tl_class' => 'w50')
);
Zunächst legen wir die neue Palette an, in der neben einigen Standardfeldern unsere beiden neuen Datenbankfelder stehen, und anschließend definieren wir diese Felder im DCA. Für die pagesize nehme ich einen Default von 50, ansonsten sind die Optionen inzwischen wohlbekannt.
Nun brauchen wir noch hübsche Labels für die neuen Felder im Backend. /system/modules/gw_turnierpaare/languages/de/modules.php:
PHP-Code:
$GLOBALS['TL_LANG']['tl_module']['size_legend'] = 'Seitenlänge und Detail-URL';
$GLOBALS['TL_LANG']['tl_module']['gw_ml_pagesize'] = array('Meldungen pro Seite', 'Bitte geben Sie an, wieviele Meldungen pro Seite ausgegeben werden sollen');
$GLOBALS['TL_LANG']['tl_module']['gw_ml_coupledetails'] = array('URL der Paar-Detailseite' , 'URL, auf der die Paardetails ausgegeben werden, z.B. /turnierpaarliste/info/');
Entsprechend die englische Variante natürlich genauso.
Damit sind wir im Backend hier angekommen:
https://community.contao.org/de/atta...1&d=1275065020
Nun das Frontendmodul /system/modules/gw_turnierpaare/gwMeldeliste.php :
PHP-Code:
<?php if (!defined('TL_ROOT')) die('You can not access this file directly!');
/**
* Class gwMeldeliste
*
* @copyright (C) 2010
* @author Stefan Pfeiffer
* @package Controller
*/
class gwMeldeliste extends Module
{
/**
* Template
* @var string
*/
protected $strTemplate = 'gw_meldeliste';
protected $strErrorTemplate = 'gw_meldeliste_error';
Zwei Templatenamen: Einer für die Ausgabe der Liste, der andere wenn etwas schiefgegangen ist...
PHP-Code:
public static $strPageKey = 'page';
Der URL-Bestandteil, der beim Blättern in der Liste unsere aktuelle Seite mitzählt. Mit 'page' also z.B. .../meldeliste/page/3.html um die dritte Seite anzeigen zu lassen.
PHP-Code:
/**
* Generate module
*/
protected function compile()
{
$moduleParams = $this->Database->prepare("SELECT gw_ml_pagesize, gw_ml_coupledetails FROM tl_module WHERE id=?")
->limit(1)
->execute($this->id);
$pagesize = $moduleParams->gw_ml_pagesize;
$this->Template->coupledetails = $moduleParams->gw_ml_coupledetails;
Zunächst holen wir uns unsere beiden Modulparameter aus der Datenbank. die Detail-URL schreiben wir gleich ins Template, die pagesize merken wir uns erstmal.
PHP-Code:
if ( strlen($this->Input->get(gwMeldeliste::$strPageKey)) )
{
if(is_numeric($this->Input->get(gwMeldeliste::$strPageKey)))
{
$page = $this->Input->get(gwMeldeliste::$strPageKey);
}
else
{
// Error
$this->Template = new FrontendTemplate($this->strErrorTemplate);
return;
}
}
else
{
$page = 0;
}
$this->Template->page = $page;
Falls eine Seitenzahl in der URL angegeben, wird diese in $page gespeichert. Falls die Angabe nicht-numerisch ist, wird das Fehlertemplate ausgegeben. Und wenn keine Seitenzahl angegeben wird, tragen wir 0 ins Template ein.
PHP-Code:
$limit = " LIMIT ".($pagesize*$page).",".$pagesize;
$testNextPage = $this->Database->execute("SELECT * from tl_gw_meldungen ORDER BY datum DESC LIMIT ".($pagesize*($page+1)).",1");
if($testNextPage->numRows < 1)
{
$this->Template->nextpage = -1;
}
else
{
$this->Template->nextpage = $page+1;
}
Hier basteln wir uns das passende "LIMIT"-Statement für die aktuell ausgewählte Seite zusammen. Ausserdem machen wir einen Testselect auf die erste row der darauffolgenden Seite. Falls diese existiert, wird die Nummer der nächsten Seite ins Template geschrieben, ansonsten eine -1 als Hinweis fürs Template.
Ich weiss, dass man diesen Check, ob eine weitere Seite existiert auch anders lösen kann, wahrscheinlich sogar eleganter. Aber erstmal funktioniert es :-).
PHP-Code:
$arrMeldungen = array();
Hier kommen die ganzen Datensätze des Ergebnis rein...
PHP-Code:
$objMeldungen = $this->Database->execute("SELECT * FROM tl_gw_meldungen ORDER BY datum DESC".$limit);
if($objMeldungen->numRows == 0)
{
// Error
$this->Template = new FrontendTemplate($this->strErrorTemplate);
return;
}
Ergebnisdatensätze aus der DB holen - wenn keine gefunden wurden, dann Error-Template ausgeben.
PHP-Code:
while($newArr = $objMeldungen->fetchAssoc())
{
Jede Row in ein assoziatives Array umwandeln...
PHP-Code:
$objPaar = $this->Database->execute("SELECT * FROM tl_gw_turnierpaare WHERE id=".$newArr['pid']);
$name = $objPaar->partnernachname;
if($objPaar->partnervorname)
{
$name .= ', '.$objPaar->partnervorname;
}
if($objPaar->partnerinnachname)
{
$name .= ' und '.$objPaar->partnerinnachname;
}
if($objPaar->partnerinvorname)
{
$name .= ', '.$objPaar->partnerinvorname;
}
$newArr['name'] = $name;
$newArr['paaralias'] = $objPaar->alias;
Und für jede Row aus der pid den Namen und den Alias des Turnierpaares bestimmen und mit ins assoziative Array aufnehmen.
PHP-Code:
$arrMeldungen[] = $newArr;
}
$this->Template->meldungen = $arrMeldungen;
}
}
?>
Schließlich wird jeder Datensatz an das große Ergebnisarray angehangen und dann ins Template geschrieben.
Womit wir zu den Templates kommen. Zunächst /system/modules/gw_turnierpaare/templates/gw_meldeliste_error.tpl :
PHP-Code:
<div class="<?php echo $this->class; ?> block meldeliste"<?php echo $this->cssID; ?>
<?php if ($this->style): ?> style="<?php echo $this->style; ?>"<?php endif; ?>>
<h3>Es trat ein Fehler auf!</h3>
Erstmal easy as this, hier könnte man sich natürlich beliebig weiter austoben als nur mit diesem lapidaren Satz.
Spannender ist /system/modules/gw_turnierpaare/templates/gw_meldeliste.tpl :
PHP-Code:
<div class="<?php echo $this->class; ?>"<?php echo $this->cssID; ?><?php if ($this->style): ?> style="<?php echo $this->style; ?>"<?php endif; ?>>
<?php if ($this->headline): ?>
<<?php echo $this->hl; ?>><?php echo $this->headline; ?></<?php echo $this->hl; ?>>
<?php endif; ?>
Standardauftakt...
PHP-Code:
<table cellpadding="4" cellspacing="0" summary="Meldeliste">
<thead>
<tr>
<th class="centered">Datum</th>
<th class="centered">Name</th>
<th class="centered">Ort</th>
<th class="centered">Turnier</th>
<th class="centered">Turnierart</th>
<th class="centered">Platz</th>
<th class="centered">Paare</th>
<th class="centered">Bemerkung</th>
</tr>
</thead>
Header der Tabelle...
PHP-Code:
<tbody>
<?php foreach ($this->meldungen as $meldung): ?>
Wir gehen durch alle rows im Array durch.
PHP-Code:
<tr>
<td class="centered">
<?php echo date('d.m.Y', $meldung['datum']); ?>
</td>
Datum "richtig" formatieren...
PHP-Code:
<td><a href="<?php echo $this->coupledetails; ?><?php echo $meldung['paaralias']; ?>.html"><?php echo $meldung['name']; ?></a>
</td>
Hier wird der Paarname und als hinterlegter Link die Detail-URL mit dem Paaralias ausgegeben.
PHP-Code:
<td class="centered"><strong><?php echo $meldung['turnierort']; ?></strong>
</td>
<td class="centered<?php if ($meldung['lat_std'] == 'Std'){echo ' std';} else { if ($meldung['lat_std'] == 'Lat'){echo ' lat';}}?>"><?php echo $meldung['startgruppe']; ?> <?php echo $meldung['startklasse']; ?> <?php echo $meldung['lat_std']; ?>
</td>
In Abhängigkeit von der Tanzart wird dem TD eine CSS-Klasse zugewiesen (zur farblichen Absetzung).
PHP-Code:
<td class="centered"><?php echo $meldung['turnierart']; ?>
</td>
<td class="centered"><?php echo $meldung['platz_von']; ?>
<?php if (strlen($meldung['platz_bis']) > 0): ?>
<?php if ($meldung['platz_von'] != $meldung['platz_bis']): ?>
<?php echo " - ".$meldung['platz_bis']; ?>
<?php endif; ?>
<?php endif; ?>
</td>
<td class="centered"><?php echo $meldung['anzahlpaare']; ?>
</td>
<td><?php echo $meldung['bemerkung']; ?>
</td>
</tr><?php endforeach; ?>
</tbody>
</table>
So weit unsere Tabelle...Es fehlen noch die Links, um eine Seite vor oder zurück zu springen...wenn es dann davor oder dahinter noch Seiten gibt.
PHP-Code:
<?php if ($this->page > 1): ?>
<a id="prev" href="/{{env::page_alias}}/<?php echo gwMeldeliste::$strPageKey; ?>/<?php echo ($this->page-1); ?>.html"<<<</a>
<?php elseif ($this->page == 1): ?>
<a href="/{{env::page_alias}}.html"<<<</a>
<?php endif; ?>
Seite <?php echo ($this->page+1); ?>
<?php if ($this->nextpage >= 0): ?>
<a id="next" href="/{{env::page_alias}}/<?php echo gwMeldeliste::$strPageKey; ?>/<?php echo $this->nextpage; ?>.html">>>></a>
<?php endif; ?>
</div>
Das liefert uns im Frontend (ohne weitere CSS-Modifikationen) bei 6 Meldungen (nur zur Demo) pro Seite:
https://community.contao.org/de/atta...1&d=1275066520
und beim Weiterschalten auf die zweite Seite (.../page/1.html):
https://community.contao.org/de/atta...1&d=1275066520
Ein Klick auf den Turnierpaarnamen springt zur Detail-URL, bei mir /turnierpaarliste/info/<alias>.html:
https://community.contao.org/de/atta...1&d=1275066520
So, das soll es erstmal gewesen sein. Ich habe eine Menge gelernt, und würde den Code JETZT ganz anders aufziehen. Und das werde ich auch tun (haha). Allerdings ist der Zeithorizont unklar, und ich werde es auch nicht mehr schaffen "nebenbei" das Tagebuch zu führen. Pro Folge war es doch ca. eine Stunde Arbeit - mehr als gedacht! Mir fehlen für meinen Einsatz des Moduls auch noch einige kleine Features, aber das ist wirklich sehr speziell. Zunächst werde ich gemeinsam genutzte Funktionen der Back- und Frontendmodule wohl in eine gemeinsame Klasse packen, um die Module und auch die Templates etwas zu entschlacken.
Vielleicht werde ich zu gegebener Zeit zum Thema "AJAX" in Frontendmodulen nochmal etwas schreiben, aber dann nur speziell auf diesen Teilbereich bezogen.
Es tut mir leid, dass es hier die letzten Wochen etwas zäh und "langweilig" wurde, aber unerwarteter Weise war meine Freizeit (in der ich das hier tue) knapper als gedacht. Ich hoffe, Einige konnten teilweise Kleinigkeiten aus meinem Lernprozess mitnehmen , egal wie chaotisch er war, und für sich sinnvoll nutzen.
Stefan
EDIT: Den Downloadlink zum Archiv des Quelltextes im ersten Beitrag des Threads ist jetzt auch aktualisiert...
Dieses Tutorial ausdrucken
Hat jemand eine Möglichkeit gefunden, dieses Tutorial vollständig und lesbar auszudrucken? Es soll ja noch Leute geben, die ganz altmodisch Papier und Textmarker verwenden. ;)
Wenn ich die Normalansicht verwende, sind zwar die Abbildungen dabei, aber die Codeteile sind fast alle abgeschnitten.
Verwende ich die Themenoption Druckbare Version anzeigen fehlen nicht nur die Abbildungen, sondern bei einigen Codeteilen ist ebenfalls ein Teil abgeschnitten.
Dieses Tutorial ausdrucken
Also ich hab' mir da in einer irren Foddelarbeit ein 127-seitiges (!) PDF-Dokument erstellt, das ich natürlich gerne der Community zur Verfügung stellen würde.
Allerdings sind dabei nicht nur die Lobhudel-Beiträge entfernt, sondern auch manche Anrede und Grußformel. Ich wollt's so kurz wie möglich haben. Auch die Fotos der Poster und den ganzen Forumskram habe ich entfernt.
Also müssten Stefan und Nina wohl ihr Plazet geben, dass diese PDF-Datei hier eingestellt wird. Soll ich Euch beiden das Ding erstmal per PM zur Ansicht zuschicken?
Leider habe ich keine Sorgfalt darauf verwendet, dass
- die Seitenumbrüche ordentlich sind
- die Zeilenumbrüche im Code korrekt sind
da mir das
- nicht so wichtig und vor allem
- zuviel Arbeit
war. Für meine Zwecke langt es so wie es ist.
Auf Wunsch (Kermit wirbelt wild mit den Händen!) würde ich mich vielleicht an die Überarbeitung setzen - oder irgendjemand, der sowas gerne macht, die DOC-Datei mailen :D
Irgendwas ist da komisch...
Hallo,
ich habe mich mit diesem Tutorial beschäftigt, weil ich auf Fehlersuche bin. Ich habe vorher bereits ein Tutorial durchgearbeitet (http://blog.qzminski.com/2010/04/cre...dule-part-one/) und stoße (wie auch bei diesem Tutorial) auf folgendes Problem:
Ich sehe in der Liste keine selbst-erstellten Module zur Auswahl, wenn ich einen neuen Artikel erstellen will. Weder die CD-Collection, noch die Paar-Tanz-Sachen.
Ich nutze Contao 2.9.1, hab auch mal nen Screenshot angehängt.
http://www.embeeart.de/stuff/contao-...tao-screen.jpg
Ich freue mich sehr wenn jemand mir helfen kann, ich komm einfach nicht weiter.
Vielen Dank und Liebe Grüße
Qualtext