Kapitel 10. Erstellung des Designs mit Zend_View, Zend_Layout, HTML 5 und der Yahoo! User Interface Library

Inhaltsverzeichnis

10.1. Einleitung
10.2. Zend_View: objektorientiertes Templating
10.2.1. Layouts
10.2.2. Partials
10.2.3. View-Helfer
10.2.4. Platzhalter
10.2.5. Kurze PHP-Tags vs. lange PHP-Tags
10.3. Einrichtung der ZFBlog-Applikation
10.4. Eine Indexseite mit HTML 5 erstellen
10.5. Statisches Markup ins Layout übernehmen
10.6. Veränderbare Elemente durch Platzhalter ersetzen
10.7. Erweitern der Unterstützung von HTML 5 durch eigene View-Helfer
10.8. Einen Link zu einem eigenen Stylesheet hinzufügen
10.9. Das Design anpassen
10.10. Fazit
[Wichtig] Wichtig

Dieses Kapitel befindet sich noch im Entwurfsstadium. Der Inhalt kann sich ändern und neue Abschnitte können in den nächsten Tagen hinzugefügt werden. Ihr Feedback ist wie immer willkommen und kann zu jedem Absatz oder Abschnitt als Kommentar hinzugefügt werden. Auch Kritik (die ist sogar noch willkommener!) oder Vorschläge für zukünftige Aktualisierungen dieses Kapitels sind gefragt.

10.1. Einleitung

In diesem Kapitel werden wir ein simples Webdesign für unseren Blog erstellen. Das gehört eindeutig zur View-Schicht des Model-View-Controller-Architektur-Designpatterns (MVC). Auf einen einfachen Nenner gebracht werden wir vor allem HTML für die Auszeichnungen der Inhalte (das "Markup") und CSS für die Gestaltung des Blogs verwenden. Wie in vielen Frameworks wird das Markup in Form von Templates in unserer Anwendung gespeichert, die Templates werden am Ende jedes Requests gerendert und als Antwort an den Benutzer gesendet. Die Sache wird interessanter, wenn Sie bedenken, dass eine komplexe HTML-Seite in (bedingt) wiederverwendbare Elemente heruntergebrochen werden kann. Zum Beispiel hat vermutlich jede HTML-Seite wiederverwendbare Elemente wie einen Header, einen Footer, ein Navigationsmenü, eine Breadcrumb-Leiste etc. Neben diesen Elementen gibt es dynamische Inhalte und einige andere Elemente, die nur auf bestimmten Seiten benötigt werden. In anderen Fällen müssen Sie vielleicht spezielle Formatierungsregeln anwenden oder Daten aus der Datenbank formatiert ausgeben. Indem man Zend Frameworks Templatesystem mittels Zend_View einsetzt, kann man die Duplizierung von Code zum Bearbeiten von Markup und Templates vermindern. Ermöglicht wird das durch View-Helper ("View-Helfer"), Partials ("Partielle"), Placeholder ("Platzhalter") und Zend_Layout für die gebräuchlichsten Auszeichnungselemente wie <head>, <title>, <html> und andere Elemente wie Links zu CSS-Stylesheets oder Javascript-Dateien, die in die Seite eingebunden werden müssen.

Ich bin ein ziemlich schlechter Webdesigner (tut mir leid!), weswegen ich das Design für unseren Blog ziemlich einfach halten werden werde, damit es jemand anderer später verschönern kann. Für das Markup wird HTML 5 verwendet. HTML 5 ist die nächste größere Überarbeitung von HTML. Daher erschien es mir angemessen, mit dem neuen Standard etwas zu experimentieren. Da das XHTML-2.0-Projekt (zumindest vorerst) beendet wurde, handelt es sich auch um den nächsten größeren Pseudo-Ersatz für XHTML. Um das festzuhalten: HTML 5 kann technisch gesehen als HTML- oder XML-basierte Serialisierung ausgeliefert werden (das heißt: entweder als text/html oder application/xml+xhtml in Kombination mit den jeweils zutreffenden Syntaxregeln). Aufgrund der Browserunterstützung (IE akzeptiert den XML-Content-Type nicht) ist nur die HTML-Serialisierung wirklich verwendbar, wobei ich XHTML-Konventionen wie das Schließen aller Tags und die Verwendung von klein geschriebenen Attributen und Tag-Namen befolgen werde, da ich mit diesen am meisten vertraut bin.

Mein Einsatz von HTML 5 sollte als experimentell angesehen werden. Es liegt noch nicht in einer finalen Fassung vor und wird nicht von allen Browsern unterstützt. Für HTML 5 kommen Firefox 3.5, Internet Explorer 8 oder neuere Browserversionen in Frage - alles, was älter ist, unterstützt den Standard nur sehr lückenhaft. Allerdings ist HTML 5 bis zu einem gewissen Grad abwärtskompatibel zu HTML 4. Macht man sich an die Gestaltung der Seite, muss man Internet Explorer erst etwas gut zureden, damit alles funktioniert (IE erkennt die neuen HTML5-Elemente nicht und kann ihr Design im Gegensatz zu anderen Browsern nicht ändern), aber wir werden dieses Problem lösen, indem wir eine sehr nette kleine JavaScript-Bibliothek namens html5shiv verwenden.

Um uns die Gestaltung zu erleichtern, werde ich zudem das Yahoo User Interface (YUI) Library's CSS framework verwenden. Ich werde es einsetzen, um für alle Browser denselben Ausgangszustand herzustellen (die gleichen Schriftarten, Schriftgrößen, Abstände zwischen den Elementen und ihre Ränder etc.). Außerdem werde ich mittels der Grid-Implementierung die Erstellung der einzelnen Abschnitte der Seite vereinfachen. YUI CSS ist nur eines von vielen CSS-Frameworks. In der Vergangenheit habe ich Elastic und Blueprint CSS verwendet. Diese Frameworks dienen primär dazu, die Entwicklungszeit zu verkürzen und (hoffentlich) weniger CSS zu schreiben - für mich ein wichtiger Faktor, da meine Designkünste einiges zu wünschen übrig lassen und ich Stunden damit verbringen könnte, mit den CSS-Einstellungen zu spielen!

Ich mag es wirklich nicht, CSS-Code zu bearbeiten, wenn ich einen Affen mit einem großen Sack voller Erdnüsse dazu bringen könnte, diese Aufgabe besser zu erledigen. Oder einen Webdesigner (das wäre wahrscheinlich noch besser als ein Affe). Vorerst brauche ich einfach nur ein Standard-Design, das im Notfall als ganz ordentlich durchgeht. In diesem Artikel werden wir uns daran versuchen, ein Standard-Blog-Design aufzusetzen, es mit Inhalten zu befüllen und schließlich das Design mit Zend_View-Templates zu erfassen.

10.2. Zend_View: objektorientiertes Templating

Wir PHP-Progammierer haben Template-Engines seit dem Anbeginn von PHP verwendet. Eigentlich ist PHP selbst eine Template-Engine. Ursprünglich war das sogar der einzige Daseinszweck - man kann PHP sehr leicht in HTML oder andere Auszeichnungssprachen einbringen. Nicht so großartig waren wir allerdings im Unterscheiden zwischen der Präsentation (Auszeichnung / Formatierung) und der Präsentationslogik (kapselt die Logik, um Markup zu generieren, Elemente zu formatieren oder auf das Model zuzugreifen). Meistens bestand die Unterscheidung darin, PHP und HTML voneinander zu trennen, was das Ziel verfehlt , weil PHP so nur durch eine weitere, speziell angepasste Tag-Sprache ersetzt wird. Diese Strategie hatte nur eine gute Seite: Webdesigner wurden dadurch oft davon abgehalten, PHP selbst zu verwenden - zweifellos ein berechtigtes Anliegen. Es handelt sich um einen wesentlichen Bestandteil davon, wie PHP arbeitet. Der bedachte Einsatz als Teil eines Templating-Systems leitet uns nur in die richtige Richtung.

Zend_View ist die Template-Rendering-Lösung des Zend Framework und formalisiert das Konzept der Trennung von Präsentation und und Darstellungslogik. Es handelt sich um eine objektorientierte Lösung, die ohne ein Tag-basiertes System auskommt (wie es bei Smarty oder JavaServer Pages (JSP) in Java der Fall ist). Templates, die von Zend_View umgesetzt werden, verwenden stattdessen direkt PHP. Auf einer höheren Ebene kapselt Zend_View die View-Schicht einer Anwendung, die Benutzereingaben annimmt und eine Darstellung der Anwendung in Form von HTML, XML, JSON etc. ausgibt, je nachdem welche Daten ihr übergeben wurden oder welche sie selbstständig abgefragt hat. Genau genommen nimmt Zend_View keine Benutzereingaben an, da dies in der Controller-Schicht gehandhabt wird - aufgrund der Funktionsweise von HTTP gibt es hier eine undichte Stelle zwischen der View und den Controllern, sobald man MVC für Webapplikationen anstatt für Desktopanwendungen einsetzt. Überdies wirkt der Controller selbst bei der Präsentation mit. Er beschränkt sich strikt auf die aktuelle Anfrage und regelt die Erstellung sowie Ausgabe der Antwort.

Der wichtigste Aspekt der Verwendung von Templates mit Zend_View ist die Objektorientierung. Sie können in den Templates alle Wertetypen verwenden: Arrays, skalare Werte, Objekte und sogar PHP-Ressourcen. Es gibt kein zwischengelagertes Tagsystem, das sich zwischen Sie und den gewaltigen Funktionsumfang von PHP stellt. Teil dieses OOP-Ansatzes ist, dass alle Templates innerhalb des Variablen-Geltungsbereichs der aktuellen Zend_View-Instanz ausgeführt werden. Zur Erklärung sehen wir uns einmal das folgende Template an.

Wir sehen, dass PHP in diesem Template direkt verwendet wird. Wir sehen auch eine Referenz auf $this - es repräsentiert die aktuelle Objekt-Instanz. Aber das Template ist kein Objekt! Woher kommt "this"?

Templates werden in der geschützten Methode Zend_View::_run() eingebunden. Der evaluierte Inhalt des Templates wird dann mittels Output-Buffering abgefangen. Somit wird jeglicher Inhalt eines Templates als Code innerhalb von Zend_View::_run() behandelt, was heißt, dass Sie auf alle Klassenmethoden, Eigenschaften (wie die öffentliche Eigenschaft Zend_View::$myName) und andere Features zugreifen können, als ob Sie eine Methode in Zend_View selbst schreiben würden (was Sie effektiv auch tun!).

Wie beeinflusst das unser Denken über die View? Da alle Templates im Endeffekt (in der einen oder anderen Form) Methodenaufrufe sind, sind Templates indirekt offen für all die Vorteile, die sich aus der objektorientierten Programmierung ergeben. Templates können verschachtelt, wiederholt, refaktoriert und durch Komposition in andere Objekte (View-Helfer) gekapselt werden. Dies wird offensichtlicher, sobald Sie die verschiedenen Konzepte kennenlernen, die von Zend_View angeboten werden: View-Helfer, Placeholder und Layouts. Einleuchtend ist es auch durch unsere bisherigen Diskussionen rund um die Rolle des Model. Wie ich in Kapitel 3: Das Model erwähnt habe, können Views mit Hilfe von View-Helfern direkt auf das Model zugreifen und es auslesen, ohne dass man Controller-Code benötigt, der diese Interaktion steuert.

10.2.1. Layouts

Das Konzept des Layouts wird durch Zend_Layout implementiert. Beim Layout handelt es sich um jenen Teil der Präsentation, der über viele (oder alle) Seiten relativ statisch bleibt. In einem Standard-HTML-Dokument trifft dies üblicherweise auf den Header, den Footer, das Navigationsmenü und andere Bereiche der Seite zu, die auf vielen Seiten ident sind. Da diese Teile in hohem Maße statisch sind, wäre es sinnlos, sie in jedem Seiten-Template zu duplizieren. Die Wartung wäre schlicht unmöglich, da für jede Änderung zahllose Templates bearbeitet werden müssten. Aus diesem Grund können wir Zend_Layout verwenden, um eine kleine Zahl an Layout-Templates zu erstellen, in welche die restliche Template-Ausgabe eingefügt wird. So wird sichergestellt, dass sich die restlichen Templates der Anwendung nicht mit dem gemeinsamen Layout herumschlagen müssen - diese Aufgabe wird an Zend_Layout abgegeben.

Ein einfaches Layout-Template könnte so aussehen:

  • <html>
  • <head>
  • <title>My Page</title>
  • </head>
  • <body>
  • <?php echo $this->layout()->content ?>
  • </body>
  • </html>

Wie in vorherigen Kapiteln erwähnt implementiert der Controller das Rendern der View standardmäßig, indem der ViewHelper-Action-Helfer verwendet wird. Zend_Layout registriert einen neuen Action-Helfer und ein FrontController-Plugin, Zend_Layout_Controller_Action_Helper_Layout und Zend_Layout_Controller_Plugin_Layout, die es erlauben, ein Layout zu rendern und von den Controllern aus auf die Layout-Instanz zuzugreifen. Wir werden uns später noch genauer mit Helfern und Plugins auseinandersetzen, aber vorerst müssen wir nur wissen, dass sie Hook-Methoden implementieren können, die vom FrontController automatisch aufgerufen werden, sobald bestimmte Ereignisse aufgetreten sind. In diesem Fall interessiert uns eine Methode postDispatch(), die aufgerufen wird, wenn eine Anfrage an den Controller übergeben wird. Die Ausgabe der View, die zu dem Controller (bzw. der Action) gehört, wird der öffentlichen Eigenschaft $content des Layout-Templates zugewiesen. Den Inhalt von $content können wir an einer beliebigen Stelle im Layout-Template ausgeben, in unserem Beispiel zwischen den <body>-Tags.

Zend_Layout ermöglicht Ihnen über den Action-Helfer auch, in Controllern wahlweise das Rendern des Layouts zu deaktivieren. Das ist dann wichtig, wenn Sie etwas anderes als HTML (zum Beispiel JSON) ausgeben. JSON oder XML in einem HTML-Layout auszugeben, ist vermutlich nicht in Ihrem Interesse. Sie können zudem das Layout austauschen oder im Controller andere Layout-Variablen setzen. Es handelt sich also um ein sehr flexibles System.

Was Designpattern anbelangt, implementiert Zend_Layout eine nicht allzu strikte Form der Two-Step-View, wie sie von Martin Fowler in Patterns Of Enterprise Application Architecture (POEAA) beschrieben wird. Fowlers Definition der Two-Step-View lautet wie folgt:

Turns domain data into HTML in two steps: first by forming some kind of logical page, then rendering the logical page into HTML.

In unserem Fall werden die Domain-Daten unübersehbar direkt in HTML gerendert, bevor sie an einer bestimmten Stelle in den HTML-Code des Layouts eingefügt werden. Die Verwendung des Two-Step-View-Ansatzes für das Layout ergänzt den Template-Kompositionsansatz von Zend_View, wie er unten bei der Verwendung von Partials beschrieben wird.

Ihnen sollte bewusst sein, dass Zend_Layout sich darum kümmert, wie die Zusammensetzung und Zuweisung des Layouts funktioniert, das Layout-Template selbst aber wieder von einer Zend_View-Instanz gerendert wird. Somit kann Ihr Layout Gebrauch von den typischen Zend_View-Delikatessen wie Partials, Placeholdern und View-Helfern machen. Das kann aber auch verwirrend sein und zu suboptimalen Lösungen führen, wie ich jetzt gleich erwähnen möchte.

Auf einen Aspekt bei Zend_Layout muss man nämlich aufpassen: wie im Referenzhandbuch steht, können Layouts und benannte Antwortsegmente in Verbindung mit dem ActionStack-Action-Helfer verwendet werden, um Seiten aus mehreren Widgets zusammenzusetzen. Das würde bedeuten, dass jede Action eine View rendert, die dann an der definierten Stelle in das Template eingefügt wird. Ein extrem wichtiger Punkt muss hierbei berücksichtigt werden - eine Aktion abzuarbeiten ist ein sehr teurer Vorgang. Wie der Name ActionStack suggeriert, erzeugt dieser Action-Helfer einen Stapel von Controller-Aktionen. Um diese abzuarbeiten, muss jeweils beinahe das gesamte MVC-Framework in Bewegung gesetzt werden. Das sollte unbedingt vermieden werden, falls es irgendwie möglich ist. Es gibt fast nie einen Grund, den ActionStack zu verwenden, da alles, was damit erreicht werden kann, genauso einfach mit View-Helfern erreicht werden, wobei nur ein Bruchteil der Performancekosten anfällt. Ryan Mauger hat dies genauer in seinem Blogpost Why the Zend Framework Actionstack is Evil erklärt. Das soll nicht heißen, dass Sie diesen Aspekt des Zend Framework nie verwenden sollten, aber der Einsatz muss sehr gut begründet sein und Sie müssen einen großen Performanznachteil in Kauf nehmen.

10.2.2. Partials

Partials haben einen sehr simplen Anwendungsfall. Sie stellen Fragmente eines höher gelagerten Templates dar, welche entweder einmal oder mehrfach an einer bestimmten Stelle eines Elterntemplates gerendet werden können. Partials können demnach auch "Template-Fragmente" genannt werden. Ein einfacher Anwendungsfall wäre die Indexseite unseres Blogs, die eine Anzahl von Artikeln darstellt. Das Markup jedes Artikels ist nahezu ident und unterscheidet sich nur durch den Text der Artikel. Wir könnten dieses gemeinsame Artikel-Markup leicht in ein einzelnes Partial auslagern, über eine Sammlung von Artikeln iterieren und jeden Artikel einzeln rendern. Wir reduzieren die Menge des Markups in dem Template, wir bieten einen einfachen Mechanismus an, über diesen Datensatz zu iterieren und wir können den Code wiederverwenden. Wir könnten das Markup für den Artikel auf der Indexseite, der Seite des jeweiligen Eintrags und an verschiedenen anderen Stellen verwenden. Indem wir ein Partial verwenden, können wir dieses wiederholte Markup isolieren und globale Änderungen an einer einzigen Stelle vornehmen.

Diese Template-Fragmente bilden gemeinsam mit allen anderen Markup-generierenden Mechanismen die Implementierung des Composite-View-Designpatterns, das in dem Buch Core J2EE Patterns definiert wird.

Use composite views that are composed of multiple atomic subviews. Each component of the template may be included dynamically into the whole and the layout of the page may be managed independently of the content.

Eine Beobachtung, die ich bei unserer vorhergehenden Auseinandersetzung mit Layouts gemacht habe ist, dass die Two-Step-View selbst ein Subpattern der Composite-View ist. Fowler hatte in POEAA einige heute häufig verwendete View-bezogene Designpattern ausgelassen, zum Beispiel die Composite-View und View-Helfer. Diese wurden einige Zeit später im Buch Core J2EE Patterns erstmals formell definiert.

Zend_View-Partials werden durch die View-Helfer Zend_View_Helper_Partial und Zend_View_Helper_PartialLoop implementiert. View-Helfer werden wir im nächsten Abschnitt behandeln. Der erste Helfer rendert ein Partial-Template bei jedem Aufruf und kann entweder für ein einmaliges Rendering oder in einer Schleife innerhalb eines übergeordneten Templates verwendet werden. Die zweite Klasse unterstützt das Durchlaufen einer Schleife intern, so dass die Schleife nicht im übergeordneten Template eingebettet werden muss.

An ein Partial können nicht beliebige Parameter übergeben werden; es muss sich um ein assoziatives Array oder um ein Objekt handeln, das die Methode toArray() implementiert. Andernfalls wird das Objekt mittels get_object_vars() auf ein Array von Objekt-Eigenschaften reduziert. Die Namen für die lokalen Eigenschaften der Klasse werden aus den Schlüsseln des resultierenden assoziativen Arrays bezogen und die Eigenschaften mit den entsprechenden Werten des Arrays versehen.

Im folgenden Beispiel werden ein paar Daten an ein typisches Partial ohne Schleife übergeben.

  • <?php echo $this->partial('article.phtml', array(
  • 'title'=>'Title',
  • 'content'=>'My Content!'
  • )) ?>

Die Datei article.phtml ist wie jedes andere Template, allerdings wird sie als Partial aufgerufen. Die übergebenen Daten sind hier lokale Klasseneigenschaften einer eigenen Zend_View-Instanz.

  • <article>
  • <h3><?php echo $this->escape($this->title) ?></h3>
  • <div class="content">
  • <?php echo $this->content ?>
  • </div
  • <article>

Wird der Partial-Helfer für Schleifen verwendet, dann sollte der Parameter ein Array von Einträgen sein, über die iteriert werden kann (jeder Eintrag sollte dieselben Regeln wie ein normal Partial-Parameter erfüllen, siehe oben) oder irgendein iterierbares Object, das valide Partial-Parameter liefert. Die Klasse Zend_Db_Table_Rowset zum Beispiel stellt eine Sammlung von Zend_Db_Table_Row-Objekten dar, über die iteriert werden kann. Jede Instanz von Zend_Db_Table_Row implementiert eine Methode toArray(). Ein Rowset ist somit ein gültiger Partial-Loop-Parameter.

Wir könnten nun zum Beispiel unser Partial von vorher verwenden und den Partial-Loop-Helfer zum Einsatz bringen, indem wir ihm ein Array von Artikeln übergeben.

  • <?php echo $this->partialLoop('article.phtml', array(
  • array('title'=>'Title', 'content'=>'My Content!'),
  • array('title'=>'Title2', 'content'=>'More Content!')
  • )) ?>

Anstatt eines Arrays kann man auch direkt Objekte übergeben und den Versuch umgehen, das Object in ein Array zu konvertieren. Dies erreicht man, indem man einen Objekt-Schlüssel setzt, bevor man die Methoden Zend_View::partial() oder Zend_View::partialLoop() samt Parametern aufruft. Denken Sie daran, dass der Aufruf der primären Methode eines View-Helfers ohne Parameter üblicherweise nur das View-Helfer-Objekt zurückgibt, um die Verkettung von Methoden ("Method-Chaining") zu ermöglichen. Zum Beispiel:

  • <?php echo $this->partialLoop()->setObjectKey('article')
  • ->partialLoop($articleCollection) ?>

Im obigen Beispiel werden der Klasse des Partial-Templates alle Objekte des iterierbaren Collection-Objekts (etwas, was wir vielleicht in unserem Domain-Model implementieren werden) als öffentliche Eigenschaft $article hinzugefügt. Innerhalb des Partials könnten wir etwas wie dies hier schreiben:

  • <article>
  • <h3><?php echo $this->escape($this->article->title) ?></h3>
  • <div class="content">
  • <?php echo $this->article->content ?>
  • </div>
  • </article>

Da wir Objekte verwenden, müssen wir für die Referenzierungen des Partials mehr schreiben, als das bei einem Array der Fall wäre. Ein Array wäre einfacher zu handhaben, doch es gibt Situationen, in denen es Vorteile mit sich bringt, Objekte zu verwenden, zum Beispiel wenn das Domainobjekt Lazy-Loading für Referenzen auf andere Domainobjekte verwendet, wie es beim Autor unseres Artikels in Kapitel 9 der Fall ist.

Zwei Aspekte sind bei Partials noch zu erwähnen. Erstens haben sie einen anderen Geltungsbereich für ihre Variablen als die übergeordneten Templates. Das bedeutet, dass sie nur auf jene Daten zugreifen können, die ihnen übergeben werden, wenn die Methoden Zend_View::partial() oder Zend_View::partialLoop() aufgerufen werden (die Aufrufe werden auf die Methoden Zend_View_Helper_Partial::partial() respektive Zend_View_Helper_Partial::partialLoop() umgelegt, da es sich dabei um die primären Methoden des jeweiligen View-Helfers handelt). Sie besitzen keine Kenntnis von Daten, die ein Controller der übergeordneten View zuweist (oder dem übergeordneten Partial, falls es sich um Fragment handelt, das in einer zweiten Ebene verschachtelt ist). Diese selbst auferlegte Einschränkung fördert eine stärker objektorientierte Herangehensweise an den Einsatz von Templates und verhindert, dass Daten frei über verschiedenste Templates und Templatefragmente hinweg verfügbar sind (ein Problem, das bei OOP-Designs mit globalen Variablen auftritt). So vorzugehen, ist einfach guter Stil. Wir erlauben es globalen Variablen (hoffentlich) nicht, den Variablengeltungsbereich unseres Objekts zu kontaminieren - warum sollten wir es dann bei Templates tun? Alle Templates sollten voneinander getrennt sein, um das Auftreten unerwünschter Abhängigkeiten zu vermeiden, die das Testen, die Wartung und Anpassung des Codes verkomplizieren würden.

Zuletzt können Sie Partials in andere Partials verschachteln. Falls nötig, können Sie einen ganzen Hierarchiebaum erstellen. Es kommt ganz darauf an, was Ihr Partial repräsentiert: ein ganz kleines Stück Markup oder einen wesentlichen Teil ihres Seiten-Templates bzw. -Layouts. Es liegt in der Natur des Templates-Spiels, eine einzelne View aus vielen einzelnen Teilen zusammenzusetzen. Wie diese Teile zusammengesetzt oder getrennt werden, um die Duplizierung von Markup zu verhindern und die Wiederverwendung der Bestandteile zu ermöglichen, hängt von Ihren Bedürfnissen ab.

10.2.3. View-Helfer

Wenn Partials dazu da sind, Markup in wiederverwendbare Pakete zu schnüren, dann sind View-Helfer ("View-Helper") ihre Pendants auf der Seite der Präsentationslogik. View-Helfer werden verwendet, um eine wiederverwendbare Operation auf ein Template zu erstellen. Stellen Sie sich zum Beispiel vor, dass einem Partial ein Domainobjekt übergeben wurde, das einen Benutzerkommentar repräsentiert. Es enthält einen Namen, eine E-Mail-Adresse, eine URL, Text und ein Datum. Bei den ersten vier gibt es kein Problem, aber wie formatieren Sie das Datum für die Darstellung? Ihr Partial könnte PHP-Code enthalten, der das Datum (abhängig vom verwendeten Standard) parst und es wieder in der gewünschten lesbaren Form zusammensetzt. Aber Einträge haben Veröffentlichungsdaten und sogar Bearbeitungsdaten, dasselbe trifft auf Kommentare zu. Wollen Sie diesen PHP-Code überall wieder einbauen?

Diese Lösung wäre eindeutig nicht sehr gut wartbar. Wenn sich etwas ändert und Sie die Parsinglogik rekonfigurieren müssen, müssen Sie viele Templates bearbeiten. Es wäre weitaus einfacher, diese PHP-Funktionalität in einen View-Helfer zu kapseln, eine wiederverwendbare Klasse, die jederzeit von jedem Templates aus aufgerufen werden kann. Sie könnten den selbst geschriebenen Helfer in einer Klasse ZFExt_View_Helper_FormatDate implementieren, der in der Methode formatDate() ein beliebiges Datum entgegennimmt und es derart formatiert zurückgibt, wie es ihm in Form eines Musters vorgegeben wurde, zum Beispiel "YYYY-MM". Dadurch erhält man eine einfach wiederverwendbare Klasse, die von Unit-Testing profitieren kann und die ebenso zur Wiederverwendung in andere Projekte portiert werden kann.

Wie ich bereits in Kapitel 3: Das Model erwähnt habe, können View-Helfer auch verwendet werden, um ein Model zu kapseln, zum Beispiel die Implementierung eines simplen Data-Mappers wie im letzten Kapitel. Ihr Helfer kann nun an das Domain-Model eine Anfrage stellen, um von ihm Daten zu erhalten, sie dann zu formatieren, Auszeichnungen hinzuzufügen und das Endergebnis zurückzugeben, damit es direkt an einer bestimmten Stelle in einem Template eingefügt werden kann. Wichtig bei diesem Ansatz ist, dass Sie immer daran denken, dass ein View-Helfer niemals das Domain-Model verändern sollte, mit dem es interagiert - das ist Aufgabe des Controllers oder von wem auch immer, der die Eingaben des Benutzers handhabt. View-Helfer sind daher also ein sehr nützliches Feature. Sie können spezielle Formatierungen, die Generierung von Menüs, das Stellen von Anfragen an Models, die Dekoration von Elementen mit Markup und vieles mehr auf View-Helfer abladen.

View-Helfer werden im Buch Core J2EE Patterns als eigenes Pattern definiert, das View-Helper-Designpattern.

Alle View-Helfer folgen einem ähnlichen Design. Sie implementieren eine Methode, deren Name dem Klassennamen entspricht. ZFExt_View_Helper_FormatDate definiert dementsprechend die Methode ZFExt_View_Helper_FormatDate::formatDate(). Diese "primäre" Methode nimmt üblicherweise Parameter entgegen, der Helfer verarbeitet diese und retourniert die Ergebnisse. Handelt es sich um komplexere Helfer mit vielen öffentlichen Methoden, dann gibt diese primäre Methode möglicherweise auch nur das Objekt selbst zurück. Da es sich bei ZFExt_View_Helper_FormatDate um einen selbstgeschriebenen View-Helfer handelt, müssen Sie Zend_View auch mitteilen, wo er zu finden ist und wie der Klassen-Namespace lautet.

Sehen wir uns ein einfaches Beispiel an. Aufgrund einer Analyse der Ladezeiten unserer Website möchten wir dafür sorgen, dass JavaScript- und CSS-Dateien durch die Clients der Besucher für eine längere Zeit gecacht werden können. Dieses Thema wird durch die gut dokumentierten Yahoo Performance Best Practices im Abschnitt Add an Expires or a Cache-Control Header behandelt. Aufgrund dessen ziehen wir los und konfigurieren Apache so, dass er weit in der Zukunft liegende Expires-Header setzt, wenn er statische Dateien wie CSS oder JavaScript ausliefert. Nun stehen wir aber vor einem anderen Problem - wenn der Client diese Dateien für immer cacht, wie können wir dann Änderungen und Aktualisierungen ausliefern? Eine verbreitete Strategie ist, einen URI-Querystring an den URI der statischen Dateien anzuhängen, welcher in unserem Markup aktualisiert wird, wenn sich die referenzierte Datei ändert. Die zwischengespeicherte Resource verwendet den originalen URI (Sie können URIs mit Querystrings cachen, wenn ein explizierter Expires-Header gesendet wird), aber wenn sich der URI ändert, dann wird der Klient die Datei vom neuen URI laden, da sich der Querystring geändert hat - es handelt sich für den Client also um eine neue Resource.

Daraus würde sich ein URI in dieser Art ergeben: http://zfblog.tld/css/style.css?1185131970. Der finale Querystring ist - unschwer zu erkennen - ein Timestamp, der das Änderungsdatum der Datei repräsentiert. Sie könnten genauso einfach eine Versionsnummer verwenden. Schreiben wir uns nun einen View-Helfer, der diese Querystrings automatisch hinzufügt, damit unsere Updates ihre URIs ohne manuelle Eingriffe ändern. Ihr maßgeschneiderter Helfer erweitert Zend_View_Helper_Abstract, der einige Standardfunktionalitäten anbietet, falls sie benötigt werden (was hier nicht der Fall ist).

  • <?php
  • class ZFExt_View_Helper_AppendModifiedDate extends Zend_View_Helper_Abstract
  • {
  • public function appendModifiedDate($file) {
  • $root = getcwd();
  • $filepath = $root . $file;
  • $mtime = filemtime($filepath);
  • return $file . '?' . $mtime;
  • }
  • }

Der neue View-Helfer ist kurz und knackig gehalten. Er nimmt den relativen Pfad zu einer Datei auf unserem Server entgegen, wie er normalerweise im Markup eingebunden werden würde, fragt das Datum der letzten Bearbeitung der Datei ab und fügt das Datum als Timestamp an den Pfad an. Der resultierende Pfad kann dann an alle weiteren Helfer weitergereicht (z.B. an die HeadLink- oder HeadScript-View-Helfer) oder direkt in das relevante Markup ausgegeben werden. Hier ein Template-Fragment, das unseren neuen Helfer verwendet:

  • <link rel="stylesheet" href="<?php echo $this->appendModifiedDate('/css/style.css') ?>" type="text/css" media="screen" />

Neben selbst geschriebenen Helfern bietet das Zend Framework einige Standard-View-Helfer für häufige Aufgaben wie die Generierung von Markup an (z.B. für Form- und Kopfelemente). Wir werden bald einige von ihnen verwenden, und sie verwenden ein weiteres Feature von Zend_View namens Placeholders.

10.2.4. Platzhalter

Platzhalter ("Placeholder") befassen sich mit einem speziellen Bedürfnis, das bei der Verwendung von Templates mit Zend Framework auftritt. Sie erlauben es Templates, ungeachtet ihres Variablengeltungsbereiches auf eine gemeinsame Registry für Daten zuzugreifen. Das kommt dem Registry-Pattern sehr nahe, welches in Zend Framework häufig mittels Zend_Registry implementiert wird, doch dieser Anwendungsfall ist spezifischer. Stellen Sie sich ein Szenario vor, in dem Sie feststellen, dass eine Webseite ein bestimmtes Stück JavaScript-Code benötigt, das so früh wie möglich geladen werden soll. Ihr Layout enthält diesen Code nicht, da er nicht für jede Seite gebraucht wird. Platzhalter können dieses Dilemma lösen, indem sie es Templates und Partials ermöglichen, einem Platzhalter Werte hinzuzufügen, einem Schlüssel in dieser gemeinsamen Registry. When ein übergeordnetes Template oder Layout gerendert wird, kann es dann die Daten dieses Platzhalters an einem beliebigen Punkt rendern. Im Falle von JavaScript wird dies durch einen spezialisierten Platzhalter bewerkstelligt, der von Zend_View_Helper_HeadScript implementiert wird. Mit ihm kann man von jedem Subtemplate aus beliebiges Javascript am Ende oder am Beginn anfügen, überschreiben oder mit einem bestimmten Index-Wert versehen, bevor es letztendlich im Layout gerendert wird.

In Zend Framework gibt es eine Auswahl an spezialisierten Platzhaltern wie HeadLink, HeadMeta etc. Daneben können Sie aber auch den Basis-Placeholder-View-Helfer für beliebige Daten verwenden, die über verschiedene Templates hinweg kommuniziert werden müssen. Sehen wir uns ein Beispiel an, in dem ein Layout die Ausgabe eines Platzhalters einfügen kann. Das Layout ermöglicht es somit jedem Template, den Text in der Hauptüberschrift zu ändern.

  • <html>
  • <head>
  • <title>My Page</title>
  • </head>
  • <body>
  • <h1><?php echo $this->placeholder('pageHeader') ?></h1>
  • <?php echo $this->layout()->content ?>
  • </body>
  • </html>

Wir können diesen Wert nun von jedem beliebigen Template (inklusive Partials) aus setzen.

  • <?php $this->placeholder('pageHeader')->set('About') ?>
  • <p>My name is Joe. Joe Bloggs.</p>

Im Gegensatz zu den spezifischeren Placeholder-Implementierungen kann der allgemeine Placeholder-Helfer für alle Arten von Werten verwendet werden. Haben Sie einen bestimmten Wert, der entsprechend bestimmter Regeln formatiert werden soll, können Sie eine Subklasse vom allgemeinen Helfer ableiten, um die Formatierung vorzunehmen, wenn die Daten in ein Template ausgegeben werden. Als Beispiel implementieren wir einen eigenen View-Helfer, der die Überschrift der Seite für HTML 5 formatiert.

  • <?php
  • class ZFExt_View_Helper_PageHeader extends Zend_View_Helper_Placeholder
  • {
  • public function pageHeader()
  • {
  • return $this;
  • }
  • public function set($value)
  • {
  • parent::placeholder('pageHeader')->set($value);
  • }
  • public function __string()
  • {
  • return "<h1>" . (string) parent::placeholder('pageHeader') . "</h1>";
  • }
  • }

Es handelt sich um ein sehr einfaches Beispiel, das lediglich einen konkreten Wrapper für den Placeholder-Container mit dem Namen "pageHeader" darstellt. Zudem schließt es den Wert des Seitentitels in <h1>-Tags ein. Hier ist unser Layout und unser Seitentemplate, das den neuen Platzhalter verwendet.

  • <html>
  • <head>
  • <title>My Page</title>
  • </head>
  • <body>
  • <?php echo $this->pageHeader() ?>
  • <?php echo $this->layout()->content ?>
  • </body>
  • </html>
  • <?php $this->pageHeader()->set('About') ?>
  • <p>My name is Joe. Joe Bloggs.</p>

10.2.5. Kurze PHP-Tags vs. lange PHP-Tags

Sie haben vielleicht bemerkt, dass ich in diesem Buch bevorzugt die Langform der PHP-Tags verwende, also <?php (echo) ... ?> anstatt der Kurztags <?(=) ... ?>. Bei der Kurzform (im Englischen als short tags bezeichnet) handelt es sich um eine optionale PHP-Einstellung. Viele Server haben die Kurzform zwar aktiviert, um die Kompatibilität zu Anwendungen zu erhöhen, die diese Form verwenden, doch ebenso viele Server haben die Option deaktiviert, da dies auch in der PHP-Konfigurationsdatei php.ini.recommended der Fall ist. Darüber hinaus wissen wir, dass PHP 6 die Kurzform voraussichtlich "missbilligen" wird, um einen Konflikt mit der XML-Deklaration zu lösen, die in ähnlicher Form <?xml ... ?> verwendet. XML-Deklarationen müssen herkömmlich mittels "echo" ausgegeben werden, wenn das XML aus einem Template mit short tags generiert wird. Ich persönlich finde es ärgerlich, dass diese Notation mit PHP6 vermutlich fallen gelassen wird. <?php echo ... ?> öfter als einige wenige Male zu schreiben, ist für mich einfach nur nervig; andererseits handelt es sich aber eben nur um eine optionale Einstellung, die deswegen in zur Verteilung bestimmten Anwendungen ohnehin nicht verwendet werden sollte. Auf jeden Fall ist es besser, mit der Variante zu arbeiten, die auf jeden Fall funktioniert als zu riskieren, dass der Code später nicht mehr läuft.

Zend Framework ermöglicht die Nutzung der Kurzform unabhängig von der PHP-Konfiguration durch den Einsatz eines Streamwrappers. Falls die Option zur Verwendung der Kurzform in Ihrer Konfigurationsdatei php.ini deaktiviert ist (also das Flag short_open_tag), ist dieser Wrapper aktiviert und Sie können die Option useStreamWrapper auf true setzen. Das machen Sie, indem Sie den Schlüssel useStreamWrapper in der Datei application.ini verwenden oder indem Sie in Ihrer Bootstrap die Methode Zend_View::setUseStreamWrapper() aufrufen. Die Verwendung des Streamwrappers wird sich vermutlich auf die Performance auswirken; falls Sie wirklich auf Kurztags angewiesen sind, ist es besser, sie einfach in Ihrer PHP-Konfiguration zu aktivieren.

10.3. Einrichtung der ZFBlog-Applikation

Die neue Blog-Applikation einzurichten, ist nicht furchtbar schwer. Kopieren Sie einfach das "Hallo Welt"-Beispiel in das Projektverzeichnis der Blog-Anwendung, in dem Sie die erste Ausbaustufe des Domain-Models unserer Anwendung abgelegt haben. Damit haben wir eine ausgezeichnete Ausgangsbasis. Sie können eine neue lokale Domain und einen virtuellen Host speziell für den Blog anlegen, indem Sie den Anweisungen im Anhang zum Aufsetzen von virtuellen Hosts in Apache folgen. Für dieses Buch werde ich die Domain http://zfblog.tld verwenden.

Weitere Änderungen sind nicht nötig. Im Rest dieses Kapitels wird genau beschrieben, wie wir weiter vorgehen.

10.4. Eine Indexseite mit HTML 5 erstellen

Die Indexseite unseres Blogs wird (vorhersehbarerweise) eine Liste von Einträgen darstellen. Fangen wir damit an, dass wir uns die ersten HTML-Tags von HTML 5 ansehen. Später werden wir uns um das Design und weitere Elemente sowie Informationen auf der Seite kümmern. Zunächst bearbeiten wir /application/views/scripts/index/index.phtml.

HTML 5 verwendet einen vereinfachten Doctype, der im Prinzip nur aussagt, dass es sich um HTML handelt. Die Version oder andere Informationen müssen nicht angegeben werden. Da wir wissen, dass wir den Doctype setzen können, der von Zend_View und den davon abhängigen Komponenten verwendet wird, können wir diesen Code in unserem Template mittels des View-Helfers Zend_View_Helper_Doctype ausgeben. Die Unterstützung von Zend Framework für HTML 5 geht nur so weit, dass der Doctype alle abhängigen View-Helfer anweist, Tags in Form von HTML 4 zu verwenden. Freilich kann HTML 5 auch in ähnlicher Weise wie XHTML geschrieben werden: Sie können also kleingeschriebene Tag- und Attributnamen verwenden, Attributwerte in Anführungszeichen einschließen, alle Tags schließen etc. Ich bevorzuge diese Form, HTML 5 zu schreiben, da es sich sauberer anfühlt und ich mich nicht zu weit von dem mir bekannten XHTML-Stil entfernen muss. Zend Framework wirft hierbei ein kleines Problem auf - es unterscheidet nicht zwischen HTML 5 und XHTML 5 (HTML 5, geschrieben unter Berücksichtigung der Regeln für wohlgeformtes XML). Wir werden das Problem beseitigen, indem wir einen eigenen View-Helfer schreiben, der die Unterstützung für einen XHTML-5-Doctype hinzufügt.

Wie immer bei HTML/XHTML ist das Root-Element <html>. Unmittelbar danach folgt ein <head>-Element, das alle von uns benötigte <link>-, <meta>-, <title>- oder <style>-Elemente enthält. Sie können in einem der üblichen Stile geschrieben werden, doch wie ich erklärt habe, bevorzuge ich den XHTML-Stil. HTML 5 bringt hier ein neues Feature mit sich - ein neues Attribut für das Element <meta> namens "charset". Mit diesem neuen Attribut soll man dem Parser leichter mitteilen können, welche Zeichenkodierung das Dokument verwendet, weswegen es sich um einen adäquaten Ersatz für das häufig verwendete <meta>-Element Content-Type handelt. Auch dieses Attribut wird von Zend Framework noch nicht unterstützt, weswegen wir einen zweiten eigenen View-Helfer hinzufügen werden, der es implementiert.

Ihnen wird auffallen, dass ich diese Tags als View-Helfer implementiere, obwohl wir sie stattdessen direkt in unsere Templates schreiben könnten. Dabei handelt es sich abermals um guten Stil, da die anderen Templates den Inhalt von <head> durch die Verwendung von View-Helfern modifizieren können, indem Stylesheets, Skripte und Meta-Informationen hinten und vorne angefügt oder gar überschrieben werden. Den Vorteil werden Sie in jenen Situationen erkennen, in denen eine bestimmte Seite zusätzliches Styling oder andere JavaScript-Dateien benötigt, die nicht für alle Seiten erforderlich sind.

Ich habe auch zwei CSS-Stylesheets aus dem "Yahoo User Interface Framework" hinzugefügt. Diese werden direkt vom Yahoo-Server geladen. Daher brauchen wir lokal nichts abzuspeichern. Die erste Datei ist eine minifizierte Version der drei zusammengelegten Stylesheets "reset", "fonts" und "grid". Die ersten zwei stellen sicher, dass alle Browser die gleiche Ausgangslage haben, indem Unterschiede in der standardmäßigen Darstellung von Elementen ausradiert werden. Somit muss man sich nicht mit den Inkonsistenzen der Browser bei der Darstellung der Standardelemente herumschlagen. Das Stylesheet "grid" werden wir später verwenden, um uns das Erstellen eines Spaltenlayouts zu erleichtern. Das zweite eingebundene Stylesheet "base" stellt eine hervorragende Basis für das Design der wichtigsten Elemente her. Somit sollte ich - außer dem, was für mein bevorzugtes Blogdesign nötig ist - ziemlich wenig CSS zu schreiben haben.

Zum Schluss haben wir eine JavaScript-Datei des Projekts html5shiv eingebunden. Dabei handelt es sich um ein relativ simples JavaScript, das dafür sorgt, dass Internet Explorer die neuen HTML-5-Elemente erkennt. Ansonsten könnten wir ihr Aussehen nicht verändern. Nachdem dieser Code nur für Internext Explorer benötigt wird, haben wir den Aufruf in Conditionals gepackt, die auf alle Versionen von Internet Explorer (aber auf keine anderen Browser) zutreffen.

Fügen wir nun einen Kopfabschnitt hinzu, der den Seitentitel und den oberen Navigationsbereich repräsentiert.

Wir können bereits einige der neuen Elemente von HTML 5 sehen. Unser Kopfabschnitt ist in einen Tag <header> gekleidet, der unsere gewohnte Überschrift <h1> und ein weiteres neues Mitglied umfasst, nämlich das Element <nav>, das unser oberes Navigationsmenü umschließt. Das beste daran neben der semantischen Bedeutung ist vermutlich, dass wir endlich die Unmenge von <div>-Elementen vergessen können, die das Klassenattribut verwenden müssen um uns zu verdeutlichen, was sie repräsentieren.

HTML 5 liefert für diese neuen Elemente die folgende Definition:

header

Das Element "header" repräsentiert eine Gruppe von Einführungs- oder Navigationshilfen. Ein header-Element ist üblicherweise dafür vorgesehen, die Überschrift eines Abschnitts (ein h1- bis h6-Element oder ein hgroup-Element) zu enthalten; dies ist aber nicht zwingend erforderlich. Das header-Element kann ebenso dazu verwendet werden, ein Inhaltsverzeichnis, ein Suchformular oder relevante Logos eines Abschnitts zusammenzufassen.

nav

Das Element "nav" repräsentiert einen Seitenabschnitt, welcher Verknüpfungen zu anderen Seiten oder zu Abschnitten innerhalb derselben Seite herstellt: einen Abschnitt mit Navigationslinks. Nicht alle Linkgruppen auf einer Seite müssen in einem nav-Element zusammengefasst werden — nur bei Abschnitten mit größeren Navigationsblöcken ist ein nav-Element angemessen. Besonders in Fußzeilen ist eine Liste von Links zu verschiedenen bedeutenden Teilen einer Website üblich, doch in diesen Fällen ist es passender, das Element footer zu verwenden. Ein nav-Element ist für diese Links nicht notwendig.

Als nächstes benötigen wir den "body" unseres Markups, der zeigt, wo wir unsere Blogeinträge rendern würden, und abschließend den footer-Bereich, der das Copyright bzw. ähnliche Hinweise enthält.

Nun, das ist neu! Wie nicht zu übersehen ist, hat HTML 5 noch weitere neue Elemente mit sich gebracht. Viele kommen an Stellen zum Einsatz, an denen wir üblicherweise <div>-Tags mit Klassenattributen setzen würden, die den Zweck des <div> erläutern. Auf einer simplen Seite würden Sie vielleicht erwarten, den Inhalt einfach in einen <article>-Tag einzubetten, da der Inhalt einen einzelnen Eintrag darstellt. Da wir jedoch wissen, dass in unserem Blog jeder Eintrag auf der Seite einen selbstständigen Artikel darstellt, verwenden wir mehrere <article>-Elemente und fassen sie in einem einzelnen <section>-Element zusammen (falls die Seite weiter unterteilt ist, können wir auch mehrere <section>-Elemente verwenden und sie im Gegensatz zu Docbook XML verschachteln). Jeder Artikel hat wiederum einen Kopf- und einen Fußbereich, für welche wir die entsprechenden HTML-5-Tags verwenden. Zuletzt werden alle Detailinformationen zum Autor in <address>-Tags eingebettet. Es mag etwas verwirrend sein, doch der Tag <address> bezieht sich nicht auf eine physische Adresse - er bezieht sich mehr auf andere Details einer Person wie ihren Namen, die Website, E-Mail, Beschreibung etc.

Werfen wir einen Blick auf die Definitionen der HTML-5-Spezifikation für diese neuen Elemente in HTML 5.

section

Das Element "section" repräsentiert einen allgemeinen Dokument- oder Anwendungsabschnitt. Ein Abschnitt ist in diesem Zusammenhang eine thematische Gruppierung von Inhalten, die typischerweise einen Kopfbereich und eventuell einen Fußbereich aufweist.

article

Das Element "article" repräsentiert einen Abschnitt einer Seite, der sich aus Elementen zusammensetzt, die miteinander einen unabhängigen Teil eines Dokuments, einer Seite, Anwendung oder Site formen. Es kann sich um ein Posting in einem Forum, einen Artikel in einem Magazin oder einer Zeitung, einen Weblogeintrag, einen Benutzerkommentar, ein interaktives Widget oder jegliche andere Art von unabhängigem Inhalt handeln. Ein article-Element ist insofern "unabhängig", als es für sich allein stehen und dabei Sinn ergeben könnte, zum Beispiel auf einer Seite mit syndizierten Inhalten oder als austauschbare Komponente auf einer vom Benutzer konfigurierbaren Portalseite.

footer

Das Element "footer" repräsentiert einen Fußbereich für den nächstgelegenen übergeordneten Inhaltsabschnitt. Ein Fußbereich enthält typischerweise Informationen über den Abschnitt wie zum Beispiel den Autor, Verknüpfungen zu verwandten Dokumenten, Urheberrechtsinformationen und ähnliches. Kontaktinformationen sollten - möglicherweise selbst in einem footer-Element - in einem address-Element untergebracht werden.

address

Das Element "address" repräsentiert die Kontaktinformation für das nächstgelegene übergeordnete article- oder body-Element. Falls es sich dabei um das body-Element handelt, dann gilt die Kontaktinformation für das gesamte Dokument. Das Element address darf nicht verwendet werden, um beliebige Adressen zu repräsentieren (z.B. Postanschriften), falls es sich dabei nicht tatsächlich um relevante Kontaktinformationen handelt.

time

Das Element "time" repräsentiert entweder die Zeit auf einer 24-Stunden-Uhr oder ein präzises Datum im "Fortlaufenden Gregorianischen Kalender", optional mit einer Uhrzeit und einer Information über die Verschiebung durch die Zeitzone. Das Ziel dieses Elements ist es, Daten und Uhrzeiten in maschinenlesbarer Form bereitzustellen, damit Useragenten die Möglichkeit anbieten können, diese Termine zum Kalender des Benutzers hinzuzufügen. So können zum Beispiel Geburtstagserinnerungen oder Termine hinzugefügt.

Es sei kurz angemerkt, dass das neue Element <time> nicht ohne Probleme auskommt. Es ist strikt an den Gregorianischen Kalender gebunden, womit nicht-Gregorianische Daten vor seiner Einführung nicht repräsentiert werden können. Da <time> das Element <abbr> im Mikroformat hCalendar ersetzen soll, handelt es sich eigentlich um einen Fehlschlag, weil historische Daten so nicht repräsentiert werden können. Hoffentlich wird die finale Spezifikation von HTML 5 das korrigieren.

Starten Sie Ihren Browser und navigieren Sie zu http://zfblog.tld. Sie sollten das neue Template sehen, das mit dem Standarddesign von YUI CSS dargestellt wird.

Anfängliche Indexseite des Blogs

10.5. Statisches Markup ins Layout übernehmen

Nachdem wir nun ein erstes Design haben, müssen wir identifizieren, welche Teile dieses Templates statisch sind, sich also von Seite zu Seite kaum ändern werden. Ein kurzer Bliick genügt um zu sehen, dass wir auf unserer Indexseite nur bei den Einträgen Änderungen erwarten. Die Anzahl der anzuzeigenden Beiträge kann variieren und der Inhalt der Artikel wird regelmäßig aktualisiert. Bewaffnet mit dieser Erkenntnis können wir unser Index-Template auf die dynamischen Teile reduzieren - den Rest werden wir gleich in ein Layout auslagern.

Hier ist das überarbeitete Template /application/views/scripts/index/index.phtml.

Verlegen wir das übriggebliebene Markup in ein neues Template (bezeichnet als Layout) in /application/views/layouts/default.phtml.

Sie werden bemerkt haben, dass wir nun eine Methode layout() von Zend_View referenzieren. Wie wir bei der Auseinandersetzung mit den View-Helfern angemerkt haben, reflektiert der Klassenname eines View-Helfers den Namen seiner primären Methode, welcher typischerweise (zumindest) dazu verwendet wird, eine Instanz des View-Helfer-Objekts zu erhalten. In diesem Fall holen wir uns eine Instanz von Zend_View_Helper_Layout und geben aus, was auch immer in seiner öffentlichen Eigenschaft $content enthalten ist.

Um zu verstehen, wie unser Index-Template in diese Eigenschaft gelangt, müssen wir verstehen, dass das Rendern standardmäßig von den Controllern mittels des Action-Helfers ViewRenderer bewerkstelligt wird. Dieser Helfer rendert das Template für den aktuellen Controller und die aktuelle Action in die Antwort (auf die Anfrage) hinein. Zend_Layout kann dann den Body der gerenderten Antwort abfangen und ihn in das Layout-Template einfügen. Dann wiederum wird das gesamte Layout gerendert, um schließlich die gesamte Seite zu produzieren.

Sie sollten im Hinterkopf behalten, dass Layouts wie alle Templates innerhalb des Variablengeltungsbereiches eines Zend_View-Objekts ausgeführt werden: alle View-Helfer und Methoden von Zend_View sind also weiterhin von Ihren Layouts aus erreichbar.

Um Zend_Layout zu aktivieren, müssen wir es für die Verwendung konfigurieren. Normalerweise muss man dafür die statische Methode Zend_Layout::startMvc() aufrufen und die Instanz konfigurieren. Da wir jedoch Zend_Application für unseren Bootstraping-Prozess verwenden, müssen wir nur einige Konfigurationswerte für die Layout-Ressource in /config/application.ini ablegen. Die zwei benötigten Optionen (die wir am Ende des Abschnitts "Standard Resource Options" hinzufügen) setzen den Standardnamen des Layout-Templates ohne die Dateiendung .phtml sowie den Pfad, in dem die Layout-Templates platziert werden können.

Kehren Sie zu Ihrem Browser zurück und navigieren Sie zu http://zfblog.tld. Sie sollten exakt dieselbe Seite wie zuvor ohne Unterschiede siehen. Unser Index-Template wird nun in das neue Layout gerendert.

Um das Bild abzurunden, nutzen wir die Gelegenheit und bearbeiten auch gleich das zweite Seitentemplate in unserer Anwendung unter /application/views/scripts/error/error.phtml. Sie werden feststellen, dass der Inhalt nicht in <article>-Tags eingebettet ist, da es sich um eine allgemeine Nachricht und nicht um ein alleinstehendes Artikeldokument handelt. Ich verwende nun auch eine Überschrift der zweiten Ebene <h2>, die für den <article> von einem Element <header> eingeschlossen wird.

Ziehen Sie los und starten Sie einen Testlauf, indem Sie eine ungültige URL wie http://zfblog.tld/foo/bar aufrufen. Denken Sie daran, dass Sie das Werfen von Ausnahmen kurzfristig in application.ini deaktiveren müssen, um die Fehlerseite zu sehen.

Anfängliche Fehlerseite des Blogs

10.6. Veränderbare Elemente durch Platzhalter ersetzen

Unser Layout ist nun eingerichtet, aber wir können noch weiter gehen und es den Seitentemplates (oder gar Partials) ermöglichen, Teile des Layouts je nach Bedarf zu modifizieren, besonders was die Stylesheets, den Titel der Seite, JavaScript und die Meta-Tags betrifft. Zend Framework ermöglicht solche Änderungen durch die Verwendung von Platzhaltern, genauer gesagt durch einige spezielle Implementierungen wie Zend_View_Helper_HeadTitle, Zend_View_Helper_Doctype, etc. Wie bereits erwähnt erlauben Platzhalter es den Templates, Einträge innerhalb eines eigenständigen Datencontainers hinzuzufügen, zu entfernen oder gar zu ersetzen. Der Inhalt wird dann in seiner finalen Form in die äußere Schicht der Template-Hierarchie eingefügt.

Lassen Sie uns anfangen, indem wir unser Layout in /application/views/layouts/default.phtml modifizieren, damit es verschiedene dieser Placeholder-View-Helfer verwendet.

Zend_View_Helper_Doctype und Zend_View_Helper_HeadMeta werden beide ohne Änderung ausgegeben, außer dass wir - wo angebracht - Informationen über die Einrückung des Codes und Zeilenumbrüche hinzufügen. Dadurch sorgen wir nur dafür, dass der Quelltext leichter zu lesen ist. Anstatt Werte für diese zwei Helfer innerhalb des Layouts zu setzen, werden wir sie von der Bootstrap aus konfigurieren, indem wir Optionen aus der application.ini verwenden.

Zend_View_Helper_HeadTitle wird aufgerufen, um den Titel auf "Pádraic Brady's Blog" zu setzen. Sie können weitere Teile hinzufügen, die innerhalb des Elements <title> ausgegeben werden - getrennt durch ein beliebiges Zeichen, das Sie mittels Zend_View_Helper_HeadTitle::setSeparator() bestimmen. Sie können der primären Methode headTitle() ein optionales zweites Argument mit dem Wert "APPEND" (die Standardeinstellung) übergeben, um einen String anzufügen, oder "PREPEND" verwenden, um den neuen Teil des Seitentitels vorne anzufügen.

Zend_View_Helper_HeadLink kann verwendet werden, um Links zu Stylesheets hinzuzufügen, voranzustellen oder zu ersetzen. Genauso können andere allgemeine Links hinzugefügt werden, indem man ein Array von Attributen und Werten an Zend_View_Helper_HeadLink::headLink() übergibt. Alternative Links setzen Sie, indem Sie appendAlternate() und die gleichgelagerten Methoden (prepend/set/offsetSetAlternate()) verwendet. Fügt man Stylesheets hinzu, dann kann man mit optionalen Parametern die Attribute "media" und "type" setzen sowie in einem Array Konditionen festlegen (so wie wir es in der nächsten Zeile verwenden werden, um eine JavaScript-Datei nur im Internet Explorer einzubinden). Wenn Sie einen Block von Styles hinzufügen müssen, können Sie das stattdessen durch die Verwendung von Zend_View_Helper_Style erreichen. Zend_View_Helper_Script schließlich erlaubt das Einbinden von Skripten und JavaScript-Dateien.

Sie können bei diesen Placeholder-Implementierungen einige Gemeinsamkeiten beobachten. Sie haben Methoden, um Einträge zu setzen, anzufügen und voranzustellen. Viele bieten auch offsetSet*()-Methoden an, um Einträge an einer bestimmten Stelle im Einträge-Array hinzuzufügen.

Ausgelassen habe ich bisher das Markup für den charset-<meta>-Tag. Dieser kann bis jetzt nicht durch das Zend Framework gerendert werden, doch wir werden dieses Problem im nächsten Abschnitt beseitigen. Zuerst nehmen wir aber einige Änderungen an unserer Methode ZFExt_Bootstrap::_initView() vor, um die zu rendernden Meta-Tags und den Doctype zu konfigurieren.

Nun können wir application.ini modifizieren, um ein letztes Stück Meta-Information hinzuzufügen, nämlich die Sprache der Seite. Es handelt sich dabei nicht um einen Standard-Konfigurationswert für Zend_View, sondern nur um einen Standardwert, den wir in unserer Bootstrap-Klasse einlesen, wenn wir die Meta-Informationen für die Seite setzen.

10.7. Erweitern der Unterstützung von HTML 5 durch eigene View-Helfer

Momentan geht die Unterstützung von HTML 5 durch Zend Framework nicht über den HTML-Doctype hinaus. Der verwendete Doctype ist wichtig - nicht nur weil er ganz am Beginn unseres Templates steht, sondern weil er zudem für mehrere View-Helfer bestimmt, wie die Tags konstruiert werden. Wie ich bereits erwähnt habe, bevorzuge ich bei HTML 5 den XHTML-Stil mit geschlossenen Tags, kleingeschriebenen Attributnamen und in Anführungszeichen eingeschlossenen Attributwerten (neben anderen Facetten). HTML 5 selbst verhält sich gegenüber diesen Details relativ indifferent, doch als Alternative zur typischen HTML-Variante gibt es auch eine Definition für eine XML-Serialisierung.

Wenn wir unseren Doctype auf "HTML 5" setzen, wird es problematisch wenn wir XHTML testen wollen, da die View-Helfer kontrollieren, ob der Doctype-Name mit "XHTML" beginnt. HTML5 (unser aktueller Doctype-Optionswert) tut das nicht. Damit wir das Problem überwinden, sollten wir eine eigene Implementierung des Helfers Zend_View_Helper_Doctype hinzufügen (eine einfache Subklasse ist alles, was wir benötigen), um die Unterstützung für eine Option namens "XHTML5" hinzuzufügen, die dafür sorgt, dass XHTML-Regeln auf die Ausgabe der View-Helfer angewandt werden.

Wir werden unseren View-Helfer in /library/ZFExt/View/Helper/Doctype.php abspeichern. Um unserer Anwendung bzw. Zend_View mitzuteilen, wo es unsere maßgeschneiderten Helfer findet, können wir neue Optionen in der Sektion "Standard Resource Options" unserer application.ini definieren. Eintrag werden wir dort einen neuen Pfad, in dem View-Helfer gefunden werden können sowie den Klassen-Präfix, den sie verwenden. Danach brauchen wir keine weiteren Änderungen durchzuführen - angepasste View-Helfer, die den Namen eines Zend-Framework-View-Helfers widerspiegeln (das heißt: sie enden mit demselben Ausdruck in CamelCase-Schreibweise), setzen die Helfer des Frameworks außer Kraft. Rufen wir Zend_View::doctype() auf, wird somit zuerst nach einem angepassten View-Helfer dieses Namens gesucht.

Diesen neuen View-Helfer zu implementieren hat ein einziges Ziel - einen neuen XHTML5-Doctype zu erlauben, der den normalen HTML5-Doctype in einem Template ausgibt. Eine Abfrage des Doctype sollte den korrekten Wert zurückgeben, um den XHTML-Test zu bestehen, der von anderen View-Helfern verwendet wird. Falls Sie mir bei den vorangegangenen Domain-Model-Tests gefolgt sind und eine neue Datei AllTests.php aufgesetzt haben, können Sie diesen Prozess für alle View-Helfer wiederholen, die wir hinzufügen wollen. Hier sind unsere Unit-Tests für eine solche Implementierung. Wir speichern sie in /tests/ZFExt/View/Helper/DoctypeTest.php ab.

Um den Helfer zu implementieren, reicht eine simple Subklasse in /library/ZFExt/View/Helper/Doctype.php.

Wie Sie in der Klasse sehen können, übergeben wir die Kontrolle für alle möglichen Doctypes außer das XHTML5-spezifische Handling zurück an die Elternklasse.

Eine weitere Facette der Unterstützung von HTML 5, die wir angehen können, ist das neue Attribute charset, das im <meta>-Tag verwendet werden kann. Das Attribut ersetzt diesen Code

durch eine kürzere Form, die keinen Content-Type deklariert. Natürlich können Sie wenn Sie wollen weiterhin den Content-Type hinzufügen, aber die Webanwendung sollte ohnehin mit dem korrekten Typ ausgeliefert werden.

Unglücklicherweise erkennt der View-Helfer Zend_View_Helper_HeadMeta, der Templates und Layouts das Hinzufügen von Metainformationen erlaubt, das charset-Attribut nicht als valide an. Wir können ihn dazu überreden, indem wir einen weiteren eigenen View-Helfer hinzufügen, und wieder müssen wir uns dafür über die originale Klasse hinwegsetzen. Wir werden ein weiteres Mal eine Subklasse der Originalklasse erstellen, damit wir möglichst wenig Code schreiben müssen.

Hier die relevanten Unit-Tests, die in /tests/ZFExt/View/Helper/HeadMetaTest.php gespeichert werden.

Die Implementierung ist etwas komplizierter als gewohnt, da wir die Unterstützung für einen komplett neuen <meta>-Tag-Typen hinzufügen, den es bisher nicht gab. Die neue Klasse wird in /library/ZFExt/View/Helper/HeadMeta.php geschrieben.

Keine Sorge, falls das jetzt für Sie unverständlich wirkt! Wir werden uns in späteren Kapiteln ein paar anwendungsspezifische View-Helfer ansehen, aber vorerst benötigen wir nur diese zwei maßgeschneiderten Helfer, um sicherzustellen, dass wir eine vollständigere HTML-5-Unterstützung zur Hand haben.

In der obigen Klasse ersetzen wir zwei Originalmethoden: _isValid() und itemToString(). Die erste validiert die Details einiger Metainformationen, die wir in einem <meta>-Tag rendern wollen. Die Daten werden als eine Instanz von stdClass gespeichert. Es handelt sich also um einen simplen Datencontainer, der genauso gut ein Array hätte sein können. Die hauptsächliche Neuerung, die ich bei der Validierung eingeführt habe ist, dass ich einen neuen Meta-Typ namens "charset" erlaube, falls der Doctype einem der HTML-5-Typen entspricht. In der Methode itemToString() habe ich die Fähigkeit hinzugefügt, diesen neuen Meta-Typ in einen <meta>-Tag zu transformieren und den Tag zu schließen, falls der verwendete Doctype XHTML5 ist.

Ich habe auch eine neue Methode setCharset() hinzugefügt, damit die Erstellung dieses neuen Tags von den zwei ursprünglich unterstützten Meta-Typen (den einfachen name/value-Typen und den http-equiv-Typen) getrennt wird.

Nachdem wir unsere erweiterte Unterstützung von HTML 5 abgeschlossen haben, sehen wir uns application.ini erneut an und fügen eine neue HTML-Markup-Option namens "charset" zusammen mit dem neuen Doctype ein. Wir könnten auch einfach die Zend_View-Einstellung für die Zeichencodierung verwenden, aber ich habe mich entschieden, das zu trennen, damit wir den neuen Meta-Tag-Typen explizit dann hinzufügen können, wenn seine spezifische Option gesetzt wurde.

Wir können nun auch die Methode _initView() der Bootstrap-Klasse abändern, um die neue Charset-Option zu verwenden.

Laden Sie nun die Index-Seite neu. Wenn Sie den Quelltext kontrollieren, sollten Sie nun sehen, dass er vollständig im Einklang mit dem originalen Markup steht, welches wir am Beginn dieses Kapitels definiert haben.

10.8. Einen Link zu einem eigenen Stylesheet hinzufügen

Durch die Verwendung des YUI-CSS-Framework haben wir einen Basisstil für den Blog, der sehr schlicht gehalten, aber dafür über alle größeren Browser gleich ist. Wir können den Code nun durch unser eigenes angepasstes Design ergänzen, um dem Blog einen abgerundeten Stil zu verpassen.

Um das zu erreichen, müssen wir eine Änderung vornehmen, damit unser Layout-Template eine Datei style.css in /public/css/style.css einbindet. In dieser Datei können wir weitere CSS-Regeln ablegen. Hier ist der überarbeitete <head>-Abschnitt des geänderten Layouts.

Unter Berücksichtigung der Tatsache, dass wir uns die Yahoo Performance Best Practices zu Herzen nehmen, fügen wir unser vorhergehendes View-Helfer-Beispiel mit ein paar Änderungen ein. Hier haben wir die eigentlichen Unit-Tests, die zu unserem Helfer ZFExt_View_Helper_IncludeModifiedDate gehören. Sie verwenden eine leere Datei style.css, die Sie in /tests/ZFExt/View/Helper/_files/style.css speichern können, um ein Bearbeitungsdatum zu erhalten. Die Unit-Tests werden in /tests/ZFExt/View/Helper/IncludeModifiedDateTest.php abgelegt. Wie bei all diesen Tests fügen Sie sie zu der nächstgelegenen Datei AllTests.php hinzu, damit sie von PHPUnit aufgerufen wird. Der einzige knifflige Teil dabei ist das aktuelle Arbeitsverzeichnis von PHP so gestalten, dass der relative URI in diesem Test Sinn macht und die Datei style.css damit aufgefunden werden kann.

Wie Ihnen wahrscheinlich auffällt, haben wir den Helfer dahingehend aufgepeppt, dass er mit einem weiteren Fall umgehen kann, in dem der URI einer Datei bereits einen Query-String in irgendeiner Form enthält. Dieses Mal wird der Timestamp des Bearbeitungsdatums auch innerhalb des Dateinamens anstatt im Querystring hinzugefügt. Dadurch wird das Szenario abgedeckt, wonach viele Proxies den Query-String beim Cachen von Dateien ignorieren. Das Ändern des Dateinamens oder des Pfades ist somit weitaus effektiver. Natürlich ändern wir nicht tatsächlich den Dateinamen - stattdessen fügen wir /public/.htaccess eine neue Rewrite-Rule hinzu, damit alle URIs zu Dateien in der Form von /name.timestamp.extension (z.B. /style.1252108229.css) korrekt auf den eigentlichen Dateinamen umgeschrieben werden. Hier ist die Implementierung für /library/ZFExt/View/Helper/IncludeModifiedDate.php.

Die folgende Änderung fügt die Unterstützung in /public/.htaccess dafür hinzu, Dateien auf diese Art mit einem Timestamp zu versehen und die darunterliegende Datei zu laden, die keinen Timestamp in ihrem Namen besitzt. Die Rewrite-Rule entfernt lediglich den Timestamp-Teil aus dem Namen der angefragten Datei. Sie können je nach Anforderung weitere Dateiendungen und Verzeichnisse hinzufügen. Ich habe einige häufig betroffene Standardtypen und -verzeichnisse eingetragen.

SetEnv APPLICATION_ENV development

RewriteEngine On

RewriteRule ^(scripts|css|images)/(.+)\.(.+)\.(js|css|jpg|gif|png)$ $1/$2.$4 [L]

RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d

RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]

Unser Layout-Template kann nun den neuen, selbst geschriebenen Helfer wie folgt verwenden.

Wir werden den View-Helfer nicht gemeinsam mit URIs von YUI CSS verwenden, da sie bereits eine Referenz auf eine Versionsnummer eingebaut haben. Wir können die URIs manuell im Layout-Template ändern ohne weitere Maßnahmen setzen zu müssen, wenn eine neue Version des CSS-Frameworks veröffentlicht wird. Behalten wir das Markup unseres Layouts im Auge und sehen wir uns an, wie der Quelltext unseres <head>-Abschnitts momentan aussieht:

Wie Sie sehen können, befindet sich im URI von style.css nun wie erwartet ein Bearbeitungsdatum im Query-String.

Falls Sie sich fragen, wie man die Verwendung der Header Expires und Cache-Control in der Praxis umsetzt: normalerweise wird das über die Konfiguration Ihres virtuellen Hosts geregelt. Sie müssen zuvor in Ihrer Webserverkonfiguration das Apache-Modul mod_expires aktivieren.

Hier ist eine Beispielkonfiguration für den virtuellen Host des Blogs. Er implementiert einen Expires-Header, der sechs Monate nach dem ersten Zugriff des Clients auf die statische Datei wirksam wird. Ich habe zwei Regeln hinzugefügt, um die Optionen zu illustrieren - die eine bestimmt die betroffenen Dateien über einen regulären Ausdruck, die andere wählt die Dateien explizit über den Dateityp aus. Die Regeln werden in eine Bedingung eingebettet, damit sie ignoriert werden, falls das benötigt Modul von Apache nicht geladen wurde. Es ist sehr wichtig, dass Sie folgendes in Erinnerung behalten: falls Sie als URI zu einer Ressource einen Query-String ohne das Bearbeitungsdatum verwenden, wird der Client diese Ressource für sechs Monate zwischenspeichern und während dieser Zeit nie nach Aktualisierungen fragen, solange er nicht seinen Cache verliert oder ein kompletter Reload erzwungen wird (z.B. mittels CTRL+SHIFT+R in Firefox).

<VirtualHost *:80>
    ServerName zfblog.tld
    DocumentRoot /home/padraic/projects/zfblog/public

    <Directory "/home/padraic/projects/zfblog/public">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride all
        Order deny,allow
        Allow from all

        <IfModule mod_expires.c>
            ExpiresActive on
            <FilesMatch "\.(ico|jpg|jpeg|png|gif)$">
                ExpiresDefault "access plus 6 months"
            </FilesMatch>
            ExpiresByType text/css "access plus 1 year"
            ExpiresByType text/javascript "access plus 1 year"
        </IfModule>

    </Directory>

</VirtualHost>

Diese Konfiguration bewirkt, dass alle Dateien dieser Dateitypen mit zwei neuen Headern an den Client ausgeliefert werden. Zum Beispiel:

Cache-Control: max-age=31536000
Expires: Sun, 05 Sep 2010 00:39:27 GMT

Expires gibt ein Datum sechs Monate in der Zukunft an, ab dem die Datei als verfallen angesehen und einmal vom Server neu angefordert werden sollte. Der zweite Header Cache-Control gibt die maximale Lebenszeit der Ressource in Sekunden an. Technisch gesehen ist der Cache-Control-Header der wichtigere, da er in den meisten Fällen die Einstellung des Expires-Headers überschreibt. Sie können dies von Ihrer .htaccess-Datei aus einstellen, indem Sie dieselben Regeln verwenden.

10.9. Das Design anpassen

Bevor wir uns dem maßgeschneiderten Stylesheet zuwenden, nutzen wir zuerst, was wir bereits mit dem Yahoo User Interface CSS eingefügt haben. Mit den folgenden Änderungen in unserem Standard-Layouttemplate in /application/views/layouts/default.phtml verpassen wir dem Blog ein typisches Website-Layout mit einem Kopf- und Fußbereich, einem Hauptteil und einer linksseitigen Navigation für zusätzliche Navigationslinks oder ergänzende Inhalte.

Da wir HTML 5 verwenden, setzen große Teil des Markups diese neuen Elemente ein - außer dort, wo das Markup eindeutig nur dazu benötigt wird, das Raster-Layout von YUI CSS zu verwenden. In diesen Fällen habe ich das normale <div>-Element verwendet. Dadurch behalten wir den Satz logisch richtiger Elemente bei, wobei es nur zwei unterschiedliche <section>-Elemente gibt: eines für den Hauptinhalt, das andere für die linke Seitenleiste, die ich eingeführt habe. <div>-Elemente steigen damit in die langweilige Kategorie der "CSS-Hooks" im Markup ab, die dazu dienen, das Design zu verschönern - nicht gerade der sauberste Ansatz, doch da ich ein CSS-Framework verwende, kann ich damit leben.

Ich habe zudem in Übereinstimmung mit einem weiteren neuen Standard neben HTML 5 einigen Elementen Rollen zugewiesen. Bei dem Standard handelt es sich um Accessible Rich Internet Applications Suite (WAI–ARIA 1.0), der versucht, Webinhalte und -anwendungen für Menschen mit Beeinträchtigungen zugänglicher zu machen. Es handelt sich dabei um eine kleine Geste, die wir machen, während wir HTML 5 adoptieren und ein Layout von Grund auf neu aufbauen. Der Standard definiert einen Satz sogenannter "document landmark roles", die einer Person mit der entsprechenden Software theoretisch erlauben würden, sich direkt den Teilen des HTML-Dokuments zuzuwenden, die für sie interessant sind. Die Rollen sind deswegen sehr intuitiv gehalten: Banner (banner), Hauptinhalt (main), Navigation (navigation), Information zum Inhalt (contentinfo), ergänzende Inhalte (complementary), Suche (search) etc. Es gibt viele Rollen, aber ich verwende nur ein Minimum davon. Als Anreiz für die Verwendung werden "landmark roles" aktuell durch den JAWS 10 screen reader unterstützt.

Ich habe dem Blog einen Untertitel und somit gleich ein neues Element <hgroup> hinzugefügt. Dieses Element gruppiert zusammengehörende Überschriften bzw. Bestandteile der Kopfzeile und wird in der HTML-5-Spezifikation wie folgt definiert.

hgroup

Das Element hgroup wird üblicherweise verwendet, um eines oder mehrere h1- bis h6-Elemente zu gruppieren - zum Beispiel, um die Überschrift eines Abschnitts und einen zugehörigen Untertitel zusammenzufassen.

Laden Sie den URI http://zfblog.tld in Ihrem Browser neu, um zu sehen, welche Auswirkung die Änderungen haben.

Grundlegendes Design der Blog-Index-Seite

Das aktualisierte Design sieht - langsam - nach etwas aus, das wir ertragen könnten, wenn wir endlose Geduld besäßen. Lassen Sie es uns noch etwas verbessern, indem wir diesmal /public/css/style.css bearbeiten.

/**
 * Basis-Elemente
 */
body {
    font-family: Geneva, Verdana, Helvetica, sans-serif;
}
a:link, a:visited {
    color: blue;
    text-decoration: none;
}
a:hover {
    color: red;
    text-decoration: underline;
}

/**
 * HTML-5-Block-Anzeige
 *
 * Von den meisten (wenn nicht allen) Browsern benötigt,
 * bis die Unterstützung für die Elemente hinzugefügt wird.
 */
article, aside, dialog, figure, footer, header, hgroup, nav, section {
    display:block;
}

/**
 * Seiten-Elemente
 */
header#hd {
    text-align:center;
}
footer#ft {
    text-align: center;
    margin-top: 25px;
    border-top: 1px solid lightgray;
    border-bottom: 1px solid lightgray;
}
section#content {
    border-left: 3px double lightgray;
    padding-left: 10px;
}

/**
 * Überschriften
 */
h1, h2, h3, h4, h5, h6 {
    Helvetica,Arial,Calibri,sans-serif;
}
header#hd h1 {
    font-size: 200%;
    margin-bottom: 0;
}
header#hd h2 {
    margin-top: 0.4em;
    font-size: 100%;
}

/**
 * Horizontales Navigationsmenü im Header
 */
header#hd nav {
    border-top: 2px solid #000;
    border-bottom: 2px solid #000;
}
header#hd nav ul {
    list-style: none;
    margin: 0 0 0 20px;
    text-align: left;
}
header#hd nav li
{
    display: inline-block;
    min-width: 50px;
    margin: 0 2px 0 2px;
}
header#hd nav a:link, nav a:visited {
    color: blue;
    display: inline-block;
    height: 20px;
    padding: 5px 1.5em;
    text-decoration: none;
}
header#hd nav a:hover {
    background-color: lightgray;
}

/**
 * Vertikale Sidebar für Menü/Links
 */
section nav h2 {
    font-size: 120%;
}
section nav ul {
    list-style: none;
    width: 100%;
    margin: 0 auto;
}
section nav ul li
{
    float: left;
    display: inline;
    margin: 0;
}

/**
 * Styling für die Artikel
 */
article header {
    margin-bottom: 1em;
}
article header h2 {
    border-bottom: 1px dashed gray;
    font-size: 130%;
    color: green;
    margin-bottom: 0.5em;
}
article header p {
    font-style: italic;
}

Somit haben wir ein sehr einfaches, aber akzeptables Design, mit dem wir fürs Erste arbeiten können.

Finales Design der Blog-Index-Seite

10.10. Fazit

In diesem Kapitel haben wir die Grundlagen abgedeckt, um ein Webdesign zu erstellen und es unter Einsatz von Zend_View, Zend_Layout und einer Zahl von View-Helfern in das Templatingsystem des Zend Framework zu migrieren. Wir werden in den folgenden Kapiteln noch viel mehr auf die Erstellung von eigenen View-Helfern eingehen. Darüber hinaus haben wir die Grundlagen von HTML 5, dem kommenden neuen Standard für HTML, kennengelernt und uns mit den Implikationen auseinandergesetzt, die sich aus der Implementierung des Standards momentan ergeben, darunter der Notwendigkeit der Verwendung eigens geschriebener View-Helfer. Ich habe aber keinen Zweifel, dass sich die Unterstützung von HTML 5 im Zend Framework in sehr naher Zukunft materialisieren wird, also bleiben Sie dran!

In Kapitel 11 werden wir zum nächsten fundamentalen Themenkreis weitergehen. Wir werden Blogeinträge aus unserer Datenbank anzeigen müssen, und natürlich müssen wir erst einmal einen Weg finden, sie zu schreiben! Daher werden wir uns ansehen, wie wir unser bereits vorbereitetes Domain-Model verwenden können und wie wir neue Blogeinträge verfassen können, indem wir Formulare mittels Zend_Form generieren.