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:

 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 Core 3.0.3 mit Sicherheits-Update von Symfony 5
- Vor kurzem hatten wir über das Release von Zikula 3.0.1 berichtet, welches wichtige Korrekturen für Zikula 3 bereitgestellt hat. Vor einer Woche wurde die nächste Version 3.0.2 veröffentlicht. …
Symfony Services asynchron im Hintergrund aufrufen
- Oftmals greift man in Symfony-Projekten auf CLI-Kommandos mit Hilfe der Symfony Console-Komponente zurück, um länger laufende Prozesse außerhalb des Webservers durchzuführen. In komplexeren Vorhaben …
Zikula 3.0.1 veröffentlicht - erstes Bugfix Release für Zikula Core 3
- Genau einen Monat nach der Veröffentlichung von Zikula 3.0.0 ist nun das erste Update erschienen. Zikula 3.0.1 bringt wichtige Korrekturen und sorgt somit für ein Stück mehr Stabilität in der neuen …
Zikula Framework in Version 3 veröffentlicht
- Heute haben wir das finale Release von Zikula Core 3.0.0 veröffentlicht. Es basiert auf Symfony 5.1 sowie Twig 3 und verwendet unter anderem Bootstrap 4 und Font Awesome 5. Einen grundlegenden …
Zikula 3 erscheint als Release Candidate
- Heute wurde der erste Release Candidate für Zikula Core 3.0.0 veröffentlicht. Einen grundlegenden Überblick über die wichtigsten Änderungen sowie die weiterführenden Links zur Dokumentation und zu den …