Controller-Funktionen in Templates einbinden

in  Zikula Apps  , , ,

Controller-Funktionen in Templates einbinden

Im Gegensatz zu älteren Versionen empfangen Controller-Aktionen ab Zikula 1.3.x keine Argumente mehr. Bei der Umstellung bestehender Module kann man darüber stolpern, zum Beispiel wenn Controller-Funktionen mit dem modfunc-Plugin in Smarty-Templates eingebettet wurden.

Worum geht es?

Früher wurde in Zikula nicht unterschieden zwischen Controller-Aktionen und API-Methoden. Beides konnte beliebig aufgerufen und integriert werden, sowohl in PHP als auch in Smarty-Templates. Seitdem Symfony unter der Haube die Behandlung von Requests steuert, erfolgt hier eine striktere Trennung: während API-Methoden immer noch mit Argumenten aufgerufen werden, werden bei Controller-Aufrufen ausschließlich die Request-Parameter injiziert. Dies folgt der Konvention, dass ein Controller immer eine Response für einen gegebenen Request zurückliefern sollte.

Bei der Aktualisierung älterer Module wird dies relevant, wenn wie oben erwähnt Controller-Aktionen aus Templates heraus aufgerufen werden. Ein Beispiel:

{foreach item='person' from=$items}
    <div id="{$person.id}" style="page-break-after: always">
        {modfunc modname='MeinModul' type='user' func='display' ot='person' id=$person.id theme='Printer'}
    </div>
{/foreach}

Dies wird in dieser Form nicht mehr funktionieren, da die Parameter des modfunc-Aufrufs nicht beim Controller ankommen.

Möglichkeiten zum Umstieg

Es gibt in Symfony und Twig Mittel und Wege, um den oben gezeigten Code fast direkt zu übernehmen. Dies wird jedoch in Zikula erst mit Version 1.4.0 relevant. Was also in der Zwischenzeit tun?

Die folgenden Lösungswege bieten sich allgemein als Ersatz für den direkten Controller-Aufruf an:

Ein Template inkludieren
Eine einfache Variante besteht darin, gar keinen neuen Request zu starten. Im obigen Beispiel ist im Template bereits das Person-Objekt verfügbar, daher ist es naheliegend, mittels include ein untergeordnetes Template einzubinden, welches direkt mit diesen Daten arbeiten kann.
In andere Schicht verlagern
Auch eine normale Methode kann Templating nutzen. Es spricht nichts dagegen, die gewünschte Funktionalität aus dem Controller in eine Util-, Helper- oder API-Funktion auszulagern. Diese neu geschaffene Methode kann dann sowohl vom Controller als auch vom Template mit beliebigen Argumenten aufgerufen werden.

Den Request verändern

Falls diese Möglichkeit zu kurz gegriffen oder zu aufwändig sind, kann der Request vor Aufruf des Controllers verändert werden. Aus einem Smarty-Template heraus ist dazu entweder ein eigenes Plugin oder ein php-Block nötig. Wichtig ist, dass es nicht ausreicht, GET-Variablen zu verändern, denn das Request-Objekt von Symfony verwaltet seine eigene Kopie der Daten.

{foreach item='person' from=$items}
    {php}
        $person = $this->get_template_vars('person');
        $serviceManager = ServiceUtil::getManager();
        $request = $serviceManager->getService('request');
        $request->query->set('ot', 'person');
        $request->query->set('id', $person['id']);
        $request->query->set('theme', 'Printer');
    {/php}
    <div id="{$person.id}" style="page-break-after: always">
        {modfunc modname='MeinModul' type='user' func='display' ot='person' id=$person.id theme='Printer'}
    </div>
{/foreach}

Hinweis: dieser Hack ist keine saubere Lösung und sollte - wenn überhaupt - nur zu Übergangszwecken eingesetzt werden.

Trennung der Anbelange

Ein anderes mögliches Beispiel, welches kein Template zurückliefert, sondern die Daten anderweitig verarbeitet:

foreach ($persons as $persons) {
    foreach ($person['addresses'] as $address) {
        if (true !== $address['processed']) {
            $this->request->query->set('id', $person['id']);
            $this->request->query->set('firstName', $person['firstName']);
            $this->request->query->set('lastName', $person['lastName']);
            $this->request->query->set('city', $address['city']);
            $this->request->query->set('myVar', 123);
            ModUtil::func($this->name, 'user', 'processAddress');
        }
    }
}

Dieser Code schreit geradezu danach, dass die Funktion processAddress aus dem UserController in die (beispielsweise) UserApi zu verlagern. Dabei kann ein freies Array von Argumenten verwendet werden:

foreach ($persons as $persons) {
    foreach ($person['addresses'] as $address) {
        if (true === $address['processed']) {
            continue;
        }
        $args = [
            'id' => $person['id'],
            'firstName' => $person['firstName'],
            'lastName' => $person['lastName'],
            'city' => $address['city'],
            'myVar' => 123
        ];
        ModUtil::apiFunc($this->name, 'user', 'processAddress', $args);
    }
}

Oder man nutzt direkt eine eigene Klasse mit Methoden, welche - etwas strikter - vordefinierte Argumente voraussetzen:

$adressProcessor = new MyAddressProcessor();
foreach ($persons as $persons) {
    foreach ($person['addresses'] as $address) {
        if (true === $address['processed']) {
            continue;
        }
        $adressProcessor->processAddress($person['id'], $address, 123);
    }
}

Indem die Applikationslogik in eigene Klassen ausgelagert wird, entstehen automatisch leichtgewichtigere Controller, die sich auf ihren eigentlichen Aufgabenbereich konzentrieren können.

Der nächste Refaktorisierungsschritt im Beispiel wäre somit, die Auswahllogik der zu verarbeitenden Adressen ebenfalls in eine Klassenmethode zu kapseln, um den kompletten Prozess vom umgebenden Code zu lösen und damit sowohl Wiederverwendbarkeit als auch Testbarkeit zu verbessern.

$adressManager = new MyAddressManager();
$adressManager->processPersonAddresses($persons, 123);

Weitere Beiträge in Kategorie Zikula Apps

Zikula Core 3 bietet Mittel für dynamische Formularelemente
- Das Profile-Modul von Zikula bietet seit langer Zeit die Möglichkeit, die einzelnen Angaben der Benutzerprofile flexibel zu verändern und nach Belieben zu konfigurieren. Diese Funktionalität wurde im …
Der Fokus von Zikula Core 3
- Mit der Verfügbarkeit einiger zentraler Module für Zikula 2 ist eine der größten Hürden beseitigt, die den Umstieg für viele Nutzer in der Vergangenheit erschwert oder gar blockiert hatte. Die letzten …
Content Management mit Zikula 2 in der Praxis ausprobiert
- Nachdem kürzlich einige Module erschienen sind, um Inhalte mit Zikula 2 zu verwalten, haben wir nun zwei mittelgroße Projekte damit aufgebaut. Auch wenn im Detail noch nicht alles 100%ig am Schnürchen …
Neue Module braucht das Land
- Hier gibt es einige frisch geschnürte Module für Zikula 2.0.11: Content 5.0.1 Die Basis für anspruchsvolles Content Management. Formicula 5.0.0 Formulare zur Kontaktaufnahme, für Anmeldungen, …
Atom-Editor zur Symfony- und Zikula-Entwicklung einrichten
- Rund um den ursprünglich von GitHub ins Leben gerufene Editor Atom hat sich mittlerweile ein ansehnliches Ökosystem entwickelt. So existieren aktuell bereits knapp 8.000 Erweiterungen für alle …