Türchen 10: Foreach-Schleifen mit Template-Direktiven

Bastian 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 Grundlagen nicht weiter einzugehen. Er beschreibt dort unter anderem, wie man die Filter für eigene Zwecke einsetzt.

Wäre es nicht schön, in Emails einfache foreach-Schleifen einsetzen zu können?

Die Template-Direktiven sind ein mächtiges Werkzeug, die man nicht nur für Emails und Inhaltsseiten nutzen kann. Ich habe sie genutzt, um einen möglichst flexiblen Export zu implementieren. Der Export sollte templatebasiert sein und die Möglichkeit bieten, Inhalt und Form beliebig anzupassen.

Und so könnte dann ein XML-Export-Template aussehen:

<?xml version="1.0"?>
<magento>
    <order>
        <entity_id><![CDATA[{{var order.entity_id}}]]></entity_id>
        <!-- ... -->
    </order>
    <shipping>
        {{depend shipping_method.tablerate_bestway}}
        <label><![CDATA[Tablerate]]></label>
        {{/depend}}
        {{depend shipping_method.freeshipping_freeshipping}}
        <label><![CDATA[Freeshipping]]></label>
        {{/depend}}
        <!-- ... -->
    </shipping>
    <!-- ... -->
</magento>

Einfache Variablen lassen sich mit der {{var …}}-Direktive ins Template einbringen, und auch bedingte Anweisungen kann man mit der {{depend ...}}- oder {{if ...}}-Direktive im Template verwenden. Aber möchte man z.B. bestellte Produkte ausgeben, muss die Ausgabe in einer Schleife erfolgen. Dies kann man nur indirekt und wenig elegant mit Magento-Standard-Direktiven lösen.

In einem solchen Fall würde man auf eine {{block …}}-Direktive zurückgreifen und die Ausführung der Schleife dem Block überlassen. Bei kleineren Iterationsausgaben, oder insbesondere im Falle des Exports, erschien mir das zu kompliziert - darum habe ich eine {{foreach …}}-Direktive für solche Aufgaben entwickelt.

Implementierung

Bei der Implementierung der {{foreach …}}-Direktive habe ich mich für die Elternklasse Mage_Core_Model_Email_Template_Filter entschieden, da diese im Magento-Core bis auf {{widget ..}} alle anderen Direktiven implementiert. Diese Klasse kann man mit einer neuen Klasse ableiten; um die Funktionalität auch in Email-Templates einsetzen zu können, ist zusätzlich ein Rewrite auf das Model core/email_template_filter notwendig.

Um die {{foreach …}}-Direktive in die Klasse zu integrieren, sind mehrere Schritte notwendig:
Die Methode der foreach-Direktive muss in der Klasse implementiert werden
zusätzlich muss auch die Methode „filter()“ überschreiben werden, da die {{foreach …}}-Direktive als erstes ausgeführt werden soll, und auch ein etwas anderes Konstruktionsmuster hat.

Die Implementierung der Klasse ist wie folgt:

class John_Doe_Model_Filter extends Mage_Core_Model_Email_Template_Filter
{
    const CONSTRUCTION_EACH_PATTERN = '/{{foreach\s(.*?)as\s(.*?)}}(.*?){{\\/foreach\s*}}/si';

    /**
     *
     * @param type $value
     * @return type
     */
    public function filter($value)
    {

        try {

            $pattern = self::CONSTRUCTION_EACH_PATTERN;
            $directive = 'foreachDirective';

            if (preg_match_all($pattern, $value, $constructions, PREG_SET_ORDER)) {
                foreach ($constructions as $index => $construction) {
                    $replacedValue = '';
                    $callback = array($this, $directive);
                    if (!is_callable($callback)) {
                        continue;
                    }
                    try {
                        $replacedValue = call_user_func($callback, $construction);
                    } catch (Exception $e) {
                        throw $e;
                    }
                    $value = str_replace($construction[0], $replacedValue, $value);
                }
            }

            $value = Mage_Core_Model_Email_Template_Filter::filter($value);
        } catch (Exception $e) {
            throw new Exception(Mage::helper('doe')->__('The filter can not be applied to the template'));
        }

        return $value;
    }

    /**
     *
     * @param type $construction
     * @return type
     */
    public function foreachDirective($construction)
    {
        if (count($this->_templateVars) == 0) {
            // If template preprocessing
            return $construction[0];
        }

        if ($this->_getVariable($construction[1], '') == '') {
            return '';
        } else {

            $value = '';
            foreach ($this->_getVariable($construction[1]) as $item) {
                $subFilter = new self;
                $subFilter->setVariables(array($construction[2] => $item));
                $value .= $subFilter->filter($construction[3]);
            }

            return $value;
        }
    }
}

Beispiel

Erweitern wir das Export-Template von oben um die Ausgabe der bestellten Produkte unter Zuhilfenahme der {{foreach …}}-Direktive, würde dies so aussehen:

<?xml version="1.0"?>
<magento>
    <order>
        <entity_id><![CDATA[{{var order.entity_id}}]]></entity_id>
        <!-- ... -->
    </order>
    <shipping>
        {{depend shipping_method.tablerate_bestway}}
        <label><![CDATA[Tablerate]]></label>
        {{/depend}}
        {{depend shipping_method.freeshipping_freeshipping}}
        <label><![CDATA[Freeshipping]]></label>
        {{/depend}}
        <!-- ... -->
    </shipping>
    <items>
        {{foreach items as item}}
        <item>
            <item_id><![CDATA[{{var item.item_id}}]]></item_id>
            <!-- ... -->
        </item>
        {{/foreach}}
    </items>
    <!-- ... -->
</order>

Entsprechend muss die Variable, über die die Iteration läuft, ein Array oder eine Collection sein.



Ein Beitrag von Andreas von Studnitz
Andreas's avatar

Andreas has been a Magento developer since 2008, also among the first Magento Certified Developers (2011) and Magento Certified Solution Specialists (2014). After working as a Magento freelancer, he founded integer_net in 2012 in Germany, joining forces with three partners. The company is specialized in high quality Magento development. Andreas focuses on module development, interface development, training and consulting. Additionally, he is an active member of both the German and international Magento community, regularly speaking at conferences and meetups.

Alle Beiträge von Andreas

Kommentare
Tobias Vogt am

Schon passiert :)

Andreas von Studnitz am

Ups...Sorry! Tobi, kannst du den Link zu Bastians Website korrigieren?

Bastian Ike am

Super, vielen Dank, das ist eine super Idee!

Auf jeden Fall eine wirklich hilfreiche Erweiterung, und eine weitere Möglichkeit die Template-Filter zu verbessern, die ich sowieso für eine gute Funktion halte.

P.S.: theboD.de, mit d nicht mit t ;-)

Dein Kommentar