Frontcontroller
1. Einleitung
Ein weiterer Bestandteil dieses Frameworks ist - wie bereits erwähnt - eine Implementierung des
Frontcontrollers. Basis für die hier zur Verfügung stehende Implementierung ist die
Pattern-Definition von
Martin Fowler
und die Auslegung des Patterns im JAVA-Framework
Struts.
Einführend Beispiele können auf der
PHP-Patterns-
Webseite eingesehen werden.
Die FrontController-Komponente des Applikations-Frameworks besteht aus einer Singleton-Instanz der
Klasse FrontController, die den Benutzer-Requests auswertet und die enthaltenen
Anweisungen ausführt. Es gibt mehrere Arten von Actions, die gemäß den später
beschriebenen Timing Model ausgeführt werden. Im Allgemeinen werden Actions, die das
Schlüsselwort "pre" enthalten, vor einer definierten Aktion ausgeführt und Actions mit
"post" in der Typenbezeichnung nach einem Code-Teil. Aktionen des Typs "prepagecreate" dienen meist
zum Aufbau des Models einer Applikation, Actions des Typs "posttransform" z.B. für
Logging-Aufgaben oder Ähnliches. Um eigene Actions zu implementieren bietet das Framework zwei
abstrakte Basisklassen für Actions (AbstractFrontcontrollerAction) und ihre
Informationen (FrontcontrollerInput). Der frontcontrollerRequestFilter
und der frontcontrollerRewriteRequestFilter sind Komponenten, die die Action-Anweisungen
aus der URL extrahieren, so dass der FrontController diese ausführen kann. Mit diesen beiden
Komponenten kommt der Entwickler aber in der Regel nicht in Berührung, da diese vom FrontController
selbstständig ausgeführt werden. Um Actions zu haben, die bei jedem Request ausgeführt
werden, hat der Entwickler die Möglichkeit diese Actions in der index.php zu registrieren.
Diese verhalten sich gemäß dem Timing Model wie normale Actions. Das folgende UML-Diagramm
zeigt die Komponenten des FrontControllers in der Übersicht:
2. Implementierung
Die Implementierung im FrontController-Style beinhaltet im Wesentlichen zwei Bereiche. Zum einen muss
eine Action- und Input-Klasse erzeugt werden, die jeweils von AbstractFrontcontrollerAction
und FrontcontrollerInput ableiten, zum anderen muss eine Konfiguration angelegt werden,
die eine Action beschreibt.
2.1. Action- und Input-Klassen
Die vom Entwickler erzeugte Action-Klasse kapselt die Funktion einer Action. Wie der
API-Dokumentation der Klasse
AbstractFrontcontrollerAction zu entnehmen ist, muss hierzu die Methode run()
implementiert werden, da diese bei der Ausführung einer Action durch den FrontController
angesprochen wird. Die Input-Klasse ist eine einfache Daten-Klasse die die Model-Informationen der
jeweiligen Action beinhaltet. Ein Input-Objekt kann im einfachsten Anwendungsfall auch das Model einer
Applikation sein. Das folgende Code-Beispiel zeigt zwei einfache Action- und Input-Klassen:
class DemoAction extends AbstractFrontcontrollerAction
{
function DemoAction(){
}
function run(){
echo 'I am front controller action class! My Name is '.$this->__Input->getAttribute('Name').'!';
// end function
}
// end class
}
class DemoInput extends FrontcontrollerInput
{
function DemoInput(){
$this->__Attributes['Name'] = 'Max Mueller';
// end function
}
// end class
}
Wird die hier gezeigte Action ausgeführt, erscheint im Browser der Satz
I am front controller action class! My Name is Max Mueller!
Da die Actions ebenso den Context der Applikation kennen, können in den Actions beliebige
Applikations-Teile verpackt werden. Ein beliebtes Beispiel ist die Benutzer-Authentifizierung. In der
Action kann dann abgeprüft werden, ob der Benutzer eingeloggt ist oder nicht und ggf. die Model-
Informationen des Login-Moduls setzen.
2.2. Konfiguration
Jede Action muss in einer Konfigurations-Datei definiert sein. Zur Definition gehören der
Namespace, die Dateinamen für Action- und Input-Klassen, deren Namen selbst und evtl. benötigte
Model-Informationen. Konfigurations-Dateien werden immer unter dem Verzeichnis
{ActionNamespace}::actions::{Context}
und mit dem Namen
{ENVIRONMENT}_actionconfig.ini
abgelegt. {ENVIRONMENT} ist dabei gegen den Wert der Umgebungsvariable zu ersetzen.
Im Standard-Fall ist dies der Wert DEFAULT. Eine Action-Konfiguration beinhalten
dabei ein oder mehrere Definitionen von Actions, die wie folgt aussehen:
[{ActionName}]
FC.ActionNamespace = ""
FC.ActionFile = ""
FC.ActionClass = ""
FC.InputFile = ""
FC.InputClass = ""
FC.InputParams = ""
Die aufgeführten Parametern haben dabei folgende Bedeutungen:
-
ActionName:
Name der Action. Dieser Name wird in der URL als Action-Name verlangt (Beispiel: setModel).
-
FC.ActionNamespace:
Namespace der Konfigurationsdatei der Action (Beipsiel: sites::demosite::biz::actions).
-
FC.ActionFile:
Name of der Datei, in der die Action-Klasse implementiert ist (Beispiel: LoadModelAction).
-
FC.ActionClass:
Name der Action-Klasse (Beispiel: LoadModelAction).
-
FC.InputFile:
Name der Datei, in dem die Input-Klasse residiert (Beispiel: DemositeModel).
-
FC.InputClass:
Name der Input-Klasse (Beispiel: DemositeModel).
-
FC.InputParams:
Konfigurations-Direktive, mit der Model-Daten per Konfiguration gesetzt werden können (Beispiel:
login:true|headview:menu. Schlüssel und Wert sind per ":" getrennt, unterschiedliche
Wertepaare per "|").
Da FrontController-Actions in der Regel Teile der Business-Schicht sind, sollten diese unter dem
biz-Ordner einer Seite, Applikation oder eines Moduls abgelegt werden. Sinnvollerweise
legt der Entwickler im Ordner biz noch einen Unterordner actions an,
um die Übersichtlichkeit zu verbessern. Da Namespace, Namen der Dateien und Klassen frei wählbar
sind, ist dem Entwickler hinsichtlich der Benennung und Strukturierung freie Hand gelassen.
2.3. Änderungen in der index.php
Um eine Applikation im FrontController-Style laufen zu lassen, sollte in der index.php
folgendes eingetragen sein:
// Instanz des Frontcontroller holen/erzeugen
$fC = &Singleton::getInstance('Frontcontroller');
// Context der Applikation setzen
$fC->set('Context','sites::demosite');
// Sprache der Applikation setzen
$fC->set('Language','de');
// Seite generieren
$fC->start('sites::demosite::pres::templates','website');
Ist es erwünscht, "permanente" Actions auszuführen, so ist ein
// Standard-Action "Login" mitteilen
$fC->registerAction('sites::demosite::biz','Login');
vor dem Aufruf der Methode start() notwendig. Anschließend ist es jederzeit
möglich Actions von Modulen oder Applikationen ausführen zu lassen.
2.4. Generierung von Links
Um Links in FrontController-basierten Applikationen ebenso einfach generieren zu können wie bei
PageController-Anwendungen wurde die Komponente frontcontrollerLinkHandler
eingeführt. Diese manipuliert einen Link auf Basis einer bestehenden URL und bettet alle
in der URL erwünschten Action-Definitionen aus dem aktuellen Model (Input-Objekte der Actions)
ein, falls das private Attribut $__KeepInURL einer Action auf true gesetzt
wurde. So können auch mehrfach abhängige Actions in der URL vorhanden sein. Das folgende
Beispiel zeigt, wie der frontcontrollerLinkHandler angewendet wird. Der Einfachheit
halber werden Links nur im Rewrite-Modus betrachtet. Die Manipulation ohne URL-Rewriting funktioniert
analog dazu.
2.4.1. Einfache Manipulation von Parametern
In vielen Applikationen ist es notwenig Links zu generieren. In Frontcontroller-basierten Anwendungen,
in denen keine Action-Anweisungen in URLs manupuliert werden müssen, kann der
frontcontrollerLinkHandler wie sein "kleiner Bruder" linkHandler
eingesetzt werden um URLs zu erzeugen. Aus einem Link
http://www.adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2
kann auf einfache Weise
http://www.adventure-php-framework.org/Seite/Guestbook/benchmarkreport/true
generiert werden. Hierzu ist lediglich ein
// URL definieren
$URL = 'http://www.adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2';
// URL-Aenderungen definieren
$ChangeParams = array(
'Seite' => 'Guestbook',
'param1' => '',
'param2' => ''
);
echo frontcontrollerLinkHandler::generateLink($URL,$ChangeParams);
notwenig.
2.4.2. Manipulation von Parametern und Actions
Actions werden gleichermaßen als URL-Parameter behandelt wie "normale" URL-Bestandteile. Daher
gestaltet es sich sehr einfach, aus einer URL
http://www.adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2
den Link
http://www.adventure-php-framework.org/Seite/Guestbook/benchmarkreport/true/param1/value1/param2/value2/~/
modules_guestbook_biz-action/LoadEntryList/pagesize/20/pager/false/adminview/true
zu generieren. Hier wurde lediglich durch ein geändertes Parameter-Array eine Action hinzugefügt:
// URL definieren
$URL = 'http://www.adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2';
// URL-Aenderungen definieren
$ChangeParams = array(
'modules_guestbook_biz-action:LoadEntryList' => 'pagesize:20|pager:false|adminview:true',
'Seite' => 'Guestbook'
);
// Link generieren
echo frontcontrollerLinkHandler::generateLink($URL,$ChangeParams);
Wurde die in der URL darzustellende Action bereits als "permanente" Action in der index.php
eingehängt ist es lediglich notwendig, den frontcontrollerLinkHandler aufzurufen
und die gewünschten "normalen" Parameter zu manipulieren. Dabei werden die vorhandenen Actions
automatisch in die URL eingefügt.
2.4.3. Manipulation von Parametern und Actions mit generateURLParams()
Das Beispiel in Kapitel 1.4.2. hat den Nachteil, dass sich der Entwickler Gedanken über die
Semantik einer FrontController-URL machen muss. Um URL-Generierung noch komfortabler zu gestalten,
wurde dem frontcontrollerLinkHandler die Methode generateURLParams
hinzugefügt. Diese ermöglicht, aus einem Satz von Parametern eine Action-Anweisung in Form
eines Arrays zu generieren, das der Methode generateLink übergeben werden kann.
Die URL-Generierung kann dann wie folgt erfolgen:
// URL definieren
$URL = 'http://www.adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2';
// URL-Aenderungen definieren
$ChangeParams = array(
'Seite' => 'Guestbook'
);
// Parameter der FrontController-Action erzeugen
$ChangeParams = array_merge(
$ChangeParams,
frontcontrollerLinkHandler::generateURLParams(
'modules::guestbook::biz',
'LoadEntryList',
array(
'pagesize' => '20',
'pager' => 'false',
'adminview' => 'true'
)
)
);
// Link generieren
echo frontcontrollerLinkHandler::generateLink($URL,$ChangeParams);
Aus Performancegründen sollte diese Methode allerdings nicht übermäßig
Verwendung finden, da diese bereits ~0.004 s zur Generierung des Parameter-Arrays benötigt. Ist
klar, in welchem Betriebsmodus die Anwendung später laufen wird (Rewrite-URLs vs. normale URLs),
sollte auf ein manuelles Generieren der URLs, wie in Kapitel 1.4.2. aufgezeigt, zurückgegriffen
werden.
2.5. Timing-Modell
Der FrontController besitzt ein eigenes vierstufiges Timing-Modell, mit dem vom Entwickler beeinflusst
werden kann, wann eine Action ausgeführt wird. Hierzu existiert in der Interface-Definition der
Klasse AbstractFrontcontrollerAction das Attribut $__Type. Dieses
ist standardmäßig mit dem Wert prepagecreate gefüllt. Soll die Action
zu anderen Zeiten zum Einsatz kommen, so stehen folgende Werte für diesen Parameter zur Verfügung:
-
prepagecreate: Action wird vor dem Erzeugen der PageController-Seite ausgeführt.
-
postpagecreate: Action wird nach dem Erzeugen der PageController-Seite ausgeführt.
-
pretransform: Action wird vor der Transformation der PageController-Seite ausgeführt.
-
posttransform: Action wird nach der Transformation der PageController-Seite ausgeführt.
Das Timing kann nur zur Entwicklungszeit per
class MyAction extends AbstractFrontcontrollerAction
{
// Timing festlegen
var $__Type = 'pretransform';
function MyAction(){
}
function run(){
}
// end class
}
manipuliert werden. Implementierungsdetails zur Methode run() finden sich in der
API-Dokumentation der Klasse
Frontcontroller.
2.6. Model-basiertes View-Konzept
Da es mit dem Frontcontroller möglich ist, die Business-Schicht, bzw. das Model der Anwendung
vor der Präsentationsschicht zu erzeugen, kann, im Gegensatz zu einer
PageController-Implementierung, die Business-Schicht dazu verwendet werden, die
Präsentationsschicht zu steuern. Der hier wichtigste Bereich ist die Steuerung von Views, bzw.
der Inhalte der Views.
Um den Vorteil einer Frontcontroller-basierten Anwendung auch im Bereich des GUI-Designs voll
ausschöpfen zu können wurde den HTML-Tools die TagLib fcon_taglib_importdesign
hinzugefügt. Diese Komponente bindet, wie oben angedeutet, Views ein die im Model der Anwendung
definiert werden. Üblicherweise wird für das Model ein eigenes Applikationsmodel definiert,
das aus Frontcontroller-Actions bedient und gefüllt wird. Hierzu wird in der Regel eine eigene
Klasse der Form
class DemoSiteModel extends coreObject
{
function DemoSiteModel(){
$this->__Attributes['view.content.template'] = 'login';
$this->__Attributes['view.topmenu.template'] = 'empty';
// end function
}
// end class
}
implementiert, die an den relevanten Stellen mit einem
$Model =&$this->__getServiceObject('sites::demosite::biz','DemoSiteModel');
verwendet oder befüllt werden kann.
In einer Template-Datei kann dann mit einem
<fcon:importdesign
templatenamespace="sites::apfdocupage::pres::templates"
modelnamespace="sites::demosite::biz"
modelfile="DemoSiteModel"
modelclass="DemoSiteModel"
modelparam="view.content.template"
/>
ein View in Abhängigkeit der Inhalte des Models eingebunden werden. Um die TagLib verwenden zu
können muss diese jedoch dem aktuellen Document zunächt per
<core:addtaglib namespace="tools::html::taglib" prefix="fcon" class="importdesign" />
bekannt gemacht werden.
Mit diesem Konstrukt lässt sich die Einbindung von Views bequem über das Model einer
Applikation steuern und so die Verantwortung wird komplett an die Business-Schicht übergeben.
Des Weiteren erfährt die Implementierung an Flexibilität gegenüber der Verwendung des
PageControllers als alleinige Steuerungskomponente für das Aussehen einer GUI.
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.