Wieso kein BE-Filter für Feld vom Typ "Tags"?
Hallo allerseits,
Wieso lässt sich im Catalog-Backend kein Filter für ein Feld vom Typ "Tags" setzen?
Im Frontend kann ich doch daraus auch einen Filter bauen... Wieso dann nicht "einfach" auch im Backend?
Ist das technisch nicht möglich, oder zu komplex von der Programmierung her, oder einfach nur ein weiterer (vielleicht bereits bestehender) Feature-Request von mir?
In meinem Katalog habe ich z.B. ein Tags-Feld "Themen", welches alle Einträge kategorisiert / durch Mehrfachauswahl-Möglichkeit verschlagwortet. Diese Themen speisen dann auf einer Catalog-Listen-Seite einen Filter und dienen gleichzeitig auf der Catalog-Leser-Seite als entsprechende Meta-Keywords für Suchmaschinen.
Und letzteres erreicht man ja derzeit z.B. auch "nur" mittels eines kleinen Hooks im catalog_full_xyz-Template:
PHP-Code:
$GLOBALS['TL_KEYWORDS'] = $entry['data']['themen']['value'];
Und im Backend finde ich es einfach sau-schade (um nicht zu sagen blöd ; o ), dass man gerade nach dererlei "Themen" o.ä. nicht filtern kann, vor allem wenn man einen sehr grossen Katalog hat, dessen Einträge sich nun mal in erster Linie durch diese Art von "Tags" definieren / kategorisieren...!
Beste Grüße
Liste der Anhänge anzeigen (Anzahl: 1)
catalog felder vom typ tags in backend-listen als filter verwenden
Hallo zusammen
Diese Woche hatte ich dieselbe Anforderung. Ich hab ein Katalog mit Schulen (cat_school). Jede Schule hat ein Kursangebot bestehend aus verschiedenen Kurstypen (cat_kurstypen). Kursangebot (bzw. Kurstypen ist ein Tags Feld). Da es viele Schulen gibt, wäre es für die Administration sehr hilfreich die Schulliste nach Kurstypen filtrieren zu können. Schlussendlich hab ich eine Lösung gefunden die in diesem Fall funktioniert. Ich hab es so allgemein und update-sicher wie möglich implementiert, somit sollte die Lösung auch wiederverwendbar sein. Über Verbesserungsvorschläge und Rückmeldungen jeder Art würde ich mich freuen.
So genug geredet. Die Folgenden 6 Schritte führen zum Erfolg :p
- Die Lösung arbeitet stark mit DCA Anpassungen. Als erstes wird das gewünschte Feld als Filter definiert mit einem options_callback.
PHP-Code:
// cat_school.php ode dcaconfig.php
$GLOBALS['TL_DCA']['cat_school']['fields']['kurstypen']['filter'] = true;
$GLOBALS['TL_DCA']['cat_school']['fields']['kurstypen']['options_callback'] = array('cat_school', 'getFilterOptionsKurstyp');
class cat_school extends Backend{
public function getFilterOptionsKurstyp(DC_Table $table){
$kurstypen = array();
$dbKurstypen = $this->Database->prepare("SELECT id, name FROM cat_kurstyp ORDER BY name ASC")->execute();
if($dbKurstypen->numRows){
$kurstypenAssoc = $dbKurstypen->fetchAllAssoc();
foreach($kurstypenAssoc as $i => $k){
$kurstypen[$k['id']] = $k['name'];
}
}
return $kurstypen;
}
}
- Tags Felder haben keinen inputType zugewiesen. Wir ändern das, indem wir ein neuen inputType tags einführen. Anhand dieses inputTyps wird später auch die DC_Table angepasst.
PHP-Code:
// cat_school.php ode dcaconfig.php
// use inputType tags as trigger for special treatment (custom DC_Table code)
$GLOBALS['TL_DCA']['cat_school']['fields']['kurstypen']['inputType'] = 'tags';
- contao speichert die Filter-Einstellungen im Session Objekt ab. Über die DCA Eigenschaft list.sorting.filter können wir einen eigenen Filter definieren
PHP-Code:
// cat_school.php ode dcaconfig.php
$filter = Session::getInstance()->get('filter');
if($filter['cat_school']['kurstypen']){
$GLOBALS['TL_DCA']['cat_school']['list']['sorting']['filter'] = array(array(" FIND_IN_SET(?, kurstypen) ", $filter['cat_school']['kurstypen']));
}
- Schritt 2 und 3 sollten nur ausgeführt werden, wenn wir uns in der Listenansicht befinden.
PHP-Code:
// cat_school.php ode dcaconfig.php
if(Input::getInstance()->get('act') == false){
// Code von schritt 2. und 3.
}
- Nun sind wir soweit, dass der Filter in der Filterbar in der Listenansicht des Katalogs cat_school angezeigt wird. Dazu werden über den options_callback die alle Optionen geladen, angezeigt werden nur diejenigen welche auch in einem Eintrag in der Liste vorkommen.
ACHTUNG: Damit die Filterbar angezeigt wird muss mindestens ein "normales" anderes Feld im Katalog als Filter in der Backendlistenansicht aktiviert sein. Bei mir hab ich das Feld language als Filterbares Feld definiert. Weil der Adminstrator das dann aber nicht braucht, habe ich mit folgenden Code wieder ausgeblendet.
PHP-Code:
// cat_school.php ode dcaconfig.php
// for any reason modifying panelLayout does not work.
// => define at least one catalog field (e.g. language) to be filterable, then the filter bar is generated automatically
// if the field should not be displayed, hide it with
$GLOBALS['TL_DCA']['cat_school']['fields']['language']['filter'] = false;
//$panelLayout = $GLOBALS['TL_DCA']['cat_school']['list']['sorting']['panelLayout'];
//$GLOBALS['TL_DCA']['cat_school']['list']['sorting']['panelLayout'] .= empty($panelLayout) ? 'filter,' : ',filter,';
- Obwohl der Filter angezeigt wird, wird er noch nicht funktionieren. Wie oben erwähnt muss der neue InputType tags noch in DC_Table implementiert werden. Dazu entweder die Anweisungen und den Code hier verwenden, oder die Patches aus dem Anhang verwenden.
PHP-Code:
/*
* contao 2.10.4
*
* custom code to add to DC_Table line ca. 4579
// helps to enable tags filter menu in backend list view of catlogs
// added by ukunz, 16. Feb 2012
else if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType']['tags']){
$doptions = explode(",", $option);
}
// end added by ukunz
* custom code to add to DC_Table line ca. 4455
// added by ukunz 16. Feb 2012
elseif($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType']['tags']){
// do nothing, filtering tag fields is done with dcaconfig (sorting.filter)
}
// end added by ukunz
*
---------------------------------------------------------------------------------------
* contao 2.11
*
* custom code to add to DC_Table line ca. 4568
// added by ukunz 16. Feb 2012
else if($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType']['tags']){
// do nothing, filtering tag fields is done with dcaconfig (sorting.filter)
}
// end added by ukunz
* custom code to add to DC_Table line ca. 4698
// added by ukunz, 16. Feb 2012
else if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType']['tags']){
$doptions = explode(",", $option);
}
// end added by ukunz
* */
Bis zur Lösung wars ein relativ steiniger Weg, der Code war (mal wieder) die beste Dokumentation. Ich hoffe dem Einen odern Anderen hilfts weiter. Die Lösung ist getestet in contao 2.10.4 und 2.11. Für die Anpassungem in DC_Table.php habe ich zusätzlich die patches im Anhang, sowie die komplette cat_school.php.
Liste der Anhänge anzeigen (Anzahl: 1)
catalog backend-listen filter mit feldern aus fremd (foreign) tabellen verwenden
Ergänzend zur Erklärung unten möchte ich hier noch ausführen, wie man einen Filter einbauen kann, der Einträge anhand eines Wertes aus einer verknüpften Tabelle (foreign key) filtert. Dies ist relativ einfach möglich. Allerdings muss auch hier wieder in den Core Code eingegriffen werden. Dafür gibt es ebenfalls einen Patch im Anhang. Diese Anleitung bezieht sich auf contao 2.11.
Zuerst zur Ausgangslage. Ich möchte im Katalog cat_school ein Filter verwenden, der mir Schulen in einem bestimmten Land anzeigt. Die Entitäten sind wie folgt verknüpft:
Code:
+--------------+
| cat_country |
|--------------|
| <---+
| | |
| | |
+--------------+ |
|
+--------------+---+
| cat_place |
|--------------<---+
| | |
| | |
+--------------+ |
|
+--------------+ |
| cat_school | |
|--------------| |
| +---+
| |
| |
+--------------+
1. & 2. Schritt 1 und 2 können vom Prinzipt her von unten übernommen werden. Der Code für das Laden der Optionen im Filterdropdown (options_callback) ist jetzt wie folgt:
PHP-Code:
$GLOBALS['TL_DCA']['cat_school']['fields']['country']['filter'] = true;
$GLOBALS['TL_DCA']['cat_school']['fields']['country']['options_callback'] = array('cat_school', 'getFilterOptionsCountry');
$GLOBALS['TL_DCA']['cat_school']['fields']['country']['eval']['tl_class'] .= ' invisible'; // hide field in entry edit mode
class cat_school extends Backend{
public function getFilterOptionsCountry(DC_Table $table){
$countries = array();
$dbCountries = $this->Database->prepare("SELECT id, name FROM cat_country ORDER BY name ASC")->execute();
if($dbCountries->numRows){
$countriesAssoc = $dbCountries->fetchAllAssoc();
foreach($countriesAssoc as $i => $k){
$countries[$k['id']] = $k['name'];
}
}
return $countries;
}
}
Da wir im DCA das Feld country ansprechen, ist es nötig dieses im catalog im Backend als Feld anzulegen. Das Feld muss vom Typ tags sein, und sollte in diesem Falle country als Spaltenname haben. Da nicht erwünscht ist, dass das Feld im Bearbeiten Modus eines Eintrags sichtbar ist, kann man es mit einem kleinen Trick ausblenden (tl_class = invisible).
3. In Schritt drei verwenden wir eine etwas anderes Filterkriterium. Der ganze Code für den angepassten Filter packen wieder in eine if, damit dieser nur in der Listenansicht ausgeführt wird.
PHP-Code:
if(Input::getInstance()->get('act') == false){
$filter = Session::getInstance()->get('filter');
if($filter['cat_school']['country']){
$GLOBALS['TL_DCA']['cat_school']['list']['sorting']['filter'][] = array(" FIND_IN_SET(?, (SELECT DISTINCT(c.id) FROM cat_school s INNER JOIN cat_place p ON p.id = s.place INNER JOIN cat_country c ON c.id = p.country WHERE s.id = cat_school.id)) ", $filter['cat_school']['country']);
}
// use inputType tags as trigger for special treatment (custom code)
$GLOBALS['TL_DCA']['cat_school']['fields']['country']['inputType'] = 'tags';
}
Die FIND_IN_SET Funktion ist nur erfüllt, wenn die Schule (cat_school.id) im Land des ausgewählten Filterlandes ($filter['cat_school']['country']) ist.
4. Soweit so gut. Das Filterfeld wird erkannt und im Backen in der Filterbar angezeigt. Allerdings werden noch keine Optionen angezeigt, obwohl wir in unserem options_callback welche zurückgeben. Das liegt daran, wie DC_Table unseren Callback-Rückgabewert verwendet. Normalerweise werden in den Filterdropdowns ja nur Werte angezeigt, die in mindestens einem Katalogeintrag als Feldwert vorkommen. Da wir kein Feld country haben (wollen), wird es auch nie eine Übereinstimmung geben zwischen vorhandenen country Werten und den von uns zurückgegebenen country Werten. Hier müssen wir also den core leicht abändern, damit dieser einfach alle von uns retournierten countries nimmt und mit diesen arbeitet, ohne sie noch mit den Vorhandenen zu vergleichen. Ersetze dazu Zeile 4731 in DC_Table.php durch:
PHP-Code:
$options = array_keys($options_callback);
Meiner Meinung nach wäre es eh besser in allen options_callbacks, ohne weitere Vergleiche, immer nur den Rückgabewert des Callbacks zu beachten. Schliesslich kann der ja entscheiden, was effektiv zu verwenden ist. Die vorhandenen Optionen die der Core verwenden würde, könnte man ja als Argument mitgeben. Dann kann der Callback entscheiden wie er mit denen umgehen will.
Source Code und Patch im Anhang. Ich hoffe das hilft jemanden Weiter. Wie gesagt Feedback willkommen. Hattet ihr auch schon ähnliche Anforderungen, wie habt ihr das gelöst?
filter dropdowns durchsuchbar machen im backend
Hab grad noch ne gute Neuigkeit die zum Thema passt. Möchte man, dass die select Felder in der Filterbar durchsuchbar sind (siehe neues contao Feature), kann auch das mit ein bischen Nachhilfe erreicht werden.
1. In der DCA Datei cat_school.php folgende Zeilen einfügen:
PHP-Code:
if(TL_MODE == "BE"){
$GLOBALS['TL_CSS'][] = 'custom/css/backend.css';
$GLOBALS['TL_JAVASCRIPT'][] = "custom/js/backend.js";
}
Die Pfade können angepasst werden und sind relativ zu TL_ROOT anzugeben. Wenn also deine css-Datei im tl_files Verzeichnis liegt einfach tl_files/backend.css angeben.
2. Die backend.css mit folgendem Inhalt abfüllen:
Code:
.chzn-container {
text-align: left;
}
.tl_subpanel strong {
vertical-align: top;
}
#country_chzn .chzn-single{ /* foreach field you apply 'chosen' to, this time i only chosenized the country field */
height: 20px;
}
3. Die backend.js mit folgendem Inhalt abfüllen:
Code:
window.addEvent('domready', function() {
// from http://stackoverflow.com/questions/3541822/getting-all-visible-elements-using-mootools
$extend(Selectors.Pseudo, {
visible: function() {
if (this.getStyle('visibility') != 'hidden' && this.isVisible() && this.isDisplayed()) {
return this;
}
}
});
// mark select elements in filterbar as searchable
$$('select#country').addClass("tl_chosen"); // country must match the catalog field name of the field you want to make searchable in the filterbar
// make marked selects searchable
if (Elements.chosen != undefined) {
$$('select.tl_chosen:visible').chosen(); // :visible allows to only get select elements that have not already been processed by the chosen script.
}
});
In unserem Fall wird das country-Dropdown durchsuchbar.
Das wars schon. Natürlich hätte man beim css auch mit einem eigenen Backend-Theme arbeiten können. Für diese kleine Änderung lohnt sich der Aufwand allerdings nicht. Zumal es ja auch so update-sicher ist.