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!