Hallo, ich heiße Matthias Zeis - schön, dass Sie den Weg zu meiner Übersetzung von ZFSTDE gefunden haben!
Falls Sie mehr über Magento 2 oder meine Arbeit als Technical Lead bei LimeSoda erfahren möchten, kommen Sie doch auf meiner Website vorbei.
Inhaltsverzeichnis
Es ist zu einer Tradition in Programmierbüchern geworden, das allereinfachste Beispiel vorzustellen, egal welche Programmiersprache, welches Framework oder welche Bibliothek behandelt wird. Üblicherweise heißt das, dass gerade genug getan wird, um "Hallo Welt" auf dem Bildschirm auszugeben, damit man die grundlegende Funktionsweise kennenlernen kann. Das ist für Frameworks nicht gerade das realistischste Beispiel, aber es ist genau so ein guter Startpunkt wie jeder andere auch!
Bevor wir uns auf den Quelltext stürzen, sei allgemein empfohlen, bei der Entwicklung eine Umgebung zu verwenden, die der Produktionsumgebung möglichst nahe kommt. Ich werde in diesem Buch Anwendungen zumeinst innerhalb des "Document Root" des Webservers und nicht in einem Unterverzeichnis entwickeln. Sie können ein Unterverzeichnis verwenden, aber das wird etwas mehr Arbeit erfordern, wenn URI-Referenzen innerhalb der Anwendung angelegt werden - darauf gehe ich später genauer ein.
Fürs erste werfen Sie
einen Blick in Anhang A:
Eine lokale Domain mittels Apache Virtual Hosts erstellen
, der
erklärt, wie Sie für dieses Beispiel eine lokale Domain mit einem eigenen
Document Root anlegen. Mit dieser Domain und einem Apache Virtual Host
können wir das Beispiel auf unserem Entwicklungssystem von der Domain http://helloworld.tld
aus
ausliefern.
Als nächster Punkt auf
unserer Liste steht die Verzeichnisstruktur des Beispielprojekts. Zuerst
legen Sie ein neues Verzeichnis an, das (ausgenommen das führende
Unterverzeichnis /public
) dem Pfad entspricht, das
Sie vorher als Document Root für den helloworld-Virtual-Host angegeben
haben. Hier werden alle unsere Projektdateien gespeichert. Unter Ubuntu
könnte ich also das Verzeichnis in
/home/padraic/www/helloworld
erstellen oder unter
Windows in C:\projects\helloworld
.
Es gibt immer zahlreiche
Diskussionen darum, wie die Verzeichnisstruktur aussehen soll, aber vieles
davon wird jetzt unter der Führung von Ralph Schindler durch die laufenden
Bemühungen rund um Zend_Tool
formalisiert. Daraus
wird ein stabiles, vollständiges Kommandozeilen-Werkzeug zum Erzeugen und
Manipulieren von Projekten entstehen. Solange es nicht richtig stabil und
dokumentiert ist, werden wir es hier nicht verwenden, also ist etwas
Routinearbeit nötig, um die Verzeichnisstruktur von Hand anzulegen.
Das ist mein Vorschlag für die Verzeichnisstruktur. Sie ist aber keinesfalls verpflichtend - Sie können sie ganz Ihren Wünschen und anderen Anwendungen entsprechend anpassen. Für unser Beispiel brauchen wir zum Start nur einen Teil dieser Struktur.
Wie Sie sehen können,
wird der gesamte Quelltext für Controller und Views im Verzeichnis
/application
abgelegt. Controllers und Views haben
frameworkspezifische Konventionen, was das Laden anbelangt; daher macht es
Sinn, dieser Konvention zu folgen. Hier speichern wir auch Module, die
eigentlich eigenständige Sammlungen von Controllern und Views sind (und
optional Models sowie anwendungsspezifische, nicht wiederverwendbare
Helfer). Von der Dokumentation sind Sie vielleicht gewohnt, hier ein
Verzeichnis /application/models
zu sehen. Ich
verwende ein solches momentan nicht, da Models so unglaublich vielseitig
in ihrer Implementierung sind, dass eine einzelne, eindeutige
Ladekonvention für Models nicht existiert. Deswegen behandle ich Models
als Teil meiner allgemeinen Anwendungsbibliothek,
/library
, gemeinsam mit anderen für diese Anwendung
spezifischen Klassen. Es ist aber trotzdem möglich, Models in
/application/models
abzuspeichern. Sie ziehen es
vielleicht vor, Models an einem spezifischer benannten Ort zu speichern,
aber denken Sie daran, dass jedes weitere Verzeichnis zum Speichern von
Quelltext einen weiteren Eintrag in Ihrem include_path
verlangen könnte. Später in diesem Buch werden wir mit Models arbeiten,
die so wie im Referenzhandbuch beschrieben abgespeichert werden, da es uns
die Einführung von Zend_Loader_Autoload
ermöglicht,
in dieser Hinsicht flexibler zu sein.
Alles andere existiert
auf der übergeordneten Verzeichnisebene. Der ganze zukünftige
Anwendungscode außerhalb von Controllern, Views und nicht
wiederverwendbaren Klassen wird entweder im Verzeichnis /library
oder /vendor
abgelegt. Im Verzeichnis
library speichere ich alle allgemeinen Klassen, die in dieser Anwendung
verwendet werden. Das Verzeichnis vendor ist allgemein für
nicht-spezifische Klassen oder Third-Party-Bibilotheken da, die ich
verwenden möchte. Die Unterscheidung zwischen diesen beiden ist rein
willkürlich - ich trenne einfach gerne meine eigenen Klassen und
Bibliotheken von Dritten. Ich lege auch Zend Framework dort ab, falls es
nicht bereits an einer zentraleren Stelle des Servers abgelegt wurde und
über include_path
erreichbar ist.
Die restlichen
Verzeichnisse bergen keine Geheimnisse. Das Verzeichnis "config"
beinhaltet die Konfigurationsdateien. Nach jüngsten Tendenzen könnte es
auch unter /application/configs
zu finden sein. Im
Verzeichnis "data" können Daten in Dateien abgespeichert werden, zum
Beispiel Caches oder andere Informationen. Das Verzeichnis public enthält
alle Dateien, die für einen Besucher unserer Website erreichar sind. Das
Verzeichnis scripts schließlich beherbergt alle Skripts zum allgemeinen
Brauch, zum Beispiel solche, die mittels cron aufgerufen werden.
Unter Bootstrapping ist
zu verstehen, dass wir für unsere Anwendung die Konfiguration der Umgebung
und des Zend Framework sowie dessen Initialisierung vornehmen. Diese Datei
zu erstellen ist nicht schwer, doch mit zunehmender Komplexität der
Anwendung wächst sie immer weiter an. Um die Ordnung aufrecht zu erhalten,
empfehle ich wärmstens, den Bootstrap als Klasse mit handlichen,
übersichtlichen Methoden zu erstellen. Die Aufgaben des Bootstrap in
Methoden zu zerlegen, wird Wunder für Ihre (seelische) Gesundheit
bewirken! Später in diesem Buch werden wir die Lösung von Zend Framework
für dieses Komplexitätsproblem kennenlernen:
Zend_Application
, das mit Zend Framework 1.8
eingeführt wurde.
Da es sich beim
Bootstrap um eine Klasse handelt, ist der offensichtlich geeignete Ort für
die Aufbewahrung /library
. Natürlich können Sie ihn
in /application
ablegen, aber das würde einen
weiteren Include-Path verlangen (außer Sie verwenden
Zend_Application
, das den absoluten Pfad zur Klasse
verwendet), und wenn wir kreuz und quer Klassen ablegen, landen wir
irgendwann beim include_path aus der Hölle! Wenn wir annehmen, dass Sie
vorerst nicht Zend_Loader_Autoloader
verwenden,
dann vermeiden wir zwei Einträge in include_path, indem wir Models und die
Bootstrap-klasse innerhalb von /library
ablegen. Wir
werden den neuen Bootstrap als anwendungsspezifische Datei im Namespace
ZFExt abspeichern, was heißt, dass die Klasse unter
/library/ZFExt/Bootstrap.php
zu finden sein
wird.
<?php
/**
* Verwenden Sie einen absoluten Pfad, falls Sie ZF innerhalb von /vendor ablegen.
*/
require_once 'Zend/Loader.php';
class ZFExt_Bootstrap
{
public static $root = '';
public static $frontController = null;
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_startup_errors', true);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
self::$root = realpath('..');
define('APP_ROOT', self::$root);
spl_autoload_register(array(__CLASS__, 'autoload'));
}
public static function autoload($class) {
include str_replace('_', '/', $class) . '.php';
return $class;
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
realpath(self::$root . '/application/controllers')
);
$response = new Zend_Controller_Response_Http;
$response->setHeader('Content-Type',
'text/html; charset=UTF-8', true);
self::$frontController->setResponse($response);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$view->doctype('XHTML1_STRICT');
$view->headMeta()->appendHttpEquiv('Content-Type',
'text/html;charset=utf-8');
$viewRenderer =
new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->sendResponse();
}
}
Tja, nun erkennen Sie wahrscheinlich, warum dieses Kapitel das gar nicht so simple Tutorial ist!
Obwohl Sie sich
vermutlich gerade erst im Lernprozess befinden, ist der obige Bootstrap
weitaus komplexer gestaltet als das minimale Beispiel, das Sie im
Referenzhandbuch finden. Ich habe im Code oben zwei Dinge getan. Erstens
habe ich den Bootstrap als Klasse mit getrennten Methoden für verschiedene
Bootstrap-Aufgaben und -Abschnitte aufgebaut. Zweitens habe ich
Standardeinstellungen für alles vorgenommen, was von der Anwendung
ausgegeben wird, indem ich Einstellungen von
Zend_View
(das die Ausgabe der Anwendung mittels
des Einsatzes von Templates generiert) und
Zend_Controller_Response_Http
(das sich um die
Header und die Abläufe beim tatsächlichen Senden der Antworten an den
Client kümmert) modifiziert habe.
Die ersten
Setup-Abschnitte sind ziemlich einfach zu verstehen. Unser Setup der
Umgebung, welches in Zukunft durch die Konfiguration gesteuert werden
sollte, bereitet PHP mit einigen Einstellungen auf die Anwendung vor:
gesetzt werden das Level für die Anzeige von Fehlermeldungen,
Informationen zur Zeitzone (wird nicht benötigt, falls sie in php.ini
definiert wurde) und eine autoloader-Funktion, so dass wir
require_once
nicht in unserem Quelltext verwenden
müssen. Der angepasste Autoloader steht dem häufiger verwendeten
Zend_Loader
gegenüber, doch er ist wesentlich
schlanker als die Implementierung von Zend_Loader
,
die eine Menge normalerweise unnötiger Schritte durchführt (siehe Anhang
B
). Das momentane Umgebungs-Setup ist auf die Entwicklung
ausgerichtet, daher werden Fehler angezeigt.
Der nächste Schritt in
der Methode prepare()
bereitet den
Front-Controller vor. Wie erwähnt existiert in Model-View-Controller
üblicherweise ein einziger Eintrittspunkt in die Anwendung, eine Stelle,
die alle Anfragen passieren müssen. In Zend Framework ist das der
Front-Controller, der durch die Klasse
Zend_Controller_Front
repräsentiert wird. Zends
Front-Controller-Klasse ist ein Singleton (ihr lästigstes "Feature" -
Singletons sollten vermieden werden, falls sie nicht unbedingt nötig
sind). In der Methode setupFrontController()
setzen wir einige Flags, um sicherzustellen, dass der Front-Controller ein
Response-Objekt zurückgibt, anstatt einfach die Antwort auszugeben, ohne
dass wir darauf Einfluss nehmen können, und dass er es ermöglicht,
Ausnahmen sichtbar zu werfen, anstatt nach einem
ErrorController
zu suchen, der zu diesem Zeitpunkt
nicht existiert. Der nächste wichtige Schritt ist dem Front-Controller
mitzuteilen, wo er die Controller und Views unserer Anwendung
findet.
Der letzte Schritt, in
dem wir eine eigenständige Instanz des Request-Objekts (z.B.
Zend_Controller_Http_Response
) erzeugen, existiert,
um einen standardmäßigen Content-Type-Header für die Rückmeldungen der
Anwendung zu setzen. Das garantiert, dass alle Ausgaben - falls nicht
anders festgelegt - dem Client automatisch einen Content-Type-Header
"text/html" mit dem Zeichensatz UTF-8 senden. Mit dieser bewährten
Vorgangsweise vermeiden wir potentielle Unklarheiten in Fällen, in denen
wir die Werte nicht explizit in der Anwendung setzen. Wird dieser Wert
nicht festgelegt, wird der standardmäßige Content-Type aus der php.ini
verwendet.
Sind die Umgebung und
der Front-Controller einmal definiert, fügen wir im letzten Schritt
ähnliche Standardwerte für unsere Views hinzu. Momentan verwendet
Zend_View
im völligen Gegensatz zu anderen
Komponenten ISO-8859-1 als Standardzeichensatz (was auch View-Helfer tun),
was nicht funktionieren wird, wenn Sie Multibyte-Zeichen ausgeben (wie den
in meinem Namen!). Dieser ärgerliche und schon lange bestätigte Bug wird
beibehalten, um Problemen mit der Rückwärtskompatibilität bei älteren
Zend-Framework-Versionen vorzubeugen. Um diese Standardeinstellung zu
ändern, sollten wir eine neue Instanz von Zend_View
erzeugen, als angemesseneres Encoding UTF-8 einstellen und dieses
veränderte View-Objekt dem ViewRenderer-Action-Helfer einpflanzen. Dieser
Helfer wird automatisch von allen Controllern verwendet, um Views zu
erfassen und verwalten. Indem wir das zu verwendende View-Objekt explizit
festlegen, vermeiden wir, dass wir die unmodifizierte View-Instanz
verwenden müssen. Unter der Annahme, dass wir standardmäßig HTML ausgeben,
ist es auch stark anzuraten, einen standardmäßigen HTML-Doctype
festzulegen, da diese Einstellung auch von anderen Komponenten (z.B.
Zend_Form) übernommen wird, wenn diese gerendert werden. Auch hier kann es
ärgerlich sein, falls Sie darauf vergessen - zum Beispiel, wenn Formulare
unter Einsatz eines anderen Doctype generiert werden. Klarerweise
bevorzugen wir es, wenn alles, was als HTML gerendert wird, dem von der
View bevorzugten DOCTYPE entspricht.
Wie Sie sehen können,
ist das Schreiben eines Bootstrap ein wenig wie Krieg führen. Der
Bootstrap überlebt den ersten Kontakt nie und Sie werden sich ständig
dabei ertappen, wie Sie von Zeit zu Zeit Pfade, Konfigurationen und andere
Optimierungen hinzufügen. Das obige Beispiel stellt eine mögliche
Grundlage dar, aber im nächsten Kapitel lernen wir
Zend_Application
kennen, das einen
standardisierteren Ansatz ermöglicht.
Wir haben einen
Bootstrap parat, aber er ist nur nützlich, wenn wir ihn auch wo ausführen.
Wie bereits besprochen haben alle Model-View-Controller-Anwendungen (MVC)
einen einzigen Eintrittspunkt in die Anwendung. Im Fall von PHP ist das
unausweichlich eine Datei namens index.php
, die
Index-Datei.
Da diese Datei von
nahezu jedem Webserver mit PHP-Unterstützung automatisch geladen wird,
wenn kein Pfad zu einer anderen Datei angegeben wurde, ist das der Ort, an
dem unsere Anwendung zuerst geladen wird. Das ist auch die Stelle, an der
wir manuelle Änderungen an include_path vornehmen, die für die lokale
Serverumgebung notwendig sind (für Pfade, die unser Bootstrap nicht durch
den Autoload-Mechanismus laden kann). Da ich aber nicht vorhabe, ohne
gewichtigen Grund gegen die PEAR-Konventionen zu verstoßen, müssen wir in
index.php
nichts anderes tun, als den Bootstrap zu
starten!
Erstellen Sie im
Verzeichnis /public
Ihres Projekts eine Datei
index.php mit folgendem Inhalt:
<?php
require realpath('..') . '/library/ZFExt/Bootstrap.php';
ZFExt_Bootstrap::run();
Falls Sie Bibliotheken mit nicht-konventionellen Include-Pfaden verwenden, müssen Sie diese Include-Pfade hier hinzufügen oder sie über die Bootstrap-Klasse konfigurieren. Momentan kann aber alles, was wir verwenden, automatisch vom Bootstrap geladen werden.
Ein kleines Problem
haben wir mit unserem theoretisch einzelnen Eintrittspunkt - HTTP
funktioniert in der Praxis anders. Wie können wir vermitteln, welchen Teil
der Anwendung wir erreichen wollen, wenn die einzig erreichbare Datei
index.php
ist? Wir müssen in der Lage sein, eine URL
zu verwenden, die standardmäßig der Form
http://domain.tld/controllername/actionname
folgt, damit der
Router der Anwendung die korrekte Action-Methode der korrekten
Controller-Klasse aufrufen kann. Dieser Ansatz ist auf jedem Webserver zum
Scheitern verurteilt, da der Server glauben wird, dass wir ein tatsächlich
unter dieser URL existierendes Element zu erreichen versuchen, zum
Beispiel das Verzeichnis controllername/actionname/
.
Um das zu vermeiden, müssen wir die URL an index.php
weitergeben und dem Webserver zu verstehen geben, dass wir tatsächlich
alle Anfragen über index.php
laufen lassen
wollen.
Zum Glück bieten Webserver eine Funktionalität namens URL-Rewriting
an. Sie ermöglicht es uns, alle URLs (die bestimmte Kriterien erfüllen) in
eine neue URL zu transformieren, die auf index.php
abgebildet wird und so eine endgültige URL entsprechend dem Muster
http://domain.tld/index.php/controllername/actionname
erzeugt. Klarerweise muss dieses Kriterium alle URLs zu Ressourcen wie
unseren Stylesheets, Javascripts und Bildern aussparen, da wir diese nicht
als Parameter an die Anwendung übergeben wollen. Diese Dateien müssen
immer direkt ausgeliefert werden.
Wenn Sie Apache als Webserver verwenden, müssen Sie eventuell URL-Rewriting einrichten, indem Sie das Rewrite-Modul aktivieren. Dieses Modul ist bei vielen neu installierten Apache-Instanzen standardmäßig deaktiviert. Um das Modul zu aktivieren, müssen Sie die Konfigurationsdatei von Apache editieren oder auf Ubuntu einfach den (ziemlich praktischen) a2enmod-Befehl verwenden.
Wenn Sie Ubuntu einsetzen, dann versuchen Sie Folgendes:
sudo a2enmod rewrite
Der Befehl nutzt einen Symlink, um die Rewrite-Konfigurationsdatei hinzuzufügen, damit sie als Teil der Gesamtkonfiguration von Apache automatisch geladen wird. Sie müssen Apache neu laden, damit Änderungen an Modulen übernommen werden. Das wird unter Ubuntu mit dem folgenden Befehl erreicht:
sudo /etc/init.d/apache2 reload
Falls Sie Ihre Konfigurationsdatei manuell bearbeiten müssen, müssen Sie normalerweise nur etwas wie die folgende Zeile hinzufügen (Apache danach neu laden):
LoadModule rewrite_module modules/mod_rewrite.so
Falls Sie nicht Apache
verwenden, konsultieren Sie bitte den Handbuchabschnitt zu
Zend_Controller
, um Anweisungen speziell für Ihre
Installation ausfindig zu machen.
Um von URL-Rewriting
Gebrauch zu machen, fügen Sie die folgende Datei namens
.htaccess
in /public
hinzu:
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ /index.php [NC,L]
Falls Sie planen, Ihre Anwendung in einem Unterverzeichnis zu
hosten, sollten Sie auch eine RewriteBase
-Zeile hinzufügen um
sicherzugehen, dass die Regeln diesen Teil der URL ausnehmen und nur den
Pfadteil nach diesem Punkt heranziehen, wenn die URL für
index.php
umgeschrieben wird.
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteBase /subdirectory/ RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ /index.php [NC,L]
Die Datei
.htaccess
stellt einige Rewrite-Regeln auf um
sicherzugehen, dass alle entsprechenden Anfragen auf die Index-Datei
unserer Anwendung geleitet werden. Dabei sind die Regeln so formuliert,
dass alle URLs außer jenen berücksichtigt werden, auf welche die
vorangegangenen Rewrite-Konditionen zutreffen. Die Konditionen nehmen URLs
aus, die auf eine Datei (mit einer Dateigröße größer als 0), ein
existierendes Verzeichnis oder einen symbolischen Link verweisen. Dadurch
ist gesichert, dass Ihre JavaScript-, CSS- und andere Nicht-PHP-Dateien
direkt ausgeliefert werden können, selbst wenn sie woanders vorgehalten
werden und nur durch symbolische Links referenziert werden.
Als wir Model-View-Controller (MVC) diskutiert haben, haben wir angemerkt, dass der Controller dafür verantwortlich ist, die Verbindung zwischen Benutzereingaben und dem Model bzw. den Models und der View herzustellen. Der Controller ist daher eine der ersten Komponenten, mit denen Sie sich auseinandersetzen müssen, wenn Sie ein neues Feature zu einer Anwendung hinzufügen.
In Zend Framework sind
alle Controller letztlich Subklassen von
Zend_Controller_Action
. Diese Basisklasse stellt
einige Grundregeln auf, die Sie in Erinnerung behalten sollten. Zum
Beispiel wurde festgelegt, dass standardmäßig alle Controller automatisch
Views rendern. Das bedeutet, dass Sie sich nicht manuell darum kümmern
müssen, dass View-Templates gerendet werden - stattdessen wird ein
Action-Helfer mit dem Namen View-Renderer
(Zend_Controller_Action_Helper_ViewRenderer
)
aufgerufen. In unserer Bootstrap-Klasse passten wir das Standardverhalten
an, indem wir sicherstellten, dass der View-Renderer eine
Zend_View
-Instanz mit dem Standardzeichensatz UTF-8
(statt ISO-8859-1) verwendet. Im Referenzhandbuch ist dokumentiert, wie
das automatisierte Rendern von Views deaktiviert werden kann. Dies ist
häufig nützlich, wenn Sie Zend_View umgehen müssen (zum Beispiel, um JSON
oder XML-Dokumente auszuliefern, die nicht templatebasiert sind und keine
weitere Verarbeitung benötigen).
Der Router
(Zend_Controller_Router_Route
und zugehörige
Klassen) ist dafür verantwortlich festzustellen, welcher Controller
benutzt werden soll. Er trifft die Entscheidung anhand des URL-Strings,
welcher entweder den Klassennamen des Controllers sowie den Namen der
Methode enthält oder einer konfigurierten Route entspricht, die
standardmäßige Controller- und Action-Namen liefert. Fehlt diese
Information, geht der Router jeweils vom Standardwert "index" aus. Da wir
nur vorhaben, die Root-URL http://helloworld.tld
anzufragen,
werden wir einen Index-Controller mit einer Index-Action benötigen, also
einen Controller, der die Standardannahmen des Routers erfüllt, wonach die
URL gleichbedeutend mit http://helloworld.tld/index/index
ist. Hier ist der Quelltext für
/application/controllers/IndexController.php
:
<?php
class IndexController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
}
}
Die Methode
init()
können Sie ignorieren, da sie nur für
Funktionen benötigt wird, die beim Setup des Controllers ausgeführt werden
müssen - eine solche Funktion brauchen wir hier nicht. Wichtiger ist die
Methode indexAction()
. Sie ist leer, was kein
Problem ist, da wir in diese Methode nur etwas hineinschreiben müssen,
wenn wir mit einem Model oder anderen Klassen interagieren müssen.
Momentan ist das nicht der Fall. Die Methode dient nur als eine
Stub-Methode, die dem ViewRenderer-Action-Helfer mitteilt, dass er eine
View mit einem ähnlichen Namen rendern soll.
Um das Mapping zu
erklären: ein Controller "Index" mit einer Methode
indexAction()
wird auf ein View-Template
umgelegt, das unter
/application/views/scripts/index/index.phtml
zu
finden ist. ErrorController::indexAction()
würde
demnach zu
/application/views/scripts/error/index.phtml
führen
und so weiter. Es handelt sich um eine simple Konvention. Lassen Sie uns
ein Template für IndexController::indexAction()
in /application/views/scripts/index/index.phtml
erstellen:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
In Zend Framework
befinden sich Templates innerhalb des Geltungsbereichs des
Zend_View
-Objekts. Somit sind alle Eigenschaften
und Methoden, die für Zend_View
zugänglich sind,
auch für Templates erreichbar, indem $this
verwendet wird (um
auf das aktuelle Zend_View
-Objekt zu referenzieren,
dessen Variablenbereich gerade gültig ist).
Bei diesem Code handelt
es sich um ein hundertprozentig valides Template, das auch funktioniert,
aber später werden wir das Konzept der View-Helfer genauer kennenlernen.
Kurz gesagt kapselt ein View-Helfer häufig benötigte,
präsentationsbezogene Funktionen in Helfer-Klassen. Wollen wir zum
Beispiel den Doctype-String zu dutzenden Templates hinzufügen? Wenn wir
das machen und sich der Doctype ändert, müssen wir dutzende Templates
editieren. Nein, wir wollen es nicht. Zend_View
besitzt einen Helfer, den wir im Bootstrap verwendet haben, um einen
Standard-Doctype festzusetzen. Genauso, wie wir im Bootstrap einen
weiteren Helfer verwendet haben, um einen http-equiv-Header mit einem
Standard-Content-Type zu den Meta-Informationen des Headers hinzuzufügen.
Mittels dieser zwei Helfer, deren Inhalt direkt über echo ausgegeben
werden kann, weil sie indirekt die magische PHP-Methode
__string()
implementieren, können wir das
Template auf den folgenden Code reduzieren:
<?php echo $this->doctype() ?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php echo $this->headMeta() ?>
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
Nun haben wir "en" noch an drei verschiedenen Stellen eingetragen. Wir könnten das ebenfalls mit einem View-Helfer automatisieren, der sicherstellt, dass der Wert entsprechend der Sprache der Inhalte angepasst wird. Das demonstriert, warum View-Helfer nützlich sind - wir können Funktionen für Änderungen an der View in sie auslagern, damit sie vielfach über viele verschiedene Views hinweg wiederverwendbar sind. Es ist nicht wichtig, dass Sie View-Helfer schon jetzt komplett verstehen. Sie müssen nur wissen, dass sie existieren!
Editieren Sie nun
IndexController.php
, um der View den Titel der Seite
zu übergeben (im View-Skript als $this->title
referenziert).
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Hello World!';
}
}
Alle Controller müssen
Zend_Controller_Action
erweitern, da die Klasse
alle später referenzierten internen Methoden und Eigenschaften enthält,
die allen Controllern gemein sind. Falls Sie den Basiscontroller
optimieren müssen, um neue Methoden oder Eigenschaften einzuführen, können
Sie das machen, indem Sie eine Subklasse von
Zend_Controller_Action
erstellen und Sie alle
Anwendungscontroller stattdessen von dieser Subklasse ableiten. Seien Sie
aber sehr vorsichtig, wenn Sie auf diese Weise vorgehen - es ist
praktisch, der Klasse so neue Eigenschaften hinzuzufügen, aber oft ist es
besser, zusätzliche Methoden als eigenständige Action-Helfer hinzuzufügen,
um der Entwicklung einer übergeordneten Controller-Klasse vorzubeugen, die
kaum mehr ist als ein schwer wartbares Knäuel von kaum zueinander in
Verbindung stehenden Spaghetti-Nudeln.
Unsere neue Action-Methode ignoriert das Rendering; das passiert wie oben erwähnt automatisch über den standardmäßig aktivierten View-Renderer. Alles was wir zun müssen ist, die benötigten View-Daten zu definieren. Auf diese Weise können Sie fast alles zur View hinzufügen, da es schlicht eine Eigenschaft in einem View-Template wird. Tun Sie sich keinen Zwang an, Objekte und Arrays hinzuzufügen. Falls Sie Smarty oder PHPTAL verwenden, ist das vielleicht anders. Dies ist - wie ebenfalls weiter oben besprochen - auf das Konzept zurückzuführen, laut dem Views Models verwenden. Ein Model ist üblicherweise ein Objekt, also gibt es keinen Grund, Views auf einfache Arrays zu beschränken, wenn es wesentlich angenehmer ist, einfach ein Objekt zu übergeben.
Oben haben wir der View
mitgeteilt, dass Templates eventuell eine lokale, öffentliche
Klasseneigenschaft "title" verwenden werden und haben dieser den Wert
"Hello, World!" zugewiesen. Wenn die Action-Methode abgewickelt ist, kommt
der View-Renderer ins Spiel und versucht das Template in der Datei
/application/views/scripts/index/index.phtml
zu
rendern.
Innerhalb des
View-Scripts maskieren (escapen) wir den Titel, da wir nicht davon
ausgehen können, dass er harmlos ist. Es ist eigentlich obligatorisch,
alles so zu maskieren, wenn es als HTML ausgegeben wird. Intern verwendet
Zend_View
standardmäßig
htmlspecialchars()
und gibt der Funktion die
Character-Encoding-Einstellung der aktuellen Instanz mit. Auch das ist
eine dieser kleinen Ärgernisse - in Zend Framework 2.0 wird die Maskierung
automatisch vorgenommen, ohne dass ständig die Methode
escape()
aufgerufen werden muss.
In diesem Kapitel haben wir ein ziemlich einfaches (aha!) Beispiel behandelt, wie Zend Framework zur Erstellung einer Anwendung verwendet wird. Es betont auch, wie wichtig Bootstraping ist, da hier fast das komplette Setup der Anwendung erfolgt, inklusive des Setzens von Standardwerten und Konfigurationseinstellungen für Zend-Framework-Komponenten. Über das gesamte Buch hinweg erfolgt die Verwaltung eigener Plugins, Action-Helfer und anderer nicht standardmäßig zum Zend Framework gehörenden Klassen über den Bootstrap, da es sich um den logischsten Ort handelt, um ihre Konfiguration und Einbindung in eine Anwendung vorzunehmen.
Im nächsten Kapitel
beschäftigen wir uns mit etwas, was ich - wie jedem, der über die neueren
Versionen von Zend Framework informiert ist, klar aufgefallen sein wird -
ausgespart habe: wie man die Komplexität des Bootstrapping mittels
Zend_Application
bändigt. Danach erkläre ich, wie
man mit Anwendungsfehlern umgeht. Bald danach tauchen wir in eine
tatsächliche Anwendung ein, da ich unbedingt einen Ersatz für mein Blog
benötige, und so wird das die einfache Anwendung sein, andhand der wir
andere Zend-Framework-Komponenten genauer erforschen werden.