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

Kommende Neuerungen in Symfony 6.4
- Die Veröffentlichung der nächsten Symfony-Version 6.4 steht bevor. Wie immer gibt es regelmäßige Einblicke in die wichtigsten, zu erwartenden Features und Verbesserungen. Dieser Beitrag zeigt im …
Symfony vereinfacht zeitgesteuerte Aktionen mit neuer Scheduler-Komponente
- Mit Symfony 6.3 sind wieder allerhand neue Features hinzugekommen. In diesem Beitrag möchte ich kurz die neue Komponente Scheduler vorstellen, mit welcher der Symfony Messenger um zeitgesteuerte …
SSO mit Keycloak und Symfony
- Mit der Java-basierten Software Keycloak wird die Verwaltung und Authentifizierung von Nutzern zentral gelöst. Hierbei bleiben kaum Wünsche offen: egal ob nun ein Registrierungsprozess, die …
Symfony UX Translator bietet Mehrsprachigkeit in JavaScript
- Bereits seit einigen Jahren gibt es das BazingaJsTranslationBundle, welches es erlaubt, den Symfony Translator auch Client-seitig zu verwenden. Auch in Zikula kommt dieses Bundle zum Einsatz, …
Kommende Neuerungen in Symfony 6.3
- Die Veröffentlichung der nächsten Symfony-Version 6.3 ist für Ende Mai 2023 geplant. Wie immer gibt es regelmäßige Einblicke in die wichtigsten, zu erwartenden Features und Verbesserungen. Dieser …