Front-Controller

1. Einleitung

Wie im Kapitel Grundlagen beschrieben arbeitet die Anfrage-Bearbeitung des Adventure PHP Framework nach dem Bootstrapping-Prinzip. Dies bedeutet, dass alle Anfragen an eine Webseite oder Applikation von einer zentralen Datei beantwortet werden. Das APF folgt diesem Paradigma, da es Ihnen ermöglicht Initialisierung, Konfiguration und Auslieferung zentral zu steuern.

Innerhalb der Bootstrap-Datei hilft Ihnen der Frontcontroller die wiederkehrenden Aufgaben der Anfrage-Verarbeitung ohne Entwicklungsaufwand zu erledigen. Als zentraler und interaler kümmert er sich um

  • ... die Entgegennahme einer Anfrage,
  • ... die Ausführung der Input- und Output-Filter,
  • ... die Steuerung und Ausführung des Page-Controller und
  • ... die Ausführung von Anwendungslogik (Actions) zur Unterstützung der Anwendung.

2. Timing-Modell

Die Implementierung des Frontcontroller im Adventure PHP Framework basiert auf der gleichnamigen Pattern-Definition von Martin Fowler. Das APF setzt mit dem Page-Controller auf das HMVC-Pattern für die Komposition und Erzeugung der Präsentations-Schicht. Aus diesem Grund unterscheidet sich die Implementierung von reinen MVC-Frameworks, bei denen der Front-Controller lediglich ein Mapping eines Requests auf einen Controller versteht.

Das nachfolgend abgebildete Timing-Modell beschreibt den Prozess der Request-Verarbeitung des Frameworks und die Eingriffsmöglichkeiten durch Actions.

Front-Controller timing model

Da der Front-Controller als zentrale Instanz für die Verarbeitung einer Anfrage genutzt wird, existiert jeweils nur eine Instanz der Klasse Frontcontroller. Diese wird in der Bootstrap-Datei mit Hilfe der Erzeugung von Objekten-Implementierung erzeugt und mit der Methode start() gestartet.

Die Wiki-Seite Unterschied Front-Controller und Page-Controller greift nochmals wesentliche Merkmale des Front-Controller auf und beschreibt mögliche Anwendungsgebiete.

3. Actions

Front-Controller-Actions sind dazu gedacht, Logik auszuführen, die für einem bestimmten Zeitpunkt der Anwendung gedacht sind. Dies ist der Fall, wenn mehrere HMVC-Elemente auf gemeinsame Daten zugreifen, die Reihenfolge der Ausführung der Elemente jedoch nicht zur Entwickungszeit definiert werden kann - beispielsweise weil ein Redakteur die Inhalte zur Laufzeit verändern kann.

Ein weiterer Anwendungsfall ist die Kapselung und Wiederverwendung von Logik zur Initialisierung einer Anwendung (z.B. Füllen eines (View-)Models) oder zur Ausführung von View-Logik (z.B. Prüfung der Berechtigungen eines Benutzers). Neben der Ausführung von View-Logik lassen sich TYPE_PRE_PAGE_CREATE-Actions auch dazu nutzen dynamische Ressourcen (z.B. Bilder) auszuliefern oder in der TYPE_POST_TRANSFORM-Phase während der Applikationsausführung zu Tracking-Zwecken gesammelte Inhalte zu verarbeiten.

3.1. Definition

Das Timing-Diagramm in Kapitel 2 definiert vier unterschiedliche Zeitpunkte, zu denen Actions ausgeführt werden. Diese können dazu genutzt werden, um verschiedene Aufgaben innerhalb der Applikation zu erledigen. Die genannten Zeitpunkte sind durch folgenden Konstanten der Klasse AbstractFrontcontrollerAction definiert:

  • TYPE_PRE_PAGE_CREATE: die Action wird nach dem Ausführen der Input-Filter und vor dem Erzeugen des Page-Controller ausgeführt.
  • TYPE_PRE_TRANSFORM: die Action wird vor der Transformation der Seite (transform()) durch den Page-Controller ausgeführt.
  • TYPE_POST_TRANSFORM: die Action wird nach der Transformation der Seite (transform()) und vor der Ausführung der Output-Filter ausgeführt.

Weitere Hinweise zum Page-Controller und dessen Timing-Modell finden sich unter Page-Controller bzw. im Wiki.

Die in der zuvor aufgeführten Konstanten lassen sich bei der Implementierung von Actions wie folgt zur Definition des Ausführungszeitpunktes verwenden:

PHP-Code
class HeadlineImageGenerationAction extends AbstractFrontcontrollerAction { // Ausführungszeitpunkt in der Klassen-Deklaration festlegen protected $type = self::TYPE_PRE_PAGE_CREATE; public function __construct() { // Ausführungszeitpunkt bei der Erzeugung festlegen $this->type = self::TYPE_PRE_PAGE_CREATE } public function run() { ... } }
Bitte beachten Sie, dass der Ausführungszeitpunkt nur zur Implementierungszeit der Action-Klasse durch das Befüllen der Klassen-Variable $this->type definiert werden kann. Eine Änderung zur Laufzeit ist nicht vorgesehen!

3.2. Konfiguration

Der Front-Controller des APF bietet Ihnen zwei Arten von Actions:

  • Statische Actions, die in der Bootstrap-Datei für alle Requests registriert werden.
  • Dynamische Actions, die über URL-Parameter bei Bedarf ausgeführt werden.

Statische Actions kommen beispielsweise dann zum Einsatz, wenn Sie für die Ausführung Ihrer Anwendung ein von mehreren HMVC-Elementen genutztes View-Model initialisieren oder der aus Sicherheitsgründen der Login-Zustand Ihrer Benutzer vor Ausführung der eigentlichen Applikation geprüft werden soll.

Dynamische Actions werden beispielsweise dann eingesetzt, wenn Sie Komponenten Ihrer Anwendung (z.B. dynamisch erzeugte Bilder) auch über die zentrale Bootstrap-Datei ausliefern möchten oder die in einer Action enthaltene View-Logik nur unter bestimmten Bedingungen ausgeführt werden soll (z.B. Ausführung einer Suche).

Die folgenden beiden Kapitel zeigen die Konfiguration bzw. den Aufruf von statischen und dynamischen Actions.

3.2.1. Statische Actions

Statische Actions können in der Bootstrap-Datei vor dem Start des Front-Controller registriert werden. Die Reihenfolge der Ausführung hängt dabei von der Reihenfolge der Registrierung ab.

Im folgenden Code-Beispiel werden zwei Actions registriert, die für die Initialsierung eines View-Models und die Prüfung der Benutzerberechtigungen zuständig sind:

PHP-Code
include('../apps/core/bootstrap.php'); use APF\core\singleton\Singleton; use APF\core\frontcontroller\Frontcontroller; $fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); ... $fC->registerAction('APF\site\actions', 'init-model'); $fC->registerAction('APF\site\actions', 'check-permissions'); ...

Die Methode registerAction() besitzt zwei Argumente: den Namespace in der die Action-Konfiguration abgelegt ist und den Namen der Action. Details zur Konfiguration und Adressierung entnehmen Sie bitte Kapitel 5.1.

Die Actions init-model und check-permissions werden nun bei jeder Anfrage in der genannten Reihenfolge ausgeführt.

Soll eine Action für einen speziellen Anwendungsfall konfiguriert werden oder möchten Sie aus einem anderen Grund der registrierten Action dynamische oder statische Parameter mitgeben, so können Sie hierzu das dritte Argument der Methode registerAction() nutzen:

PHP-Code
include('../apps/core/bootstrap.php'); use APF\core\singleton\Singleton; use APF\core\frontcontroller\Frontcontroller; $fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); ... $fC->registerAction( 'APF\site\actions', 'init-model', array( 'foo' => 'bar', 'baz' => $_REQUEST['baz'] ) ); ...
3.2.2. Dynamische Actions

Dynamische Actions lassen sich über URL-Parameter ansteuern und damit bei Bedarf ausführen. Dieser Mechanismus ist über die mit dem APF ausgelieferten Input-Filter realisiert. Diese analysieren die aktuelle URL, extrahieren die darin enthaltenen Action-Aufrufe und übergeben diese an den Front-Controller.

Wie im URL-Rewriting beschrieben bietet das APF zwei Typen von Input-Filtern an: den ChainedStandardInputFilter, der sich um die Filterung von Standard-URLs kümmert und den ChainedUrlRewritingInputFilter, der umgeschriebene URLs decodieren und in eine interne Repräsentation umwandeln kann. Die Adressierung von Actions für Standard-URLs gestaltet sich wie folgt:

Code
{namespace}-action:{action-name}={param1}:{value1}|{param2}:{value2}|...

Dabei entspricht {namespace} dem Namespace der Action-Konfigurations-Datei und {action-name} dem Namen der Action. Die Parameter-Sätze sind jeweils durch "|" (Pipe) getrennt, Name und Wert durch ":". Innerhalb der URL ist es möglich, mehrere Action-Anweisungen und Standard-Request-Parameter zu platzieren. Die folgende URL besitzt zwei Action-Anweisungen und weitere Steuer-Parameter:

Code
?VENDOR_projects_projectone-action:setModel=pageid:1|lang:de&news-page=3&VENDOR_projects_projectone-action:stat=action:view|referer:32
Bitte beachten Sie, dass die Reihenfolge der Action-Definitionen die Reihenfolge der Ausführung für Actions gleichen Typs vorgibt! Details zu Action-Typen entnehmen Sie bitte Kapitel 3.1..

Das Schema für umgeschriebene URLs ist so gewählt, dass weiterhin eine Trennung zwischen "normalen" Parametern und Action-Anweisungen möglich ist. Aus diesem Grund wurde das Trennzeichen "/~/" zwischen Parametern und Action-Anweisungen als auch zwischen mehreren Action-Anweisungen eingeführt. Die Adressierung von Actions für umgeschriebene URLs gestaltet sich wie folgt:

Code
/~/{namespace}-action/{config-name}/{param1}/{value1}/{param2}/{value2}/...

Das zuvor aufgeführte Beispiel sieht für umgeschriebene URLs wie folgt aus:

Code
/~/VENDOR_projects_projectone-action/setModel/pageid/1/lang/de/~/news-page/3/~/VENDOR_projects_projectone-action/stat/action/view/referer/32
Der hier gewählte Ansatz ist auf Allgemeingültigkeit ausgelegt. Soll aus SEO- oder anderen Gründen ein anderes URL-Layout gewählt werden, ist dies auf Basis von Input-Filtern jederzeit möglich. Action-Aufrufe können sowohl mit Hilfe von RewriteRules als auch durch Anpassung der Filter generiert werden, die die Informationen aus der URL decodieren. Hinweise zur Implementierung von eigenen Filtern finden Sie im Wiki.
3.2.3. Action-URL-Mapping
Bitte beachten Sie, dass das in diesem Kapitel beschriebene Feature erst ab Version 2.1 verfügbar ist.

Das in Kapitel 3.2.2. beschriebene URL-Schema ist auf Allgemeingültigkeit ausgelegt. Gleichzeitig kann es aus SEO-Gesichtspunkten für Ihr Projekt suboptimal sein, da die URLs durch darin enthaltene Actions lang oder unschön werden.

Um URLs mit Action-Anweisungen kürzer und attraktiver zu gestalten, lassen sich für statische und dynamische Actions URL-Mappings definieren. Diese Trivial-Namen werden von den mit dem APF mitgelieferten Eingabe-Filtern ChainedStandardInputFilter und ChainedUrlRewritingInputFilter erkannt und in das interne Format umgewandelt. Bei der Link-Generierung durch den LinkGenerator nutzen die ebenfalls mitgelieferten LinkScheme-Implementierungen DefaultLinkScheme und RewriteLinkScheme die beim Frontcontroller registrieren Mappings um die relevanten Action-Anweisungen in ein externes Format zu konvertieren.

Die Anzahl der Action-Mappings ist technisch nicht begrenzt. Sie können daher für mehrere oder alle Actions pro URL ein Mapping definieren.

Nutzen Sie zur Ausführung einer Suche eine Front-Controller-Action, so lautet die dafür generierte URL in der Standard-Konfiguration wie folgt:

Code
?VENDOR_components_search-action:executeSearch=type:faq

Nutzen Sie umgeschriebene URLs (Details siehe URL-Rewriting), so generiert der LinkGenerator daraus

Code
/~/VENDOR_components_search-action/executeSearch/type/faq

Möchten Sie sie generierten Links kürzer und damit attraktiver gestalten, so haben Sie die Möglichkeit ein ActionUrlMapping zu registrieren. Erweitern Sie Ihre Bootstrap-Datei (index.php) dazu wie folgt:

PHP-Code
use APF\core\singleton\Singleton; use APF\core\frontcontroller\Frontcontroller; use APF\core\frontcontroller\ActionUrlMapping; $fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); ... $fC->registerAction('VENDOR\components\search', 'executeSearch'); $fC->registerActionUrlMapping( new ActionUrlMapping('search', 'VENDOR\components\search', 'executeSearch') ); ...

Nutzen Sie zur Generierung der URL den folgenden Code, so nutzt das registrierte LinkScheme die beim Front-Controller gemeldeten Mappings um eine verkürzte URL zu erzeugen:

PHP-Code
use APF\tools\link\LinkGenerator; use APF\tools\link\Url; $link = LinkGenerator::generateActionUrl( Url::fromCurrent(), 'VENDOR\components\search', 'executeSearch', array('type' => 'faq') );

Die Suche kann nun per

Code
?search=type:faq

bzw.

Code
/search/type/faq

bei Nutzung von umgeschriebenen URLs ausgelöst werden.

Bitte achten Sie bei der Definition der Trivial-Namen auf mögliche Überschneidungen mit normalen URL-Parametern. Im Fall von Überschneidungen kann die korrekte Analyse und Generierung der URL vom Framework nicht garantiert werden!

Zusätzlich zur klassischen Registrierung von Action-Mappings stehen Ihnen noch zei weitere Möglichkeiten zur Verfügung: Übergabe eines URL-Parameters bei der Registrierung der Action (Frontcontroller::registerAction()) oder Definition einer oder mehrerer Konfigurations-Dateien.

Registrieren Sie Ihre Action statisch in der Bootstrap-Datei, so bietet sich die Nutzung des vierten Arguments der Methode registerAction() an. Die Registrierung der Action und des zugehörigen Mappings verkürzt sich dann auf

PHP-Code
$fC->registerAction('VENDOR\components\search', 'executeSearch', array(), 'search');
Bitte beachten Sie, dass das dritte Argument optional ist. Möchten Sie Ihre Action bei der Registrierung vorkonfigurieren, so können Sie bereits an dieser Stelle beliebige Parameter übergeben. Diese können Sie dann innerhalb der Action über das Input-Objekt abrufen.
Bitte beachten Sie, dass die Registrierung von Actions und Mappings vor dem Start des Front-Controller über die Methode Frontcontroller::start() erfolgen muss!

Möchten Sie die Konfiguration von Action-Mappings auslagern, so können Sie dem Front-Controller eine Konfigurations-Datei mitgeben aus der das Mapping gelesen wird.

Hierzu bietet der Front-Controller die Methode registerActionUrlMappings() an. Diese nimmt den Namespace und den Namen der Konfiguration entgegen. Das Schema der Konfiguration ist wie folgt definiert:

APF-Konfiguration
[{Url-Token}] ActionNamespace = "" ActionName = ""

Die folgende Code-Box zeigt Ihnen die erforderliche Konfigurations-Sektion für das in diesem Kapitel besprochene Anwendungsbeispiel:

APF-Konfiguration
; search <-> VENDOR_components_search-action:search [search] ActionNamespace = "VENDOR\components\search" ActionName = "executeSearch"
Innerhalb einer Konfigurations-Datei lassen sich eine beliebige Anzahl an Action-Mappings definieren. Der Front-Controller nimmt eine beliebige Anzahl von Dateien zur Konfiguration von Action-Mappings entgegen. Es steht Ihnen daher frei die Struktur der Konfigurations-Dateien nach den Vorgaben Ihres Projekts zu strukturieren.

Die Registrierung des oben beschriebenen Action-Mappings lässt sich wie folgt in der Bootstrap-Datei erledigen:

PHP-Code
use APF\core\singleton\Singleton; use APF\core\frontcontroller\Frontcontroller; use APF\core\frontcontroller\ActionUrlMapping; $fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); ... $fC->registerActionUrlMappings('VENDOR\components\search', 'url-mappings.ini'); ...
Bitte beachten Sie, dass die Registierung der Action-Mappings in einer eigenständigen Konfiguration definiert werden muss. Dies ist notwendig, da die Analyse der URL gemäß des Timing-Modell zu einem früheren Zeitpunkt stattfinden als die Ausführung einer Action.
Beachten Sie ferner, dass aus dem zuvor genannten Grund die Registrierung von Action-Mappings vor dem Starten des Front-Controller vorgenommen werden muss.

3.3 Erzeugen einer Action über den DI-Container

Neben der der Möglichkeit, Actions vom Frontcontroller erzeugen zu lassen und dazu den Klassen-Namen der Action anzugeben, besteht auch die Möglichkeit die Action durch den DI-Service-Manager erzeugen zu lassen. Dies bietet sich vor allem bei komplexen Aufgabenstellungen an, in denen die Action von anderen Anwendungskomponenten abhängig ist. Die abhängigen Objekte können hier einfach vom DIServiceManager in die Action injiziert werden. Details zur Konfiguration und Anwendung finden Sie unter Services.

Die Action-Konfiguration unterscheidet sich im Wesentlichen nicht von der oben aufgeführten Variante (siehe Kapitel 3.2). In diesem Fall wird statt des Parameters ActionClass jedoch der Namespace und Name des Services angegeben:

APF-Konfiguration
[{Action-Name}] ActionServiceNamespace = "" ActionServiceName = "" [InputClass = ""] [InputParams = ""]
  • ActionServiceNamespace: Gibt den Namespace an, in dem die Service-Konfiguration für die Action abgelegt wird. (Beispiel: APF\project\biz\actions)
  • ActionServiceName: Der Name des Services für die Action. (Beispiel: initialize-model)
Bitte beachten Sie, dass die Konfigurations-Direktiven in der Version 2.0 mit dem Präfix FC. definiert werden müssen. Für die Erzeugung einer Action mit dem DIServiceManager lautet das Schema einer Action-Definition wie folgt:
APF-Konfiguration
[{Action-Name}] FC.ActionServiceNamespace = "" FC.ActionServiceName = "" [FC.InputClass = ""] [FC.InputParams = ""]

Für das obige Beispiel erwartet der Front-Controller die Konfigurations-Datei config/project/biz/action/{CONTEXT}/{ENVIRONMENT}_serviceobjects.ini, die eine Service-Definition initialize-model beinhaltet. Der Inhalt der Datei kann wie folgt aussehen:

APF-Konfiguration
[initialize-model] class = "APF\project\biz\actions\LoadModelAction" servicetype = "NORMAL" init.model.method = "setModel" init.model.namespace = "APF\project\biz\model" init.model.name = "ApplicationModel"

4. Bootstrap-Datei

Wie bereits in der Einleitung beschrieben setzt das APF auf das Bootstrapping Paradigma. Innerhalb der Bootstrap-Datei spielt der Front-Controller eine zentrale Rolle bei der Entgegennahme und Verarbeitung von Anfragen.

Die folgende Code-Box zeigt eine minimale Bootstrap-Datei, die als Start für jede Anwendung genutzt werden muss:

PHP-Code
include('./APF/core/bootstrap.php'); use APF\core\singleton\Singleton; use APF\core\frontcontroller\Frontcontroller; $fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); echo $fC->start('...', '...');

Die erste Zeile bindet die Datei bootstrap.php ein, die die notwendigen APF-Komponenten läd und das Framework initial konfiguriert. Sofern für Ihre Applikation eine davon abweichende Konfiguration verschiedener Komponenten vorgenommen werden soll, kann dies vor dem Start des Front-Controller erfolgen.

Der Front-Controller selbst kann mit folgenden Methoden konfiguriert werden:

  • setContext(): Definiert den Kontext der Anwendung und damit aller innerhalb der Anwendung über das APF erzeugten Objekte. Dieser Parameter wird vor allem für die Konfiguration genutzt.
  • setLanguage(): Definiert die aktuelle Sprache der Anwendung. Diese kann in mehrsprachigen Anwendungen für die Anzeige von Sprach-abhänigen Inhalten genutzt werden.

Weitere Konfigurationen in der Bootstrap-Datei sind unter Grundlagen aufgeführt.

5. Implementierung von Actions

Eine Front-Controller-Action im APF zeichnet sich durch eine Konfiguration und eine Implementierung in Form einer PHP-Klasse, die von AbstractFrontcontrollerAction erbt, aus. Die Trennung zwischen Implementierung und Konfiguration sorgt dafür, dass die Implementierung selbst nach aussen versteckt wird und zusätzliche Abhängigkeiten und Konfigurationen transparent aufgelöst werden können. Zusätzlich dazu bietet die Abstraktion von Action-Aufrufen über die URL ein erhöhtes Maß an Sicherheit, da die interne Struktur einer Applikation nicht nach aussen sichtbar ist.

Die Eingabe-Parameter einer Action stellt der Front-Conroller in einer Instanz der Klasse FrontcontrollerInput oder einer davon abgeleiteten Klasse zur Laufzeit zur Verfügung. Sie haben damit die Möglichkeit, auf alle Parameter, die entweder in der URL oder der Konfiguration definiert sind innerhalb der Action zuzugreifen.

5.1. Konfiguration

Jede Action wird durch eine Konfigurations-Sektion definiert. Zur Adressierung einer Action wird der Namespace und der Name der Konfigurations-Sektion genutzt. Der Name der Konfigurations-Datei lautet per Konvention immer {ENVIRONMENT}_actionconfig.ini.

Die Konfigurations-Sektion beinhaltet die Definition der Action-Implementierung und optional eine eigene Input-Implemenierung sowie optionale, statische Konfigurations-Parameter. Die Konfigurations-Parameter können dazu genutzt werden um eine konkrete Action-Implementierung für unterschiedliche Anwendungsfälle zu konfigurieren und so den Code in mehreren Anwendungen einsetzbar zu gestalten.

Die Ablage der Konfigurations-Dateien folgt dem unter Konfiguration beschriebenen Schema.

Wird eine Action mit dem Namespace APF\modules\captcha\biz und dem Namen showCaptcha beim Front-Controller zur Ausführung angefordert, so erwartet diser eine Konfigurations-Datei mit dem Namen {ENVIRONMENT}_actionconfig.ini unter dem genannten Namespace. Ist die aktuelle Applikation mit dem Context projectone und dem Standard-Einstellungen für die Umgebung konfiguriert, so wird die Konfiguration unter

Code
/APF/config/modules/captcha/biz/projectone/DEFAULT_actionconfig.ini

erwartet. Die Konfigurations-Datei beinhalten dabei eine Sektion mit folgendem Inhalt:

APF-Konfiguration
[showCaptcha] ActionClass = "APF\modules\captcha\biz\ShowCaptchaImageAction"

Der Front-Controller nutzt diese Information nun um die Instanz der Klasse ShowCaptchaImageAction zu erzeugen und gemäß der Timing-Konfiguration (siehe Kapitel 3.1) auszuführen.

Das Schema einer Action-Konfiguration besteht aus folgenden Elementen:

APF-Konfiguration
[{Action-Name}] ActionClass = "" [InputClass = ""] [InputParams = ""]

Die aufgeführten Parameter haben folgende Bedeutungen:

  • Action-Name: Externer Bezeichner der Action. Dieser Name wird sowohl in der URL als auch bei der statischen Konfiguration über die Methode registerAction() genutzt (Beispiel: setModel).
  • ActionClass: Voll-qualifizierter Klassen-Name der Action-Implementierung (Beispiel: APF\project\biz\actions\LoadModelAction).
  • InputClass: Voll-qualifizierter Klassen-Name der Input-Implementierung (Beispiel: APF\project\biz\actions\DemositeModel). Standard-Wert bei nicht definierter Konfigurations-Direktive ist APF\core\frontcontroller\FrontcontrollerInput.
  • InputParams: Enthält Parameter, die bei der Erzeugung der Action in die Instanz der Input-Implementierung injiziert werden. Schlüssel und Wert werden durch ":" getrennt, mehrere Werte-Paare durch "|" (Beispiel: login:true|headview:menu). Ist die Konfigurations-Direktive nicht vorhanden, wird das Input-Objekt nicht initialisiert.
Bitte beachten Sie, dass die Konfigurations-Direktiven in der Version 2.0 mit dem Präfix FC. definiert werden müssen. Für die Erzeugung einer Action mit dem Front-Controller lautet das Schema einer Action-Definition wie folgt:
APF-Konfiguration
[{Action-Name}] FC.ActionClass = "" [FC.InputClass = ""] [FC.InputParams = ""]
Die Nutzung einer eigenen Input-Implementierung ist empfehlenswert, wenn Sie die Verarbeitung von URL- oder Input-Parametern kapseln möchten.
Die in der Direktive InputParams definierten Eingabe-Parameter können über die URL überschrieben werden. Dies ist insbesondere dann sinnvoll, wenn Sie beabsichtigen eine Action mit Basis-Werten konfigurieren und in einem konkreten Anwendungsfall mit einem anderen Verhalten zu starten. Die hierzu notwendige Logik ist bereits in der Methode Frontcontroller::addAction() vorhanden.

5.2. Action-Implementierung

Eine Front-Controller-Action definiert sich durch eine PHP-Klasse, die von AbstractFrontcontrollerAction erbt. Zur Ausführung der Logik muss die Methode run() implementiert werden.

Das folgende Code-Beispiel zeigt eine Action, die Sie mit Hallo Welt! begrüßt und die Verarbeitung der Anfrage anschließend beendet:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\AbstractFrontcontrollerAction; class GreetMeAction extends AbstractFrontcontrollerAction { public function __construct() { $this->type = self::TYPE_PRE_PAGE_CREATE; } public function run() { echo 'Hallo Welt!'; exit(); } }

Rufen Sie die in Kapitel 4 definierte Bootstrap-Datei mit der URL

Code
?ACME_project-action:greetMe

auf, so werden Sie - vorausgesetzt Sie haben die entsprechende Konfiguration angelegt - mit dem Satz Hallo Welt! begrüßt.

Bitte beachten Sie, dass der in der URL angegebene Namespace nicht mit dem Namespace der Action-Implementierung übereinstimmen muss! Daher wählt das Beispiel bewusst einen unterschiedlichen Namespace.

Bei der Erzeugung von Actions werden Kontext und Sprache der Applikation injiziert. Aus diesem Grund können Sie in Actions beliebige Applikations-Logik verpacken. Kontext-abhängige Aktionen wie beispielsweise das Laden einer Konfiguration sind daher ohne Probleme möglich. Ebenso verhält es sich mit dem Zugriff auf gemeinsam genutzte Ressourcen wie einem View-Model.

Weitere Beispiele für Action-Implementierungen finden Sie im Kapitel 8.

5.3. Input-Implementierung

Input-Klassen kapseln die Eingabe-Parameter einer Action und sind hinsichtlich der Konfiguration als optional zu betrachten. Wird für eine Action in der Konfiguration keine Input-Implementierung angegeben, so nutzt der Front-Controller eine Instanz der Klasse FrontcontrollerInput als Standard.

Ist die Logik der Input-Klasse auf das Aufnehmen und Wiedergeben von Action-Parametern beschränkt, so ist es nicht notwendig eine eigene Klasse zu implementieren. Sie können auch in diesem Fall auf die Standard-Implementierung FrontcontrollerInput zurück greifen.

Möchten Sie von der Action in Kapitel 5.2. persönlich begrüßt werden, so können Sie dies mit der folgenden Implementierung erreichen:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\AbstractFrontcontrollerAction; class GreetMeAction extends AbstractFrontcontrollerAction { public function __construct() { $this->type = self::TYPE_PRE_PAGE_CREATE; } public function run() { $input = $this->getInput(); echo 'Hallo ' . $input->getAttribute('name') . '!'; exit(); } }

Der abgebildete Code der Methode run() setzt voraus, dass Sie die Action mit dem Parameter name ausstatten. Rufen Sie die in Kapitel 4 definierte Bootstrap-Datei mit der URL

Code
?ACME_project-action:greetMe=name:Harry

auf, so werden Sie - vorausgesetzt Sie haben die entsprechende Konfiguration angelegt - mit dem Satz Hallo Harry! begrüßt.

Wir kein Name übergeben, so wird keine korrekte Begrüßung ausgegeben. Mit einer eigenen Input-Implementierung haben Sie die Möglichkeit, eine Verarbeitung der Eingabe-Parameter zu implementieren, die beispielsweise dafür sorgt, dass fehlende Eingaben durch Standard-Werte ersetzt werden:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\FrontcontrollerInput; class GreetMeInput extends FrontcontrollerInput { public function getName() { return $this->getAttribute('name', 'Welt'); } }

Schreiben Sie die Action nun wie folgt um, so werden Sie sowohl mit als auch ohne Übergabe eines Namens korrekt begrüßt:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\AbstractFrontcontrollerAction; class GreetMeAction extends AbstractFrontcontrollerAction { public function __construct() { $this->type = self::TYPE_PRE_PAGE_CREATE; } public function run() { $input = $this->getInput(); echo 'Hallo ' . $input->getName() . '!'; exit(); } }

6. Action-Steuerung

6.1. Aktivierung/Deaktivierung

Neben der Definition der Ausführungszeit (Details siehe Kapitel 3.1) stellt der Front-Controller noch eine weitere Möglichkeit bereit die Ausführung von Actions zu beeinflussen. Möchten Sie die Ausführung einer Action unter bestimmten Umständen unterbinden, so können Sie dies durch Überschreiben der Methode AbstractFrontcontrollerAction::isActive() realisieren.

Gibt isActive() Ihrer Implementierung true zurück, wird die run()-Methode ausgeführt, im Fall von false nicht. Dies ermöglicht Ihnen die Action nicht oder erst dann auszuführen, wenn eine andere Action auf dem Action-Stack geladen ist oder eine andere Bedingung zutrifft.

Das folgende Beispiel sorgt dafür, dass die Action nur dann ausgeführt wird, denn eine bestimmte Action nicht auf dem Action-Stack vorhanden ist:

PHP-Code
public function isActive() { $captcha = $this->getFrontController()->getActionByName('showCaptcha'); return $captcha === null; }

6.2. Priorisierung

Bitte beachten Sie, dass dieses Feature erst ab Version 2.1 zur Verfügung steht.

In manchen Situationen ist es notwendig, die Reihenfolge der Action-Ausführung festlegen zu können. Dies ist beispielsweise dann der Fall, wenn Sie eine Action zur Initialisierung ihrer Applikation nutzen und zusätzlich Teile der Applikations-Logik als Actions abbilden (z.B. Suche). Vertraut die Logik der Suche auf einer initialisierten Appikation, so muss sichergestellt sein, dass die Suche erst nach der Initialisierung stattfindet.

Für diesen Fall bietet das APF die Möglichkeit an, Actions nach einer bestimmten Reihenfolge zu sortieren. In Auslieferungs-Zustand erhält jede Action die Priorität 10. Besitzt eine Action die Priorität 20, so wird diese vorher ausgeführt, sofern sie eine Zahl kleiner 10 - z.B. 1 - besitzt, danach.

Der Front-Controller nutzt die Methode AbstractFrontcontrollerAction::getPriority() zur Bestimmung der Priorität und zur entsprechenden Sortierung auf dem Action-Stack. Möchten Sie die Priorität Ihrer Action beeinflussen, so überschreiben Sie bitte die genannte Methode. Beispiel:

PHP-Code
use APF\core\frontcontroller\AbstractFrontcontrollerAction; class SearchAction extends AbstractFrontcontrollerAction { ... public function getPriority() { return 9; } ... }

Wird die SearchAction zusammen mit einer Action zur Initialisierung Ihrer Applikation ausgeführt, so kommt diese erst als zweite an die Reihe.

Die Reihenfolge der Ausführung Ihrer Aktions ist zunächst abhängig von ihrem Typ (prepagecreate, pretransform und posttransform), die gemäß Timing-Modell zu unterschiedlichen Zeitpunkten ausgeführt werden.

Bei Actions gleichen Typs entscheidet dann die von der Methode getPriority() zurückgegebene Zahl. Bei wiederum gleichen Werten entscheidet der Registrierungszeitpunkt. Dieser wird entweder durch die statische Registrierung in der Bootstrap-Datei (index.php) oder für dynamische Actions über die Position in der URL bestimmt.

Da die Priorität von der Methode getPriority() zurück gegeben wird, lässt sich diese auch auf Basis von weiteren Faktoren berechnen. Soll eine Action beispielsweise abhängig von einer anderen Action auf dem Stack früher oder - falls diese vorhanden ist - später ausgeführt werden, so lässt sich dies wie folgt realisieren:

PHP-Code
public function getPriority() { $relatedAction = $this->getFrontController()->getActionByName('relatedAction'); if ($relatedAction === null) { return 12; } return $relatedAction->getPriority() - 1; }

Mit Hilfe der Methode getActions() lässt sich beispielsweise an Hand der Liste die höchste Priorität feststellen und als Grundlage für eine Berechung nutzen:

PHP-Code
public function getPriority() { $max = 0; foreach ($this->getFrontController()->getActions() as $action) { $max = max($max, $action->getPriority()); } return $max + 1; }

Die Gestaltung des URL-Layouts übernehmen im APF die Input- und Output-Filter sowie die zugehörigen LinkScheme-Implementierungen.

Die Eingabe-Filter sind - wie in Kapitel 3.2.2. beschrieben - dafür verantwortlich die URL zu analysieren, darin enthaltene Front-Controller-Anweisungen zu extrahieren und an den Front-Controller zu übergeben. Das APF liefert hierzu die Filter ChainedStandardInputFilter für einfache URLs und ChainedUrlRewritingInputFilter für umgeschriebene URLs mit.

Die jeweils genutzte LinkScheme-Implementierung bietet Ihnen mit der formatActionLink()-Methode eine Schnittstelle an um Action-URLs automatisiert und jeweils passend zum Eingabe-Filter zu generieren. Das APF liefert hierzu die Implementierungen DefaultLinkScheme für einfache URLs und RewriteLinkScheme für umgeschriebene URLs mit.

Bei der Generierung von URLs über die Methode LinkGenerator::generateActionUrl() werden auf dem Action-Stack vorhandene Actions nicht automatisch in die generierte URL inkludiert. Dies hat den Grund, dass nur der Entwickler selbst entscheiden kann, welche Action in der URL repräsentiert sein soll und damit bei der nächsten Anfrage dynamisch ausgeführt werden soll.

Möchten Sie eine Action bewusst bei der URL-Generierung einbeziehen, so haben Sie die Möglichkeit diese über die Variable $keepInUrl zu markieren. Beispiel:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\AbstractFrontcontrollerAction; class GreetingAction extends AbstractFrontcontrollerAction { public function __construct() { $this->type = self::TYPE_PRE_PAGE_CREATE; $this->setKeepInUrl(true); } ... }

Um dynamisch zu entscheiden - beispielsweise an Hand von Parametern eines View-Models Ihrer Anwendung - ob die Action bei der URL-Generierung berücksichtigt werden soll oder nicht, können Sie auch die Methode getKeepInUrl() direkt überschreiben. Diese wird vom jeweiligen LinkScheme dazu genutzt um Actions explizit einzuschließen:

PHP-Code
namespace ACME\project\actions; use APF\core\frontcontroller\AbstractFrontcontrollerAction; class GreetingAction extends AbstractFrontcontrollerAction { public function __construct() { $this->type = self::TYPE_PRE_PAGE_CREATE; } public function getKeepInUrl() { $model = $this->getViewModel(); return $model->getFoo() === true; } ... }

Details zur Generierung von URLs können Sie dem Kapitel Links entnehmen.

8. Weiterführende Beispiele

Konkrete Anwendungsfälle wie die dynamische Auslieferung von Bildern, der Prüfung des Login-Zustands eines Benutzers oder der Sprach-Umschaltung werden im Kapitel Front-Controller Tutorial beschrieben.

Ein weiteres Beispiel für die Anwendung von Actions können Sie im Artikel RSS delivery with the APF (English) nachlesen.

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   »
Einträge/Seite: | 5 | 10 | 15 | 20 |
1
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.
2
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
3
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.
4
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.