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:

1
2
3
4
5
{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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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:

1
2
3
4
5
6
7
8
9
$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.

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

Weitere Beiträge in Kategorie Zikula Apps

Zikula Benutzer und Gruppen in DokuWiki verwenden
- In Zikula lassen sich mit Hilfe unterschiedlicher Authentifizierungsmethoden Nutzer auf verschiedenen Quellen einbinden und mischen. So kann man sich beispielsweise mit dem OAuth-Modul via Facebook, …
Ein Blick auf die Entwicklungen des News-Moduls
- Das News-Modul von Zikula blickt auf eine lange Historie zurück. Schon als ich vor etwa 20 Jahren das erste mal mit PostNuke in Berührung kam, war dort ein News-Modul an Bord. War die Funktionalität …
Zikula Aktualisierung 2.0.13 mit Sicherheitspatches von Symfony
- Der Zikula Core ist soeben in der Version 2.0.13 erschienen, da eine Reihe von sicherheitsbezogenen Änderungen in Symfony eingeflossen sind. Hier der Link zu den einzelnen Änderungen: Changelog für …
Unterschiedliche Startseiten je Domain oder Einstellung einbinden
- Eine häufige Anforderung besteht darin, die Startseite eines Projektes individuell anzupassen. Zikula bietet zwar die Möglichkeit, eine Controller-Aktion sowie die zu übergebenden Argumente in der …
Spannende Neuerungen im Zikula Core
- In Zikula 3 werden endlich weitere hilfreiche Funktionen von Symfony verwendet. Zikula 2.x hat bereits auf Symfony 3.4.x aufgesetzt, aber aus Rücksicht auf die Abwärtskompatibilität noch nicht alle …