Frontcontroller
Der Front-Controller ist neben dem Pagecontroller ein weiterer wichtiger Basis-Bestandteil
des Frameworks. 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ührende 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-Request auswertet und die enthaltenen
Anweisungen ausführt. Es gibt mehrere Arten von Actions, die gemäß dem in der
folgenden Grafik 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 FrontControllerInputFilter
ist eine Komponente, die die Action-Anweisungen aus der URL extrahiert, so dass der FrontController
diese ausführen kann. Mit dieser Komponente kommt der Entwickler aber in der Regel nicht in
Berührung, da diese vom FrontController selbstständig ausgeführt werden. Um Actions
zu definieren, 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:
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.
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:
PHP-Code
class DemoAction extends AbstractFrontcontrollerAction {
function DemoAction(){
}
function run(){
echo 'I am front controller action class! My Name is '.
$this->__Input->getAttribute('Name').'!';
}
}
class DemoInput extends FrontcontrollerInput {
function DemoInput(){
$this->__Attributes['Name'] = 'Max Mueller';
}
}
Wird die hier gezeigte Action ausgeführt, erscheint im Browser der Satz
APF-Template
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.
Hinweis:
Innerhalb einer Action kann bequem über die Variable
$__Input auf das
Input-Objekt zugegriffen werden. Besteht die Notwendigkeit innerhalb einer Applikation von
Außen
auf die Action-Input-Informationen zugreifen zu müssen, so kann auf die Instanz einer Action
die Methode
getInput() angewendet werden. Diese liefert das korrespondierende
Input-Objekt als Referenz zurück.
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
APF-Template
{ActionNamespace}::actions::{CONTEXT}
und mit dem Namen
APF-Template
{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:
APF-Template
[{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 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.
Um eine Applikation im FrontController-Style laufen zu lassen, sollte in der
index.php
folgendes eingetragen sein:
PHP-Code
// 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
PHP-Code
// 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.
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 Input-Objekt der Action ein.
Diese Verhaltensweise wird nur erreicht, sofern in der gewünschten Action die Klassen-Variable
$__KeepInURL im Konstruktor auf true gesetzt wurde. In der
Standard-Definition enthält diese den Wert false. Andernfalls werden alle
Action-Anweisungen aus der generierten URL entfernt. Damit ist es möglich, komplexe
Applikationen zu entwerfen, in denen unabhängige Module über eine lose URL-Kopplung
koexistieren.
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.
Das Link-Schema einer Front-Controller-Action-Anweisung in der URL lautet
APF-Template
{namespace}-action:{name}={key1}:{value1}|{key2}:{value2}|...
bzw.
APF-Template
/{namespace}-action/{name}/{key1}/{value1}/{key2}/{value2}/...
im Fall von URL-Rewriting. Bei normalen URLs dient
& oder
? als
Trenn-Element zwischen Parametern und Action-Anweisungen, im Fall von URL-Rewriting müssen
normale Parameter mit
/~/ von Action-Anweisungen separiert werden.
In vielen Applikationen ist es notwenig Links zu generieren. In Frontcontroller-basierten Anwendungen,
in denen keine Action-Anweisungen in URLs manipuliert werden müssen, kann der
FrontcontrollerLinkHandler wie sein "kleiner Bruder"
LinkHandler
eingesetzt werden um URLs zu erzeugen. Aus einem Link
APF-Template
http://adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2
kann auf einfache Weise
APF-Template
http://adventure-php-framework.org/Seite/Guestbook/benchmarkreport/true
generiert werden. Hierzu ist lediglich ein
PHP-Code
// URL definieren
$URL = 'http://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.
Actions werden gleichermaßen als URL-Parameter behandelt wie "normale" URL-Bestandteile. Daher
gestaltet es sich sehr einfach, aus einer URL
APF-Template
http://adventure-php-framework.org/Seite/ChangeLog/benchmarkreport/true/param1/value1/param2/value2
den Link
APF-Template
http://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:
PHP-Code
// URL definieren
$URL = 'http://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.
Das Beispiel in Kapitel 2.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:
PHP-Code
// URL definieren
$URL = 'http://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 2.4.2. aufgezeigt, zurückgegriffen
werden.
Der FrontController besitzt ein eigenes vierstufiges Timing-Model, 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
PHP-Code
class MyAction extends AbstractFrontcontrollerAction {
// Timing festlegen
var $__Type = 'pretransform';
function MyAction(){
}
function run(){
}
}
manipuliert werden. Implementierungsdetails zur Methode
run() finden sich in der
API-Dokumentation der Klasse
Frontcontroller.
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
PHP-Code
class DemoSiteModel extends coreObject {
function DemoSiteModel(){
$this->__Attributes['view.content.template'] = 'login';
$this->__Attributes['view.topmenu.template'] = 'empty';
}
}
implementiert, die an den relevanten Stellen mit einem
PHP-Code
$Model =&$this->__getServiceObject('sites::demosite::biz','DemoSiteModel');
verwendet oder befüllt werden kann.
In einer Template-Datei kann dann mit einem
APF-Template
<fcon:importdesign
templatenamespace="sites::apfdocupage::pres::templates"
modelnamespace="sites::demosite::biz"
modelfile="DemoSiteModel"
modelclass="DemoSiteModel"
modelparam="view.content.template"
[sessionsingleton="true|false"]
/>
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ächst per
APF-Template
<core:addtaglib namespace="tools::html::taglib" prefix="fcon" class="importdesign" />
bekannt gemacht werden. Das Attribut
sessionsingleton definiert, ob die verwendete
Model-Klasse im Modus
SESSIONSINGLETON oder
SINGLETON erzeugt werden soll. Details
zu dem Erzeugungsmodalitäten kann der
API-Dokumentation der Klasse
ServiceManager entnommen werden.
Mit diesem Konstrukt lässt sich die Einbindung von Views bequem über das Model einer
Applikation steuern und die Verantwortung wird so 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.
1
Tostan
23.06.2009, 16:22:26
zum Abschnitt 2.6. Model-basiertes View-Konzept
erzeugt man das Applikationsmodell SessionSingleton, wie im Tutorial Frontcontroller beschrieben, dann muss man in der Template-Datei bei fcon:importdesign den Parameter sessionsingleton = "TRUE" angeben.
2
Christian
14.07.2009, 22:59:13
Hinweise zum Timing-Modell und zum Zugiff auf REQUEST-Parameter in Input-Klassen finden sich unter http://forum.adventure-php-framework.org/de/viewtopic.php?f=6&t=144.
3
Sebastian
18.09.2009, 09:45:53
Zum Unterpunkt Model-basiertes View-Konzept wäre imho eine Erweiterung sinnvoll, die auf die Navigation innerhalb der Anwendung eingeht.
Im Code Beispiel DemoSiteModel wird dies angedeutet, jedoch nicht näher darauf eingegangen
4
Christian
24.09.2009, 22:33:27
Hallo Sebastian,
ich plane hierzu in naher Zukunft noch einen erweiterten Artikel zum Thema Model based view design. Grundsätzlich kannst du jedoch über eine Front-Controller-Action jede Form der Navigation abbilden, sofern du damit die darunterliegenden Views beeinflusst (z.B. Content-Bereich). Um schöne URLs zu erreichen kann es dann sinnvoll sein, in einer Action die URL /en/My-pyge-title.html in den jeweiligen View im Model der Anwendung zu übersetzen. Soll es möglichst einfach gestaltet sein, reicht auch das Ansprechen der Action mit der Front-Controller-URL-Semantik.