Türchen 03: Magento Template Filter

Jeder, der sich schonmal Magento angeguckt hat, kennt diese Konstrukte mit {{ und }} in CMS-Seiten, -Blöcken, E-Mails. Ich bin einfach so frei zu behaupten das sich die meisten schon ziemlich damit rumgeschlagen haben, ohne genau die Funktionalität, und vor allem die Erweiterbarkeit, dieser Filter zu kennen.

Nach dem tollen Einstieg in den Adventskalender möchte ich in diesem Türchen also die Funktion und das Erweitern des Template-Filters in Magento erklären. Anfangs die Funktionsweise, danach das Erweitern und am Ende einen Überblick was uns Magento schon im Standard zur Verfügung stellt.

Zu aller erst einen kurzen Blick auf den Template-Filter: Wir können in E-Mails, CMS-Seiten und weiteren Stellen Platzhalter einbauen die z.B. durch bestimmte Blöcke, URLs oder (in E-Mails) Kundendaten ersetzt werden. Der Syntax dafür ist super einfach: {{platzhalter argumente...}}.

Als Beispiel nehmen wir einfach mal {{var customer.email}} oder {{store url=”checkout/cart”}}. Das erste würde zum Beispiel in E-Mail-Templates durch die E-Mail-Adresse des Kunden ersetzt. Das zweite könnte einen Link auf /checkout/cart/ erzeugen.

Funktionsweise

Das Herzstück der ganzen Angelegenheit ist die Klasse Varien_Filter_Template die Zend_Filter_Interface Die damit  die Funktion filter($value)) implementieren muss. Varien_Filter_Template::filter($value) verarbeitet zuerst {{if...}} und {{depend...}} Anweisungen (bzw. Direktiven), und danach alles, was auf {{([a-z]{0,10})(.*?)}} passt. Dabei wird so vorgegangen das mit is_callable geprüft wird ob $1 (die ersten 0-10 Zeichen) mit dem Zusatz “Directive” aufgerufen werden kann. Sollte dies der Fall sein wird die Funktion mit dem Argument $2 aufgerufen, und der Rückgabewert in der Ausgabe eingefügt.

Nutzung in E-Mails und CMS-Seiten

E-Mails nutzen Mage_Core_Model_Email_Template_Filter welcher von Varien_Filter_Template erbt und einige weitere Funktionen (Magento-Spezifisch) zur Verfügung stellt. CMS-Seiten und Blöcke nutzen Mage_Widget_Model_Template_Filter welches wiederum von Mage_Cms_Model_Template_Filter und dieser vom E-Mail-Filter erbt, allerdings von der Funktionalität nichts weiter bietet, außer das noch die widgetDirective() hinzugefügt wurde. Welcher Filter genau für CMS-Elemente genutzt wird ist übrigens in der Konfiguration in global/cms/page/tempate_filter und global/cms/block/tempate_filter definiert.
Achtung: tempate ist kein Schreibfehler von mir, sondern von Magento ;-)

An dieser Stelle könnten wir jetzt ansetzen und uns ein eigenes Model bauen: Test_Filter_Model_Template_Filter soll von Mage_Widget_Model_Template_Filter erben und per config.xml den Wert von global/cms/page/tempate_filter auf filter/template_filter setzen.
Danach können wir z.B. eine Methode mit dem Namen testDirective($construction) definieren die dann durch {{test}} aufgerufen wird.

public function testDirective($construction) {
	return 'Es ist ' . date('H:i:s') . ' ' . print_r($construction, 1);
}

Einfach mal einbauen und {{test foo bar}} in eine CMS-Seite schreiben.

Wenn wir jetzt z.B. in Produktbeschreibungen auch diese Filter nutzen möchten reicht es im entsprechenden Template folgendes zu platzieren:

// aus
echo $_product->getDescription();
// wird ein
echo Mage::helper(‘cms’)->getPageTemplateProcessor()->filter($_product->getDescription());

// bei Bedarf natürlich eleganter, aber das Prinzip dürfte klar sein

So können wir bei Bedarf auch Widgets in Produktbeschreibungen unterbringen oder vernünftige URLs oder oder oder .. der Fantasie sind keine grenzen Gesetz :)

Verschiedene Direktiven

Wir wissen jetzt wie die Filter im groben funktionieren, wie wir eigene Direktiven einbinden und diese auch woanders nutzen können.Doch was gibt es überhaupt out-of-the-box von Magento? Die wichtigsten will ich hier einmal erläutern:

varDirective

Die varDirective dient dem Zugriff auf Variablen. Diese können z.B. über setVariables(array $variables) dem Template-Filter übergeben werden. Für E-Mails gibt es als Beispiel die Mage_Sales_Model_Order::sendNewOrderEmail(...) Funktion:

$mailer->setTemplateParams(array(
    'order'        => $this,
    'billing'      => $this->getBillingAddress(),
    'payment_html' => $paymentBlockHtml
));

Im E-Mail-Template könnte man jetzt z.B: mit {{var order...}} auf das aktuelle Mage_Sales_Model_Order-Objekt zugreifen. Beispielsweise {{var order.getCustomer().getAddress().format(‘html’)}} oder alternativ {{var order.customer.address.format(‘html’)}}. So kommt man an alle wichtigen Informationen die man irgendwo einbauen möchte.

widgetDirective

wird genutzt um Widgets einzubinden. Die Benutzung lernt man am einfachsten über den Assistenten im WYSIWYG-Editor.

blockDirective

Schonmal den Wunsch gehabt einfach einen Block in einer CMS-Seite einzubinden? Hier ist die Lösung:
{{block type=”catalog/product_list” id=”cms_product_list” output=”toHtml” category_id=”3”}}
Die Parameter type, id und output werden für die Erstellung und das anzeigen des Blocks genutzt, alle andere Parameter werden mit setDataUsingMethod($paramter, $value) auf den Block gesetzt.

configDirective

Unter Umständen möchte man Werte aus der Konfiguration anzeigen, zum Beispiel die Store-Owner-Daten. {{config path=”path/to/xml/node”}} gibt dafür den entsprechenden Wert zurück.

htmlescapeDirective

Zum Schutz vor XSS kann man mit {{htmlescape var=”gefährlich

”}} Strings maskieren. Nützlich z.B. auch für E-Mail-Adressen u.ä.

mediaDirective / skinDirective

Im Prinzip dieselben Funktionen zum ermitteln der entsprechenden URLs für das skin und media Verzeichnis.
Als Parameter wird jeweils url genutzt: {{media url=”somepicture.png”}}

storeDirective

Eigentlich sollte storeDirective urlDirective heißen, denn genau das macht sie: eine URL zurückgeben:
{{store url=”somewhere”}} Wer sich immer schon bei Sachen wie {{store url=”somewhere.html”}} über das / am Ende geärgert hat sollte übrigens mal {{store direct_url=”somewhere.html”}} ausprobieren statt das (leider) oft genutzte {{store url=””}}somewhere.html.

Fazit

Ich persönlich finde die Filter ziemlich mächtig. Das einfache hinzufügen eigener Funktionen macht das ganze nur noch schöner. Auf jeden Fall lohnt sich auch nochmal ein Blick in die entsprechenden Klassen um selber ein Bild davon zu bekommen wie der Code aussieht. Trotzdem hoffe ich hiermit eine gute Einführung gegeben zu haben. In dem Sinne: schonmal einen schönen 2. Advent und bis zum nächsten Türchen :-)

P.S.: Noch ein Tipp aus Sicht der Sicherheit: Lasst niemals Daten von dritten durch den Template-Filter (z.B. durch Erweiterungen für Kundenkommentare, Schlagwörter, o.ä.). Der einfachste Weg so etwas auszunutzen wäre das Auslesen von Datenbankkonfiguration u.ä. per configDirective, ein etwas erfahrener Angreifer könnte u.U. auch eigenen Code ausführen!



Ein Beitrag von Bastian Ike
Bastian's avatar

'Bastian Ike arbeitet seit über 3 Jahren mit Magento und ist seit November 2013 bei AOE als Magento-Entwickler angestellt.

Alle Beiträge von Bastian

Kommentare
HaJo am

Ich habe einen Text in meiner sql-Datenbank abgelegt. Die bezeichnung ist "customer_entity_text". Wie kann ich diesen Text auf einer CMS - Seite in Magento anzeigen lassen ?

MfG HaJo

Magento App Factory Blog» Advanced Store URL Template Directive am

[...] template directives get parsed by a template filter. Bastian Ike provides a short insight in how to create your own template filter at the Webguys [...]

Türchen 10: Foreach-Schleifen mit Template-Direktiven « Das Entwickler-Blog für Magento, eCommerce und Shops – webguys.de am

[...] Ike hat im Türchen 03 bereits Magento Template Filter behandelt. Ich möchte mich für die Vorarbeit bedanken und nutze die Gelegenheit, auf die [...]

Tobias Vogt am

Ui so viel darf ich vertraten Vinai: Es kommt noch ein Post der auf dem Thema hier aufbaut :)

Vinai am

Super, vielen Dank für den Post! Bin begeistert eine gute Referenz zu haben auf die in Zukunft bei Bedarf verwiesen kann :) Vielleicht hast Du ja Lust noch was in einem neuen Post zu schreiben zu der Verwendung von {{if ...}} und {{depends ...}}? Dazu werden ja auch öfter Fragen gestellt... :)

Tobias Vogt am

Erst mal sind alle im Wochenende :)

Bastian Ike am

Danke :)

Wenn noch jemand anderes Feedback hat: immer her damit! :)

Tobias Vogt am

Ich finde den Beitrag übrigens super spannend - hatte mich bis dato nicht so intensiv mit der Problematik beschäftigt, danke!

Dein Kommentar