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 Dokumentation in neuem Gewand
- Schon seit einiger Zeit werden im GitHub-Repository des Zikula Core die Dinge dokumentiert, die zusätzlich zu den Handbüchern von Symfony, Doctrine, Twig, Bootstrap usw. zu beachten sind. Nun wurde …
Weitere neue Features in Zikula Core 3
- Nachdem wir bereits in einigen Artikeln zu Zikula 3 die Änderungen unter der Haube, umfangreiche Modernisierungen, Möglichkeiten für dynamische Formulare und Neuerungen rund um Übersetzungen …
Zikula 3 bringt zahlreiche Neuerungen rund um Übersetzungen
- Nachdem zwischen den Jahren der komplette Unterbau von Zikula 3 auf Stand gebracht worden ist, hat sich der nächste Sprint dem Übersetzungssystem gewidmet. In diesem Beitrag wird kurz dargestellt, …
Zikula startet modernisiert ins neue Jahr
- Im Rahmen des letzten Sprints wurde der Unterbau von Zikula 3 auf den neuesten technischen Stand gebracht. Im Folgenden wird kurz zusammengefasst, was hierbei genau passiert ist. Twig 3 Als erstes …
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, …