(Document-)Controller

1. Zweck eines (Document-)Controllers

Wie in den Kapitel Hallo Welt! und Templates angesprochen, werden Document-Controller zur Generierung von dynamischen Inhalten verwendet. Der Begriff Controller bezieht sich auch im Adventure PHP Framework auf das MVC-Pattern, allerdings geht das APF mit dem Page-Controller noch einen Schritt weiter und setzt bei der Erstellung von Oberflächen auf das HMVC-Pattern.

Das HMVC-Pattern bietet gegenüber dem MVC-Pattern den Vorteil, dass Elemente einer Webseite oder Anwendung granularer definiert werden können und sich diese somit flexibler innnerhalb der selben Anwendung oder auch weiteren Projekten wiederverwenden lassen. Aus Sicht des Software-Designs können Abhängigkeiten zwischen Komponenten - die bei Nutzung des MVC-Pattern üblicherweise umfassende Logik der Gesamt-Anwendung im Controller bedeuten - deutlich reduzieren.

Damit ein Controller seine Aufgabe erledigen kann muss dieser im gewünschten Templates referenziert werden. Dies geschieht durch ein

APF-Template
<@controller class="..." @>

zu Beginn des Templates. Das class-Attribut beinhaltet dabei den voll-qualifizierten Klassen-Namen der Controller-Implementierung (z.B. VENDOR\pres\controller\NavigationController). Bei der Transformation nutzt der Page-Controller diese Deklaration und führt die transformContent()-Methode der Klasse aus.

Um den Code in Controller-Klassen übersichtlich zu gestalten, wird empfohlen die Generierung der Darstellung bzw. die View-Logik stark zu begrenzen. Ist weitere Logik zur Darstellung nötig kann es sinnvoll sein, einen eigenen Tag zu erstellen, der die Darstellung von komplexen Daten übernimmt.

Als Daumenregel gilt, dass Controller so lange als Mittel der Wahl eingesetzt werden sollten, solange die View-Logik und die Komplexität der Darstellung begrenzt ist. Sobald der Controller in die Erstellung oder Manipulation des DOM-Baums eingreifen muss oder eine Datenstruktur mehr als eine Ebene besitzt, so sollten eigene Tags implementiert werden.

Darüber hinaus sollte insbesondere im Hinblick auf die Anwendung des HMVC-Pattern nur eine Aufgabe innerhalb des Controller-Code übernommen werden. Sind darin mehrere Funktionen enthalten empfiehlt sich die Aufspaltung in weitere Komponenten.

2. Aufbau eines (Document-)Controllers

Ein Document-Controller ist durch das Interface DocumentController definiert und erbt üblicherweise von BaseDocumentController, der bereits eine Reihe von Basis-Funktionalitäten bereit stellt.

Das Grundgerüst eines Document-Controllers hat daher folgende Gestalt:

PHP-Code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class NavigationController extends BaseDocumentController { public function transformContent(){ ... } }

Die Methode transformContent() wird vom Page-Controller bei der Transformation der Seite genutzt um die Funktionalität des aktuellen DOM-Elements auszuführen. Die restliche Gestaltung der Klasse ist dem Entwickler überlassen.

Wie auch der API-Dokumentation zu entnehmen ist verfügt der BaseDocumentController über Methoden, die Sie bei der Implementierung von dynamischen Funktionalitäten unterstützten. Diese sind:

  • getDocument(): Liefert eine Referenz auf den aktuellen DOM-Knoten zurück. Dies ermöglicht den Zugriff auf den kompletten Baum über die Methoden getChildren() und getParentObject().
  • getTemplate($name): Liefert eine Referenz auf ein Template-Objekt (Instanz von TemplateTag) das im aktuellen Dokument definiert ist.
  • getForm($name): Liefert eine Referenz auf ein Formular-Objekt (Instanz von HtmlFormTag) das im aktuellen Dokument definiert ist.
  • getLabel($name): Liefert eine Referenz auf ein Label-Objekt (Instanz von LanguageLabelTag) das im aktuellen Dokument definiert ist.
  • getIterator(): Liefert eine Referenz auf ein Iterator-Objekt (Instanz von HtmlIteratorTag) das im aktuellen Dokument definiert ist.
  • placeHolderExists($name): Prüft, ob im aktuellen Dokument ein Platzhalter-Tag mit dem übergebenen Namen existiert.
  • templatePlaceHolderExists(TemplateTag &$template, $name) Prüft, ob im aktuellen Dokument in einem Template ein Platzhalter-Tag mit dem übergebenen Namen existiert.
  • setPlaceHolder($key, $value): Füllt einen Platzhalter mit dem übergebenen Wert.
  • setPlaceHolders(array $placeHolderValues): Füllt eine Liste von Platzhaltern mit den übergebenen Werten.
  • setPlaceHolderIfExist($name, $value): Füllt einen Platzhalter mit dem gewünschten Wert, falls dieser existiert.
  • setPlaceHoldersIfExist(array $placeHolderValues): Füllt eine Liste von Platzhaltern, falls diese existieren.

Des Weiteren verfügt der BaseDocumentController über alle Methoden der Klasse APFObject.

Über die bisher genannten Methoden kann jeder Document-Controller auf das Dokument im APF-DOM-Baum zugreifen, für dessen Transformation er zuständig ist. Das aktuelle Dokument kann mit Hilfe der Methode $this->getDocument() bezogen werden. Anschließend ist es beispielsweise möglich, per
PHP-Code
$this->getDocument()->getAttribute('foo')
auf den Wert des Attributs foo zuzugriffen.

3. Beispiele für (Document-)Controller

3.1. Darstellung dynamischer Inhalte in Meta-Tags

Ein Anwendungsfall eines Document-Controllers ist das Füllen von Meta-Informationen im HTML-Header. Als Beispiel sollen Titel und das aktuelle Datum einer Webseite dynamisch gefüllt werden. Hierzu definieren Sie zunächst die HTML-Struktur und die gewünschten Platzhalter im Template:

APF-Template
<@controller class="VENDOR\pres\controller\MainController" @> <html> <head> <title><html:placeholder name="title" /></title> <meta name="date" content="<html:placeholder name="date" />" /> </head> <body> ... </body> </html>

Der im Template referenzierte Document-Controller implementiert die Methode transformContent() und eine Methode getTitle() um den Titel der Webseite zu evaluieren. Der Quellcode der Klasse gestaltet sich wie folgt:

PHP-Code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class MainController extends BaseDocumentController { public function transformContent(){ $this->setPlaceHolder('title', $this->getTitle()); $this->setPlaceHolder('date', date('Y-m-d')); } private function getTitle() { return ... } }

3.2. Darstellung dynamischer Listen

Mit Hilfe des <html:template />-Tags lassen sich Listen in einem Controller durch Iteration über die vorhandenen Inhalte erzeugen.

<html:template />-Tags eignen sich lediglich für die Darstellung von Inhalten, die nur eine Hierarchie-Stufe abbilden (z.B. Liste von Produkten inkl. Preis). Ist ein <html:template /> nicht mehr ausreichend und die Darstellung verlangt eine Schachtelung von Ausgaben verschiedener Templates, so wird die Implementierung eines eigenen Tags empfohlen, der die Darstellung eines kompletten Elements oder auch der kompletten Liste übernimmt. Weitere Hinweise finden Sie im Tutorial Implementierung von Tags.

Das folgende Template wurde definiert um eine Liste von Produkten mit ihrem zugehörigen Preis in einer Tabelle auszugeben:

APF-Template
<@controller class="VENDOR\pres\controller\ListController" @> <table cellpadding="0" cellspacing="0" border="0"> <thead> <tr> <td>Produkt</td> <td>Preis</td> </tr> </thead> <tbody> <html:placeholder name="products" /> </tbody> </table> <html:template name="product-item"> <tr> <td><template:placeholder name="display-name" /></td> <td><template:placeholder name="price" /></td> </tr> </html:template>

Der zugehörige Document-Controller gestaltet sich wie folgt:

PHP-Code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class ProductListController extends BaseDocumentController { public function transformContent() { $tmpl = $this->getTemplate('product-item'); $buffer = ''; foreach ($this->getProducts() as $product) { $tmpl->setPlaceHolder('display-name', $product->getDisplayName()); $tmpl->setPlaceHolder('price', $product->getPrice()); $buffer .= $tmpl->transformTemplate(); } $this->setPlaceHolder('products', $buffer); } /** * @return Product[] */ private function getProducts() { } }

Innerhalb der transformContent()-Methode wird mit Hilfe von getProducts() eine Liste von Produkten geladen. In einer Schleife werden dann die Inhalte der Produkte in das Template eingesetzt und die Ausgabe in einem Puffer gesammelt. Das Ergebnis wird schließlich in den Platzhalter products geschrieben um die Ausgabe im Template zu erzeugen.

3.3. Weiterführende Inhalte

Weitere Anwendungsfälle von Controllern finden Sie unter in den Bereichen Tutorials und Artikel.

4. Erzeugung eines Controllers über den DI-Container

Für komplexere Anwendungsfälle lassen sich Document-Controller über den DIServiceManager erzeugen. Dies bietet gegenüber der klassischen Verwendung folgende Vorteile:

  • Abhängige Objekte (z.B. Business-Services, Domänen-Objekte) lassen sich bequem konfigurieren und dem Controller übergeben. Dies vermeidet redundanten Code und erhöht die Testbarkeit des Controllers da die Controller-Implementierung keine explizite Abhängigkeiten definiert.
  • Die Möglichkeit der statischen und dynamischen Konfiguration eines APFDIService lassen sich auch zur Konfiguration eines Document-Controllers nutzen. So lässt sich das Verhalten eines Controller beispielsweise durch einen einfachen Konfigurationsparameter verändern um diesen in unterschiedlichen Applikation einzusetzen.

Um einen Controller mit dem DIServiceManager zu erzeugen ist eine angepasste Controller-Deklaration notwendig. Die Implementierung wird dabei nicht mehr direkt über das class-Attribut angegeben, sondern durch einen Verweis auf den zu nutzenden Service:

APF-Template
<@controller namespace="APF\sites\controller" service="ContactController" @>

Das Attribut namespace referenziert den Namespace der Service-Definition, service zeigt auf die Service-Definition, die den Document-Controller beschreibt. Details zur Adressierung von Services entnehmen Sie bitte dem Kapitel Services.

Zur Verwendung der Service-Deklaration erstellen Sie bitte eine Service-Konfiguration. Details zum Aufbau von Service-Konfigurationen entnehmen Sie bitte dem Kapitel Konfiguration.

Die folgende Code-Box zeigt Ihnen eine Beispiel-Konfiguration, die den Controller mit einem statischen Konfigurations-Parameter (E-Mail) ausstattet:

APF-Konfiguration
[ContactController] class = "APF\sites\documentcontroller\ContactController" servicetype = "NORMAL" conf.email.method = "setEMail" conf.email.value = "me@example.com"

Weitere Informationen zur Nutzung des Dependency Injection Containers finden Sie im Kapitel Anwendung.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.