Frontcontroller-Tutorial

1. Einführung

Die Front-Controller-Implementierung des Frameworks kann durch das Timing-Modell für unterschiedliche Bereiche eingesetzt werden. Das folgende Diagramm zeigt, wann Actions bei der Ausführung der Front-Controller-Methode start() ausgeführt werden:

Frontcontroller timing model

Das vorliegende Tutorial möchte nun Einsatzgebiete des Front-Controllers aufzeigen und konkrete Hinweise für die Implementierungen geben. Als typische Anwendungsfälle werden nun im Folgenden die Auslieferung von Bildern und die Prüfung von Login-Informationen, die zum Aufbau eines Applikationsmodels dienen, diskutiert.


2. Auslieferung von Bildern

Das Framework bietet dem Entwickler mit der Bootstrap-Architektur an, alle Inhalte über eine zentrale PHP-Datei ausliefern zu lassen. Je mehr Module eine Applikation besitzt, desto größer wird der Wunsch, auch PopUp-Fenster mit speziellen Inhalten wie Druck-Ansichten oder Formularen, bzw. Medien wie Bilder und Videos über diesen Mechanismus auszuliefern. Um derartige Inhalte darstellen zu können wird in der Regel eine weitere Bootstrap-Datei für das gewünschte Modul angelegt, das in den meißten Fällen zur index.php redundanten Code enthält.

Mit Hilfe des Front-Controllers, bzw. einer Front-Controller-Action, kann diesem Problem Abhilfe geschaffen werden. Das Timing-Modell des Front-Controller-Dispatcher-Prozesses (obiges Diagramm) sieht vor, dass eine Action vor dem Erstellen der Page-Controller-Seite (prepagecreate, siehe Front-Controller) ausgeführt werden kann. Der Entwickler ist dabei durch die Gestaltung des HTML-Codes frei, ob die mit der Action-Anweisung aufgerufene Seite im selben, oder in einem neuen Fenster erscheinen soll. Weiterer Vorteil ist, dass der Programm-Code des präsentierten Inhalts im Namespace des jeweiligen Moduls abgelegt werden kann und so eine einheitlichere und sauberere Struktur des Quellcodes erreicht werden kann. Zudem kann innerhalb einer Action problemlos eine Page-Controller-Seite erzeugt, transformiert und ausgegeben werden um den Inhalt eines PopUp-Fensters zu zeigen.

Wie in der FrontController-Dokumentation beschrieben ist, wird eine Action durch eine Sektion in einer Konfigurationsdatei, einer Action- und einer Input-Klasse definiert. Der Umfang der Input-Klasse richtet sich nach dem Umfang der auszuliefernden Applikation, bzw. der zu erledigenden Aufgabe. Im Modul socialbookmark, das mit jedem Release ausgeliefert wird, wird die im Folgenden beschriebene Action zur Auslieferung der Bookmark-Service-Symbole folgende Dateien verwendet. Diese soll nun näher erläutert werden.


2.1. Action-Konfiguration

Die Datei DEFAULT_actionconfig.ini (siehe Ordner apps/config/modules/socialbookmark/action/sites/demosite/ im apf-demopack-*-Release-Package) definiert die Bestandteile der Action und die Bezeichnung der Action in der URL. In den Input-Parametern werden Standard-Werte für das Input-Objekt definiert, die später Anwendung finden:
APF-Konfiguration
[showImage] FC.ActionNamespace = "modules::socialbookmark::biz::actions" FC.ActionClass = "ShowImageAction" FC.InputClass = "ShowImageInput" FC.InputParams = "img:bookmark_del_icio_us|imgext:png"

2.2. Action-Implementierung

Die Datei ShowImageAction.php beinhaltet die Definition des Programm-Codes der Action. Im Fall der Bild-Auslieferung wird der Pfad zum angefragten Service-Bilder zusammengesetzt, der entsprechende Header und anschließend das Bild selbst gesendet. Um die Verarbeitung nach dem Ausliefern des Bildes zu stoppen, wird am Ende ein exit() notiert. Andernfalls würde die in der index.php definierte Seite ausgeliefert werden und der Browser könnte das Bild nicht korrekt anzeigen. Folgender Quellcode ist für die Anzeige eines Bildes zuständig:
PHP-Code
class ShowImageAction extends AbstractFrontcontrollerAction { public function run(){ // Bild aus dem Input-Objekt beenden $Image = APPS__PATH.'/modules/socialbookmark/pres/image/'; $Image .= $this->input->getAttribute('img') . '.' . $this->input->getAttribute('imgext'); // Header senden header('Content-type: image/' . $this->input->getAttribute('imgext')); header('Cache-Control: public'); // Datei streamen readfile($Image); // Abarbeitung beenden exit(); } }

2.3. Input-Definition

Die Datei ShowImageInput.php beinhaltet die Definition des Input-Objekts, in dem die Parameter des Action-Aufrufs bereitgestellt werden. Im Fall der Bild-Auslieferung sind hier keine weiteren Parameter-Definitionen notwendig und folgender PHP-Code genügt um die Definition der Action zu komplettieren.
PHP-Code
class ShowImageInput extends FrontcontrollerInput { }

2.4. Praktische Anwendung

Im Socialbookmark-Modul werden die Symbole der Bookmark-Anbieter an den entsprechenden Stellen per
APF-Template
<img src="/~/modules_socialbookmark-action/showImage/imgext/png/img/bookmark_technorati" alt="" />
eingebunden. Analysiert man den Aufruf der unter 2.2. dargestellten Action, so kommt den Parametern folgende Bedeutung zu:
  • modules_socialbookmark: Namespace, unter dem die Definition (Konfiguration, siehe 1.1.) der Action gesucht wird. Action-Konfigurationen sind immer Context-abhängig und werden daher immer unter dem Verzeichnis
    APF-Template
    {NAMESPACE}/actions/{CONTEXT}
    gesucht.
  • -action: Das Kürzel dient dazu, eine Action-Anweisung in der URL zu identifizieren.
  • showImage: Name der Action und gleichzeitig der Name der Sektion der Definition.
  • imgext: Parameter imgext
  • png: Wert des Parameters imgext
  • img: Parameter img
  • bookmark_technorati: Wert des Parameters img

3. Prüfung von Login-Informationen

Front-Controller-Actions werden in der Regel dazu verwendet ein Applikationsmodel, das den Status der Anwendung repräsentiert, vor der Erzeugung der Präsentationsschicht aufzubauen. Hierzu bedient sich ein Action aus dem Input-Objekt, das vom Front-Controller bereitgestellt wird. Falls erwünscht, können in der Konfiguration der Action bereits Standard-Werte für die Input-Parameter definiert werden. Diese dienen dann zum Zeitpunkt der Verarbeitung der Action als Standard-Attribute.

Beim Thema "Prüfung von Login-Informationen" besteht die Aufgabe der Action darin, im Request mitgesendete Login-Informationen zu prüfen oder diese aus bestehenden Sessions oder Cookies auszulesen und entsprechend für die Applikation zur Verfügung zu stellen. Diese dienen dann der übrigen Businesslogik als Grundlage der Applikationssteuerung und Erzeugung der GUI. Front-Controller-Actions sind dabei als Bestandteil der Business-Logik zu sehen.

Das beschriebene Szenario beinhaltet im Wesentlichen zwei "business cases": Login-Informationen werden mitgesendet und Login-Informationen müssen aus anderen Quellen (Session, Cookie) bezogen und aufbereitet werden. Um einem Benutzer die Möglichkeit zu geben, sich per Cookie einzuloggen, muss die Action bei jedem Request ausgeführt werden. Zu diesem Zweck kann die für das Handling der Login-Informationen verantwortliche Action als "permanente" Action registriert werden. Dies geschieht in Bootstrap-Datei per
PHP-Code
$fC = &Singleton::getInstance('Frontcontroller'); $fC->setContext('my::context'); $fC->registerAction('my::namespace','myAction');
Die Implementierung der Action besteht auch hier aus drei Komponenten. Im Rahmen von MCV-, FrontController- und 3-Schicht-Architektur-basierten Applikationen ist es dabei hilfreich, ein zentrales Applikationsmodel zu definieren, das den aktuellen Zustand der Applikation speichert. Dieses Objekt kann später zur Ablaufsteuerung innerhalb der Businessschicht und zur Gestaltung der GUI eingesetzt werden.

Für das Login-Beispiel dient die nachstehend aufgeführte Klasse als Applikationsmodel. Um das Beispiel einfache zu gestalten, werden nur folgende Model-Informationen beachtet:
PHP-Code
class ApplicationModel extends APFObject { public function __construct(){ // Definiert den gerade anzuzeigenden View (login|welcome) $this->attributes['view.content.template'] = 'login'; // User-ID des aktuell eingeloggten Benutzers (null|user_id) $this->attributes['user.id'] = null; // Definiert die Methode des Logins (request|cookie|session) $this->attributes['login.mode'] = 'request'; // Zeigt, ob ein fehlgeschlagener Login vorliegt $this->attributes['login.status'] = null; } }
Im Quellcode-Beispiel wird die private Klassenvariable $attributes als Container für Model-Attribute verwendet. Das bietet den Vorteil, dass diese später durch den <fcon:importdesign />-Tag ausgelesen und zur Gestaltung der GUI genutzt werden können. Details können in Kapitel 5 der Sektion Standard TagLibs eingesehen werden.


3.1. Action-Konfiguration

Um die Action als permanente Action registrieren oder über die URL ansprechen zu können, muss zunächst eine Konfigurationssektion angelegt werden. Hierzu kann folgendes Schema als Vorlage dienen:
APF-Konfiguration
[Login] FC.ActionNamespace = "my::module::biz::actions::login" FC.ActionClass = "LoginAction" FC.InputClass = "LoginInput" FC.InputParams = ""

3.2. Action-Implementierung

Die eigentliche Funktionalität der Action wird in der run()-Methode definiert. Folgender Code kann dazu eingesetzt werden:
PHP-Code
public function run() { $username = RequestHandler::getValue('Username'); $password = RequestHandler::getValue('Password'); $id = RequestHandler::getValue('ID'); // Model der Anwendung holen $Model = & $this->getServiceObject('my::namespace', 'ApplicationModel'); // SessionManager erzeugen $Session = new SessionManager('MyApplication'); // Fall 1: (keine direkte Benutzerinteraktion) // // a) Daten aus Session sind nicht leer und diese können erfolgreich // validiert werden. // b) Daten sind nicht in der Session enthalten. // if (!isset($_REQUEST['Login'])) { // Fall 1.1: Session-Daten auslesen und prüfen $Username = $Session->loadSessionData('Username'); $Password = $Session->loadSessionData('Password'); if (!empty($Username) && !empty($Password)) { if ($this->validateCredentials($Username, $Password)) { // Fall 1.1.1: Daten aus Session gewonnen und erfolgreich eingeloggt $Model->setAttribute('view.content.template', 'content'); } else { // Fall 1.1.2: Daten aus Session gewonnen und diese sind nicht valide $Model->setAttribute('view.content.template', 'login'); } } else { // Fall 1.2: Keine Daten aus Session erhältlich $Model->setAttribute('view.content.template', 'login'); } } else { if (myValidator::validateText($username) && myValidator::validateText($password)) { // Falls 2.1: Versuch eines Logins if ($this->validateCredentials($username, md5($password))) { // Fall 2.1.1. Login-Vorgang war erfolgreich $Model->setAttribute('view.content.template', 'content'); } else { // Fall 2.1.2: Login-Vorgang war auf Grund falscher Benutzerdaten NICHT erfolgreich $Model->setAttribute('view.content.template', 'login'); $Model->setAttribute('login.status', 'failed'); } } else { // Fall Falls 2.2: Formular wurde unvollständig ausgefüllt $Model->setAttribute('view.content.template', 'login'); } } // Fall 3: (Logout) // if ($this->input->getAttribute('action') == 'logout') { // Session-Daten löschen $Session->destroySession('MyApplication'); // Login-Formular erzeugen $Model->setAttribute('view.content.template', 'login'); } }
Die im Code-Beispiel verwendeten Framework-Komponenten wie SessionManager, RequestHandler und Validator müssen natürlich zuvor per
PHP-Code
import('my::namespace','ApplicationModel'); import('core::session','SessionManager'); import('tools::request','RequestHandler'); import('tools::validator','myValidator');
eingebunden werden. Die private Methode validateCredentials() kapselt die Prüfung der Login-Daten mit Hilfe einer Business-Komponente.

Um die Model-Informationen über die Session hinweg vorzuhalten, kann das Model auch SESSIONSINGLETON erzeugt werden. Damit ist es möglich, die Action nur beim Login bzw. Logout aufrufen zu müssen, da die Login-Informationen ohnehin über die Session zur Verfügung stehen. Um dieses Verhalten zu erzeugen muss das Model per
PHP-Code
$model = &$this->getServiceObject('my::namespace', 'ApplicationModel', APFService::SERVICE_TYPE_SESSION_SINGLETON);
erzeugt werden.


3.3. Input-Definition

Im Fall der Login-Prüfung sind keine weiteren Parameter-Definitionen im Input-Objekt notwendig. Als Input-Klasse kann daher eine leere, von FrontcontrollerInput angeleitete Klasse, dienen (siehe 2.3.).


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.