Table des matières
Au cours de ce chapitre,
    nous mettrons en place un simple design web pour notre blog. De toute
    évidence, cela revient à la couche Vue du design pattern d'architecture
    Modèle-Vue-Contrôleur (MVC). D'un point de vue plus bas niveau, cela
    correspond au markup HTML et au styling CSS que le blog utilisera. Comme
    avec beaucoup d'autres Frameworks, toutes les balises seront stockées sous
    forme de templates au sein de notre application, qui seront rendus à la
    fin de toute requête, et envoyés comme réponse à l'utilisateur. Les choses
    deviennent plus intéressantes si vous considérez qu'une page HTML complexe
    peut être découpée en éléments réutilisables, ou même conditionnellement
    réutilisables. Par exemple, il est probable que toute page HTML ait des
    éléments réutilisables tels une en-tête ou un pieds de page, un menu de
    navigation, un chemin de fer, ... A côté de ces éléments, vous aurez du
    contenu dynamique et d'autres types d'éléments qui ne seront requis que
    pour certaines pages spécifiques. A d'autres endroits, vous aurez besoin
    d'appliquer des règles de présentation spécifiques, ou d'insérer du
    contenu formaté depuis votre base de données. Utiliser les fonctionnalités
    de templating de Zend Framework via Zend_View peut
    réduire la duplication de tout ce code de balisage et de traitement de
    templates en utilisant des aides de Vues, des Partials, et des
    Placeholders, ainsi que Zend_Layout, pour tout le
    markup externe commun comme les balises <head>,
    <title>, <html> et d'autres éléments
    incluant les liens vers les feuilles de styles CSS ou les fichiers
    Javascript qui ont besoin d'être inclus.
Je ne suis pas un très
    bon web deisgner (désolé !), donc, pour notre application de blogging, je
    vais rester sur un design relativement simple que quelqu'un d'autre pourra
    améliorer ultérieurement. Les balises utilisées pour le blog
    correspondront à du HTML 5. HTML 5 est la prochaine révision majeure de
    HTML, et il me semble intéressant de réaliser quelques expérimentations
    avec le nouveau standard. Puisque le projet XHTML 2.0 a été abandonné
    (pour l'instant, du moins), c'est aussi le prochain pseudo-remplacement
    majeur pour XHTML. Notons que HTML 5 peut techniquement être envoyé sous
    forme d'une sérialisation basée sur HTML ou sur XML (autrement dit,
    text/html ou application/xml+xhtml, avec les
    règles de syntaxe que chacune de ces possiblités implique). En fonction du
    support par les navigateurs (IE n'accepte pas le content type XML), seule
    la sérialisation HTML est réellement viable, même si j'utilise autant que
    possible des conventions XHTML, telles que fermer tous les tags et
    utiliser des noms d'attributs et de balises en minuscules, puisque je suis
    plus familier avec celles-ci.
Mon utilisation de HTML
    5 devrait être considérée comme expérimentale. Ce n'est pas encore une
    recommandation finale, et tous les navigateurs ne le supportent pas. Vous
    devriez utiliser Firefox 3.5, Internet Explorer 8, ou tout autre version
    de navigateur plus récente lorsque vous travaillez avec du HTML 5 - quoi
    que ce soit d'autre plus ancien n'aura un support que des plus imparfait.
    Cela dit, HTML 5 est en parti rétro-compatible avec HTML 4. Il nécessite
    quelques astuces sous Internet Explorer lorsque l'on souhaite styler un
    document (IE ne reconnait pas les nouveaux éléments HTML 5 et ne peut les
    styler - contrairement aux autres navigateurs), mais nous résoudrons ce
    problème un peu plus tard, en utilisant une petite bibliothèque Javascript
    bien pratique, html5shiv.
Pour faciliter la mise
    en forme, j'utiliserai aussi le Framework Yahoo User
    Interface (YUI) Library's CSS. J'utiliserai ceci pour normaliser les
    comportements entre tous les principaux navigateurs (notamment, types et
    tailles de polices, espacements et marges des éléments, etc.).
    J'utiliserai aussi son implémentation de grilles pour mettre en place des
    sections de pages plus facilement. YUI CSS est un des nombreux Frameworks
    CSS disponibles. Dans le passé, j'ai déjà utilisé Elastic et Blueprint CSS. Ces
    Frameworks sont principalement utilisés pour économiser du temps de
    développement, et (espérons le) écrire moins de CSS - un facteur important
    pour moi, puisque mes talents de designer laissent quelque peu à désirer,
    et que je pourrais passer des heures à jouer avec du paramétrage de CSS
    !
Je n'aime vraiment pas
    éditer trop de CSS lorsque je peux convaincre un singe avec un gros sac de
    cacahuètes de faire un meilleur boulot que moi à ce niveau. Enfin, ça ou
    un web designer professionnel (probablement meilleur que le singe). Pour
    le moment, je n'ai besoin que d'un design par défaut qui soit correct et
    ait l'air à peu près OK au premier coup d'oeil. Pour cet article, nous
    allors voir comment créer un style de blog par défaut, utiliser quelques
    contenus de remplissage, et finalement coder le design en utilisant des
    templates Zend_View.
Nous autres développeurs PHP avons utilisé des moteurs de templating depuis l'aube de PHP. En fait, PHP lui-même est un moteur de templating, et cela était la raison d'être de toute son existence : vous pouvez mélanger du PHP au sein de votre code HTML très facilement. Un point sur lequel nous n'avons pas été très bon est de fixer une limite entre la présentation (balises / formatage) et la logique de présentation (qui regroupe la logique de génération des balises, le formatage des éléments, ou même les accès au Modèle). Globalement, la seule distinction a été de séparer le PHP du HTML, ce qui rate complètement l'objectif, puisque vous ne faites rien d'autre que remplacer PHP par un language à balises personnalisées. Le seul avantage de cette stratégie est qu'elle évite souvent aux web designers d'utiliser PHP lui-même ; ce qui est sans aucun doute un objectif louable. PHP est un moteur de template ; il n'y a donc rien d'incorrect dans le fait de mélanger du PHP et du HTML. C'est une partie du fonctionnement de PHP. Le faire de manière plus délibérée, en tant que composant d'un système de template, nous pousse simplement à le faire de manière correcte.
Zend_View
    est la solution de rendering de templates de Zend Framework, qui formalise
    l'idée de séparer la présentation de la logique de présentation. C'est une
    solution orientée objet qui n'utilise pas un système basé sur des balises
    (comme celles utilisées par Smarty ou les JavaServer Pages (JSP) de Java).
    A la place, les templates rendus par Zend_View
    utilisent directement du PHP. A un plus haut niveau,
    Zend_View encapsule une Vue de l'application, à
    partir du MVC, qui accepte des entrées utilisateur et renvoit une
    présentation de l'application, qu'il s'agisse de HTML, JSON, XML, ...
    basée sur les données qui lui sont passées ou qu'elle a été conçue pour
    récupérer indépendamment. Techniquement, Zend_View
    n'accepte pas d'entrée utilisateur puisqu'elle ceci est géré au niveau de
    la couche Contrôleur - encore qu'il y ait quelques fuites entre les Vues
    et les Contrôleurs à ce niveau, lorsque vous utilisez le MVC pour des
    applications Web et non pour des applications de bureau, du fait de la
    nature de HTTP ; et, en plus de cela, le Contrôleur est lui-même un
    mécanisme de présentation. Il est strictement lié à la requête courante et
    gére la construction et le renvoi de la réponse.
L'aspect le plus
    important de l'écriture de templates avec Zend_View
    est que c'est une solution orientée objet. Vous pouvez utiliser absolument
    n'importe quel type de valeurs dans un template : des listes, des
    scalaires, des objets, et même des ressources PHP. Il n'y a aucun système
    de tags qui agisse comme intermédiaire entre vous et toute la puissance de
    PHP. Une partie de cette approche OOP est que tous les templates sont
    exécutés au sein de la portée de variables de l'instance courante de
    Zend_View. Pour expliquer ceci, considérons le
    template qui suit :
<html><head><title>My Page</title></head><body><?php echo strtolower($this->myName) ?></body></html>Dans ce template, nous voyons que PHP est utilisé directement. Nous voyons aussi une référence à $this, qui représente l'instance courante de l'objet. Mais le template n'est pas un objet ! D'où est-ce que cela vient ?
Les templates sont
    inclus dans la méthode protégée Zend_View::_run()
    lorsqu'ils sont rendus, et de l'ouput buffering est utilisé pour capturer
    le contenu évalué. Donc, tout contenu de template est en fait traité comme
    du code évalué au sein de Zend_View::_run(), ce
    qui signifie que vous pouvez accéder aux méthodes de la classe, aux
    propriétés (comme la propriété publique
    Zend_View::$myName) et à d'autres fonctionnalités,
    exactement comme si vous écriviez une méthode au sein de la classe
    Zend_View elle-même (ce que vous faites, en réalité
    !).
Comment est-ce que cela
    influence nos pensées à propos de la Vue ? Puisque que tous les templates
    sont, essentiellement, des appels à des méthodes (d'une façon ou d'une
    autre), il semble raisonable de considérer que les templates disposent
    indirectement de tous les avantages offerts par la programmation orientée
    objet. Cela devient plus évident une fois que vous rencontrez plusieurs
    concepts offerts par Zend_View : les aides de vues,
    les Placeholders, et les Layouts. C'est aussi évident par rapport aux
    discussions que nous avons eu jusqu'à présent autour du rôle du Modèle.
    Comme je le faisais remarquer au Chapitre 3:
    Le Modèle, les Vues peuvent accéder directement au Modèle en
    utilisant des aides de Vues, sans avoir besoin de code au niveau du
    Contrôleur pour gérer cette interaction.
Le concept de Layouts
      est implémenté par Zend_Layout. Un Layout est la
      partie de votre présentation qui reste relativement statique entre
      plusieurs (ou toutes les) pages de votre site. Pour un document HTML
      standard, cela incluerait probablement l'en-tête, le pieds de page, le
      menu de navigation, et toute autre section commune à plusieurs pages.
      Puisque ces portions sont en grande partie statiques, il est inutile de
      les dupliquer au niveau des templates de chaque page. Cela serait
      impossible à maintenir, demandant à modifier un nombre incroyable
      d'autres templates pour toute modification. Pour cette raison, nous
      pouvons utiliser Zend_Layout pour créer un
      certain nombre de templates de Layout, au sein desquels la sorties des
      autres templates sera injectée. Cela permet de s'assurer que les
      templates du reste de l'application n'aient pas à se préoccuper du
      layout commun : celui-ci est laissé à la charge de
      Zend_Layout.
Un simple template de Layout pourrait être :
<html><head>    <title>My Page</title></head><body>    <?php echo $this->layout()->content ?></body></html>Comme nous le disions
      dans les chapitres précédentes, le Contrôleur implémente par défaut le
      rendering de la Vue en utilisant l'Action Helper ViewHelper.
      Zend_Layout enregitre un nouvel Action Helper et
      un Plugin FrontController,
      Zend_Layout_Controller_Action_Helper_Layout et
      Zend_Layout_Controller_Plugin_Layout, qui
      permettent respectivement le rendering d'un layout et l'accès à
      l'instance de Layout depuis les Contrôleurs. Nous étudierons les Helpers
      et Plugins plus en détails ultérieurement, mais ils peuvent implémenter
      une méthode de hook qui sera appelée automatiquement par le
      FrontController après certains événements ; dans ce cas, une méthode
      postDispatch(), qui est appelée lorsqu'une
      requête est dispatchée vers un Contrôleur. La sortie de la Vue
      correspondant au Contrôleur est assignée à la propriété publique
      $content du template de Layout, que nous pouvons à
      son tour afficher là où nous le souhaitons dans le template de Layout ;
      entre les balises <body>, dans notre exemple.
Zend_Layout
      vous permet aussi de désactiver le rendering du layout depuis les
      Contrôleurs, en utilisant l'Action Helper enregistré. Ceci est important
      lorsque vous souhaitez renvoyer autre chose que du HTML, comme du JSON,
      par exemple. Encapsuler une sortie JSON ou XML dans un Layout HTML ne
      jouera probablement pas en notre faveur. Vous pouvez aussi changer de
      layout, ou définir différentes variables de layout depuis les
      Contrôleurs, ce qui en fait un système très flexible.
En termes de Design
      Patterns, Zend_Layout implémente une forme souple
      de Two
      Step View telle que décrite par Martin Fowler dans
      Patterns Of Enterprise Application Architecture
      (POEAA). La définition de Fowler du pattern Two Step View dit
      que :
Transforme des données du domaine en HTML en deux étapes : tout d'abord en formant une sorte de page logique, puis en rendant celle-ci en HTML.
De toute évidence,
      dans notre cas, les données métier sont rendues immédiatement en HTML
      avant d'être insérées en un point spécifique du HTML du layout.
      L'utilisation d'une approche Two Step View par les layouts complémente
      l'approche par composition des templates de
      Zend_View, telle que décrite ci-dessous via
      l'utilisation de Partials.
Vous devriez aussi
      noter que bien que Zend_Layout gère tous les
      mécanismes d'affectations de layouts, le template de Layout en lui-même
      est rendu par une instance de Zend_View. Cela
      signifie que vos Layouts peuvent utiliser pleinement les fonctionnalités
      typiques de Zend_View, comme les Partials, les
      Placeholders, et les View Helpers. C'est quelque chose qui peut être
      source de confusions, et peut parfois encourager une solution non
      optimale, que je vais mentionner ci-dessous.
Un aspect de
      Zend_Layout avec lequel il faut être prudent est
      que le Guide de Référence dit que les Layouts et les segments de réponse
      nommés peuvent utilisés en conjonction avec l'Action Helper ActionStack
      pour créer des pages sous forme de widgets, pour lesquelles chaque
      Action rend une Vue qui est insérée dans le template à l'endroit prévu.
      Il y a un point extrêmement important à prendre en compte ici :
      dispatcher une action est une tâche coûteuse. Comme le nom ActionStack
      le suggère, cet Aide d'Action crée une pile d'Actions de Contrôleurs à
      exécuter, requérant à son tour une multitude de dispatches à travers
      quasiment tout le Framework MVC. Ceci devrait être évité, à moins que
      cela ne soit absolument nécessaire. Il n'y a quasiment aucune raison
      d'utiliser ActionStack, puisque tout ce qu'il fait peut être effectué
      tout aussi facilement en utilisant des Aides de Vues, pour un coût en
      performances nettement inférieur. Voici un blog post de Ryan Mauger
      expliquant ceci un peu plus en détails : Why
      the Zend Framework Actionstack is Evil. Notez que cela ne signifie
      pas que vous ne devriez jamais utiliser cette fonctionnalité de Zend
      Framework, mais que vous devriez avoir un besoin clairement identifié,
      et une tolérance certaine envers le coût inévitable que cela implique
      pour les performances.
Les Partials correspondent à un cas d'utilisation très simple. Ce sont des fragments d'un template de plus haut niveau, qui peuvent être rendus, que ce soit une fois ou de manière répétée, en un point précis du template père. Les Partials pourrait aussi être appelés "fragments de template", pour refléter ce comportement. Un cas d'utilisation simple serait la page d'accueil de notre blog, qui peuvent afficher plusieurs articles. Le code HTML pour chaque article est quasiment identique : la seule différence est le texte de l'article inclu. Nous pourrions facilement extraire ce balisage commun d'article vers un partial, et utiliser une boucle pour rendre chaque article de la collection. En plus de réduire la quantité de markup dans un template, et d'offrir un mécanisme simple pour boubler sur une collection de données, les Partials apportent aussi un peu de réutilisabilité. Nous pourrions avoir du balisage pour afficher un article sur la page d'index, la page spécifique à une entrée, et aussi quelques autres emplacements. Utiliser un Partial isole ce markup répété, nous permettant d'effectuer des modifications visibles globalement à partir d'un seul endroit.
Ces fragments de
      templates, couplés avec d'autres mécanismes de génération de balisage,
      constituent une implémentation du design patter Composite
      View défini dans le livre Core J2EE Patterns
      :
Utilise des vues composites qui sont composées de multiples sous-vues atomiques. Chaque composant du template peut être inclu dynamiquement dans l'ensemble, et l'organisation de la page peut être gérée indépendamment du contexte.
Suite à l'examination des Layouts que nous avons mené précédemment, une remarque que je ferais est que le Two Step View est lui-même un sous-modèle du principe de Vue Composite. Fowler a sauté quelques design patterns en rapport avec les Vues, qui sont maintenant communément utilisés, comme Composite View et View Helper ; ceux-ci ont, à la place, été formellement défini dans le livre Core J2EE Patterns quelques temps après.
Les partials de
      Zend_View sont implémentés par les aides de Vues
      Zend_View_Helper_Partial et
      Zend_View_Helper_PartialLoop. Nous verrons les
      aides de vues plus en détail dans la prochaine section. Le premier
      helper rend un template partiel à chaque appel, et peut être utilisé
      soit une seule fois, soit imbriqué dans une boucle au sein du template
      parent. La seconde classe supporte les boucles par un mécanisme interne,
      afin que celles-ci n'aient pas à être imbriquées dans le template
      père.
Le paramètre passé à
      un partial ne peut pas être arbitraire : il doit s'agir d'un tableau
      associatif, ou d'un objet implémentant une méthode
      toArray(). Dans le cas contraire, il sera
      réduit à la liste des propriétés de l'objet en utilisant
      get_object_vars(). A partir du tableau associatif
      obtenu en résultat, les clefs sont utilisées comme noms de propriétés
      locales, et les valeurs correspondantes leur sont assignées.
Considérons l'exemple suivant, où nous passons quelques données simples à un partial non-bouclant :
<?php echo $this->partial('article.phtml', array(    'title'=>'Title',    'content'=>'My Content!')) ?>Le fichier
      article.phtml est construit comme tout autre
      template, hormis le fait qu'il soit appelé comme un partial. Il pourra
      accéder aux données reçues via des propriétés locales de classe à
      travers sa propre instance de Zend_View :
<article>   <h3><?php echo $this->escape($this->title) ?></h3>   <div class="content">       <?php echo $this->content ?>   </div<article>Lorsque nous utilisons
      l'aide partial loop, le paramètre devrait être une liste de données au
      sein desquelles nous bouclerons (chaque enregistrement devant suivre les
      mêmes règles que pour le paramètre d'un partial normal) ou tout objet
      itérable retournant un paramètre de partial valide. Par exemple, la
      classe Zend_Db_Table_Rowset est une collection
      itérable d'objets de type Zend_Db_Table_Row.
      Chaque instance de Zend_Db_Table_Row implémente
      une méthode toArray(). Cela signifie qu'un
      rowset est un paramètre valide pour un partial loop.
Par exemple, nous pourrions utiliser notre partial défini plus haut en passant une liste d'articles, et en utilisant l'aide partial loop :
<?php echo $this->partialLoop('article.phtml', array(    array('title'=>'Title', 'content'=>'My Content!'),    array('title'=>'Title2', 'content'=>'More Content!'))) ?>En plus des listes,
      vous pouvez passer directement des objets, et contourner la tentative de
      transtype d'objet en tableau. Ceci se fait en définissant une clef
      d'objet avant d'appeler les méthodes
      Zend_View::partial() ou
      Zend_View::partialLoop() avec quelque paramètre
      que ce soit. Souvenez-vous qu'un appel à la méthode principale d'une
      aide de Vue sans lui passer de paramètre va généralement juste retourner
      l'objet d'aide de Vue pour permettre l'enchainement de méthodes. Par
      exemple :
<?php echo $this->partialLoop()->setObjectKey('article')    ->partialLoop($articleCollection) ?>Dans l'exemple
      ci-dessous, tous les objets dans l'objet itérable
      Collection (quelque chose que nous pourrions
      implémenter dans notre Modèle) sont ajoutés dans la classe du template
      partial sous une propriété publique nommée
      $article. Au sein du partial, nous pourrions alors
      utiliser quelque chose de ce type :
<article>    <h3><?php echo $this->escape($this->article->title) ?></h3>    <div class="content">        <?php echo $this->article->content ?>    </div></article>Cela n'est pas tout à fait l'exemple le plus court qui soit, cela dit : utiliser des objets signifie que les références au partial sont encore plus longues. Utiliser un tableau peut être plus simple, mais il y a des cas où utiliser des objets en eux-même peut avoir des avantages, puisque l'un objet du Modèle peut porter des références à d'autres objets qui seront chargés paresseusement, comme l'auteur de notre article au chapitre 9.
Les Partials ont deux
      autres aspects importants. Tout d'abord, ils travaillent dans une portée
      de variables différente de celle du template père. Dans la pratique,
      cela signifie qu'ils ne connaissent que les données qui leur sont
      passées spécifiquement lorsque vous appelez les méthodes
      Zend_View::partial() ou
      Zend_View::partialLoop() (celles-ci étant
      respectivement mappées vers les méthodes
      Zend_View_Helper_Partial::partial() et
      Zend_View_Helper_Partial::partialLoop(), qui
      sont les méthodes principales des aides de vues). Ils n'ont pas
      connaissance de toute donnée définie dans la Vue parente par un
      Contrôleur, ou, dans le cas d'un fragment inclu au second niveau, de
      toute donnée définie dans le père du partial. Cette restriction imposée
      encourage une approche plus orientée-objet du templating, et évite les
      fuites de données entre de multiples templates et fragments de templates
      (autrement dit, le problème de l'utilisation de variables globales).
      Ceci est tout simplement une bonne pratique : nous ne permettons (je
      l'espère) pas aux variables globales de contaminer l'espace de noms de
      nos objets, alors pourquoi devrions-nous le permettre pour nos templates
      ? Tous les templates devraient être isolés les uns des autres pour
      éviter de se retrouver avec des dépendances qui rendraient les tests, la
      maintenance, et les évolutions, plus coûteux.
Pour terminer, vous pouvez imbriquer un partial au sein d'un autre partial. Vous pourriez même en avoir toute une hiérarchie, si cela s'avère nécessaire. Tout dépend réellement de ce que vos partials représentent : un petit bout de balises, ou une portion plus importante d'un template de page ou de layout. La nature du jeu de templating est de composer une seule vue à partir d'un grand nombre de parties. Comment est-ce que ces parties seront construites ou divisées pour éviter la duplication de balisage et l'encapsulation de fonctionnalités réutilisables ne dépendra que de vos besoins.
Si les partials sont le moyen de découper le balisage en composants réutilisables, leurs contreparties pour ce qui est de la logique de présentation sont les aides de Vues. Une aide de vue est utilisée pour créer une opération réutilisable appliquée à un template. Par exemple, supposez qu'un partial ait reçu en paramètre un objet métier représentant un commentaire posté par un utilisateur. Celui-ci contient un nom, une adresse email, une URL, un peu de texte, et une date. Les quatre premières données sont simples... Mais comment allez-vous formater la date pour la présentation ? Votre partial pourrait inclure un peu de code PHP pour analyser la date (en fonction du standard de date utilisé), et la recomposer sous forme d'une date lisible. Mais les articles ont une date de publication et une date de modification ; même chose pour les commentaires, d'ailleurs. Allez-vous répéter ce code PHP partout ?
De toute évidence,
      cela ne serait pas une solution des plus maintenable. Si quelque chose
      change et que vous avez à reconfigurer la logique d'analyse utilisée,
      vous pourriez avoir une quantité importante de templates à éditer. Il
      serait bien plus simple d'encapsuler cette fonctionnalité PHP dans une
      aide de Vue, une classe réutilisable qui peut être appelée depuis
      n'importe quel template à n'importe quel moment. Vous pourriez
      implémenter ceci sous la forme d'une classe
      ZFExt_View_Helper_FormatDate (une aide
      personnalisée), contenant une méthode
      formatDate() qui accepterait en paramètre une
      date quelconque, et retournerait une date formatée en accord avec un
      schéma spécifié, tel que "YYYY-MM". De la sorte, vous auriez une classe
      facile à réutiliser, qui pourrait bénéficier de tests unitaires, et même
      être portée vers, et réutilisée dans, d'autres projets.
Comme je le disais
      aussi dans le Chapitre 3: Le Modèle,
      les aides de Vues peuvent aussi être employées pour encapsuler un Modèle
      utilisant, par exemple, l'implémentation d'un Data Mapper proposée au
      chapitre précédent. Votre aide peut maintenant interroger la couche
      Modèle pour obtenir des données, les formater et ajouter du code de
      balisage, et retourner le résultat final pour qu'il soit immédiatement
      inséré à l'endroit où il est attendu dans le template. La principale
      difficulté avec cette approche est qu'il faut se souvenir qu'une aide de
      Vue ne devrait jamais modifier le Modèle avec lequel elle interagit :
      cela revient au Contrôleur qui se charge de la gestion des entrées
      utilisateur. En conséquence, les aides de Vues sont une fonctionnalité
      réellement utile. Vous pouvez décharger du formatage spécialisé, la
      génération de menus, l'interrogation du modèle, la décoration avec du
      balisage, et encore plein d'autres choses vers les aides de Vues.
Les aides de Vues sont
      aussi définies comme un pattern précis, nommé design pattern View
      Helper, dans le livre Core J2EE
      Patterns.
Tous les aides de vues
      suivent une conception similaire. Elles implémentent une méthode portant
      un nom correspondant à celui de la classe, ce qui signifie que
      ZFExt_View_Helper_FormatDate définirait une
      méthode
      ZFExt_View_Helper_FormatDate::formatDate().
      Cette méthode "primaire" va généralement accepter des paramètres sur
      lesquels l'aide travaillera, et retournera le résultat. Pour des aides
      plus compliqués, avec plusieurs méthodes publiques, cette méthode
      primaire peut aussi retourner l'objet lui-même. Puisque
      ZFExt_View_Helper_FormatDate serait un aide de
      vue personnalisé, vous auriez aussi besoin de dire à
      Zend_View où le trouver, et quel serait son
      espace de noms.
Prenons un exemple
      simple. Nous avons décidé de prendre en compte quelques analyses des
      performances de chargement de nos pages web pour assurer que les
      fichiers javascript et CSS peuvent être mis en cache par les clients
      pour une durée étendue. Ceci est recommandé par l'article Yahoo
      Performance Best Practices dans la section Add
      an Expires or a Cache-Control Header. Donc, nous y allons, et
      configurons Apache pour utiliser une en-tête Expires loin dans le futur
      lorsque nous servons des fichiers statiques tels des CSS ou du
      Javascript. Nous faisons maintenant face à une autre problème : si le
      client cache ces fichiers pour une éternité, comment pouvons-nous gérer
      les modifications et mises à jour ? Une stratégie communément utilisée
      est d'ajouter une Query String aux URIs des fichiers statiques, qui sera
      mise à jour dans notre code de balisage lorsque les fichiers référencés
      sont modifiés. Les ressources en cache utiliseront l'URI d'origine (vous
      pouvez cacher les URIs avec une Query String si une en-tête Expires
      explicite est définie), mais lorsqu'elle changera, le client
      téléchargera depuis la nouvelle URI, puisque la Query String aura changé
      ; autrement dit, puisqu'il s'agira d'une nouvelle ressource.
Cela entrainerait la
      création d'une URI de la forme
      http://zfblog.tld/css/style.css?1185131970. La Query String
      finale est, de toute évidence, un timestamp représentant la date de
      modification du fichier. Vous pourriez tout aussi simplement utiliser un
      numéro de version. Ecrivons une aide de vue qui assignera
      automatiquement ces Query Strings, de façon à ce que nos mises à jour
      altèrent les URIs directement, automatiquement, sans aucune intervention
      manuelle. Nos aides personnalisés peuvent hériter de
      Zend_View_Helper_Abstract qui offre un certain
      nombre de fonctionnalités standard qui peuvent être utiles (elles ne le
      sont pas, ici) :
<?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;    } }Notre nouvelle aide de vue est courte et va droit au but. Elle accepte le chemin relatif vers le fichier sur notre serveur, tel que nous l'utiliserions normalement dans nos balises, vérifie la date de dernière modification du fichier, et ajoute cette date au chemin sous forme d'un timestamp. Le chemin obtenu en résultat peut alors être passé à n'importe quelle autre aide (par exemple, les aides de Vues HeadLink ou HeadScript), ou renvoyé directement dans le code de balisage correspondant. Voici un fragment de template utilisant notre nouvelle aide de vue :
<link rel="stylesheet" href="<?php echo $this->appendModifiedDate('/css/style.css') ?>" type="text/css" media="screen" />En plus de permettre
      l'écriture d'aides de vues personnalisées, Zend Framework fourni un
      certain nombre d'aides de vues standard pour des tâches communes telles
      que la gestion de génération de balises (Pour les éléments de
      formulaires et d'en-tête, par exemple). Nous verrons prochainement un
      certain nombre de celles-ci, qui utilisent une autre fonctionnalité de
      Zend_View, nommée Placeholders.
Les Placeholders
      répondent à un besoin spécifique du templating avec Zend Framework. Ils
      permettent aux templates d'accèder à un registre partagé de données,
      indépendamment de leur portée de variables. C'est extrêmement proche du
      Pattern Registry, qui est souvent implémenté avec Zend Framework en
      utilisant Zend_Registry, mais a un cas
      d'utilisation plus spécifique ici. Considérez un scénario où vous
      découvrez qu'une page web a besoin d'utiliser une portion donnée de
      Javascript, chargée dès que possible. Votre layout ne la contient pas
      puisqu'elle n'est tout simplement pas nécessaire pour tout. Les
      Placeholders peuvent résoudre ce problème en permettant aux templates et
      aux Partials d'ajouter des valeurs à un Placeholder, une clef dans ce
      registre partagé. Lorsqu'un template parent ou un layout sera rendu, il
      pourra rendre rendre les données du Placeholder là où il le spécifie.
      Pour du Javscript, ceci est géré par un Placeholder spécialisé
      implémenté par Zend_View_Helper_HeadScript, avec
      lequel vous pouvez ajouter à la fin ou au début, re-définir ou définir à
      une position particulière toute portion de Javascript dont vous avez
      besoin, à partir de n'importe quel sous-template, avant qu'il ne soit
      finalement rendu dans un layout.
En plus d'offrir une sélection de Placeholders spécialisés comme HeadLink ou HeadMeta, vous pouvez utiliser l'aide de vue Placeholder de base pour toute autre donnée qui aurait besoin d'être communiquée entre templates. Jetons un coup d'oeil à un exemple où nous avons un layout qui peut insérer la sortie d'un Placeholder. Le layout fournit un point de rendering qui permet à tout template de modifier le texte correspondant à une en-tête standard :
<html><head>    <title>My Page</title></head><body>    <h1><?php echo $this->placeholder('pageHeader') ?></h1>    <?php echo $this->layout()->content ?></body></html>Nous pouvons maintenant définir cette valeur à partir de n'importe quel autre template (Partials inclus) :
<?php $this->placeholder('pageHeader')->set('About') ?><p>My name is Joe. Joe Bloggs.</p>Contrairement aux implémentations de Placeholder plus spécifiques, l'aide Placeholder générique peut être utilisée pour n'importe quelle valeur. S'il s'agit d'une valeur spécifique qui suit des règles de formatage spécifiques, vous pouvez sous-classer l'aide générale pour aussi ajouter ce formatage au moment où les données sont affichées dans le template. Par exemple, implémentons une aide de Vue personnalisée pour formater l'en-tête d'une page en utilisant HTML 5 :
<?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>";    } }C'est un exemple très
      simple qui permet tout juste de créer un wrapper concret autour du
      Placeholder correspondant à la clef "PageHeader". Il encapsule aussi le
      titre de la page entre des balises <h1>. Voici
      maintenant notre layout et notre template en page en utilisant notre
      nouveau Placeholder concret :
<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>Vous avez peut-être
      remarqué que tout au long de ce livre j'ai montré une nette préférence
      pour l'utilisation de tags PHP longs ; c'est-à-dire <?php
      (echo) ... ?> ; plutôt que pour les tags courts,
      <?(=) ... ?>. Les short tags sont un point de
      configuration optionnel de PHP. Bien que de nombreux serveurs aient les
      short tags activés pour améliorer la compatibilité avec les applications
      qui les utilisent, beaucoup de serveurs en configuration par défaut ne
      les supporteront pas, puisqu'ils sont désactivés dans le fichier de
      configuration recommandé de PHP,
      php.ini.recommended. En plus de cela, nous savons
      que PHP 6 pourrait complètement déprécier les short tags pour résoudre
      un conflit avec les déclarations XML qui utilisent une forme similaire :
      <?xml ... ?>. Traditionnellement, les déclarations
      XML ont été echo-ées si l'on générait du XML à partir d'un template en
      utilisant des short tags. Personnellement, je considère que la probable
      dépréciation en PHP 6 est une mauvaise chose. Ecrire <?php echo
      ... ?> plus de quelques fois est tout simplement ennuyeux,
      mais, encore une fois, puisqu'il s'agit d'un point de configuration
      optionnel, les applications distribuées ne devraient de toute manière
      jamais l'utiliser. En tout cas, il vaut mieux en rester à ce qui marche
      dans tous les cas, plutôt que de se fier à quelque chose qui risque de
      casser plus tard.
Zend Framework a
      autorisé l'usage de short tags, indépendamment de la configuration, en
      utilisant un stream wrapper. Celui-ci est activé si l'option short tags
      est désactivée dans votre fichier de configuration
      php.ini (autrement dit, en fonction du flag
      short_open_tag) et si vous avez défini l'option
      useStreamWrapper à true. Vous pouvez effectuer
      ceci en utilisant la clef d'option useStreamWrapper dans
      votre ficher application.ini ou en appelant la
      méthode Zend_View::setUseStreamWrapper() dans
      votre bootstrap. Utiliser le stream wrapper aura certainement un impact
      sur les performances, ce qui signifie que si vous avez réellement besoin
      des short tags, il vaut mieux juste les activer dans votre configuration
      de PHP.
Mettre en place la
    nouvelle application de blogging n'est pas horriblement difficile : vous
    n'avez qu'à copier les exemples Hello World vers le répertoire project de
    l'application de blog, où vous avez actuellement le début de Modèle de
    notre application. Cela fera un excellent point de départ pour commencer.
    Vous pouvez souhaiter ajouter un nouveau nom de domaine local et un
    nouveau Virtual Host spécifiquement pour le blog, en suivant les
    instructions du chapitre correspondant ainsi que celles de l'annexe dédiée
    à la configuration d'Apache et des Virtual Hosts. Pour les besoins de ce
    livre, j'utiliserai le nom de domaine http://zfblog.tld.
Aucune autre modification n'est nécessaire. Le reste du chapitre va détailler où aller à partir de là.
La page d'index de notre
    blog va, comme nous aurions pu nous y attendre, être la page qui affichera
    une liste d'entrées. En utilisant HTML 5, commençons par jeter un coup
    d'oeil aux tags HTML d'ouverture. Par la suite, nous ajouterons le style
    et nous soucierons de l'ajout de plus d'éléments et d'informations à la
    page. Pour l'instant, nous éditerons le fichier
    /application/views/scripts/index/index.phtml :
<!DOCTYPE html><html><head>    <meta name="language" content="en" />    <meta charset="utf-8"/>    <title>Pádraic Brady's Blog</title>    <link href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="screen" rel="stylesheet" type="text/css" />    <link href="http://yui.yahooapis.com/2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />    <!--[if IE]><script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]--></head>HTML 5 utilise utilise
    un Doctype simplifié qui ne fait pas grand chose de plus que de dire qu'il
    s'agit de HTML. Il n'y a pas de numéro de version ou d'autre information
    attachée ni nécessaire. Puisque nous savons que nous pouvons définir le
    Doctype utilisé par Zend_View et tout composant qui
    en dépend, nous pouvons afficher cela dans notre template en utilisant
    l'aide de Vue Zend_View_Helper_Doctype. Le support
    HTML 5 de Zend Framework va jusqu'au Doctype qui dit aux aides de Vues
    dépendantes qu'elles doivent utiliser des balises de type HTML 4. En fait,
    HTML 5 peut aussi être écrit d'une manière similaire à XHTML ; autrement
    dit, en utilisant des noms de balises et d'attributs en minuscules, en
    délimitant les valeurs d'attributs avec des guillemets doubles, en fermant
    toutes les balises, etc. Ceci est ma méthode préférée lorsqu'il s'agit
    d'écrire du HTML 5, car cela semble plus propre, et je n'ai pas besoin de
    trop m'éloigner du XHTML avec lequel je me suis familiarisé. Zend
    Framework pose alors un problème mineur : il ne fait pas la distinction
    entre HTML 5 et XHTML 5 (HTML 5 écrit avec des règles de formatage XML).
    Nous résoudrons ce problème en écrivant une Aide de Vue personnalisée,
    pour ajouter le support pour un Doctype XHTML 5.
Comme avec tout document
    HTML/XHTML, l'élément racine est <html>. Il est
    immédiatement suivi par un élément <head> qui contient
    tout élément <link>, <meta>,
    <title> ou <style> dont nous aurions
    besoin. Ceux-ci peuvent être écrit avec n'importe que style typique, même
    si, comme je l'expliquais au-dessus, je préfères les habitudes XHTML. HTML
    5 ajoute ici une fonctionnalité : un nouvel attribut pour l'élément
    <meta>, nommé charset. Ce nouvel attribut
    est conçu comme moyen simplifié d'indiquer au parseur quel encodage de
    caractères est utilisé par le document, et agit donc comme remplacement
    valide de l'élément couramment utilisé <meta>
    Content-type. Encore une fois, Zend Framework ne supporte pas encore ceci,
    donc nous ajouterons une seconde aide de Vue pour l'implémenter.
Vous pouvez remarquer
    que, bien que nous puissions écrire toutes ces balises directement dans
    nos templates, je préféres considérer que nous les implémenterons sous
    forme de Views Helpers. Ceci est une bonne pratique, puisqu'utiliser les
    Aides de Vues permet aux autres templates de modifier ce qui apparait dans
    <head> en ajoutant au début, à la fin, ou même en
    re-définissant quelles feuilles de styles, scripts, et meta informations
    sont initialement définies. Vous pourriez voir à quel point ceci est
    intéressant dans des situations où une page spécifique a besoin de styling
    supplémentaire ou d'un ensemble différent de code Javascript, qui ne soit
    pas requis pour toutes les pages.
J'ai aussi ajouté deux feuilles de style CSS issues du Framework Yahoo User Interface. Elles sont chargées directement depuis un serveur de Yahoo, afin que nous n'ayons pas à stocker quoi que ce soit localement. La première de celles-ci est une version minifiée de trois feuilles de styles fusionnées : reset, fonts, et grid. Les deux premières assurent que tous les principaux navigateurs afficheront la même page, en éradiquant les différences de rendering que chacun implémente par défaut. Il n'y a rien de tel que d'essayer de gérer les inconsistences entre navigateurs sur des simples points de style. Les styles de grilles seront utilisés plus tard pour créer un layout en colonnes plus facilement. La seconde feuille de styles inclue, base, impose un socle de styling de base pour tous les éléments principaux. A ce rythme, je ne devrais pas avoir beaucoup de CSS à écrire, hormis ce qui s'applique spécifiquement à mes styles préférés pour le blog.
Finalement, nous avons
    inclu un fichier Javascript issu du projet html5shiv project.
    C'est une portion de Javascript relativement simple utilisée pour
    s'assurer que Internet Explorer puisse reconnaitre les nouveaux éléments
    HTML 5. Autrement, il ne nous serait pas possible de les styler. Ceci
    étant uniquement requis pour Internet Explorer, nous avons entouré cette
    inclusion par un couple de conditions reconnaissant toutes les versions
    d'Internet Explorer, mais excluant tout autre navigateur.
Ajoutons maintenant une section header représentant le titre de la page et le bandeau de navigation supérieur :
<!DOCTYPE html><html><head>    <meta name="language" content="en" />    <meta charset="utf-8"/>    <title>Pádraic Brady's Blog</title>    <link href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="screen" rel="stylesheet" type="text/css" />    <link href="http://yui.yahooapis.com/2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />    <!--[if IE]><script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]--></head> <body>     <header>        <h1><a href="/">Pádraic Brady's Blog</a></h1>        <nav>            <ul>                <li><a href="/">Home</a></li>                <li><a href="#">About</a></li>                <li><a href="#">Projects</a></li>                <li><a href="#">Contact</a></li>            </ul>        </nav>    </header>Avec HTML 5, nous
    pouvons déjà voir quelques uns des nouveaux éléments. Notre section
    d'en-tête est maintenant entourée par la balise
    <header> qui entoure notre titre d'en-tête traditionnel
    <h1>, et aussi un autre élément, une balise
    <nav> qui entoure notre menu de navigation supérieur.
    Le point probablement le plus agréable ici, en dehors de la signification
    sémantique, est que nous pouvons nous débarasser de tout un tas d'éléments
    <div> utilisant des attributs class pour
    nous indiquer ce qu'ils représentent.
HTML 5 fourni quelques définitions pour ces nouveaux éléments :
L'élément header représente un groupe d'aides d'introduction ou de navigation. Un élément header est utilisé pour contenir, en général, l'en-tête de la section (un élément h1-h6 ou un élément hgroup), bien que cela ne soit pas requis. L'élément header peut aussi être utilisé pour encapsuler le sommaire d'une section, un formulaire de recherche, ou tout autre logo significatif.
L'élément nav représente une section de page qui contient des liens vers d'autres pages ou vers des parties au sein de la page : une section avec des liens de navigation. Tous les groupes de liens au sein d'une page n'ont pas besoin d'être dans un élément nav ; seules les sections qui constituent des blocs de navigation majeurs sont appropriées pour l'élément nav. En particulier, il est commun pour les bas de pages de contenir une liste de liens vers diverses parties clefs du site, mais l'élément footer est plus approprié dans ce genre de cas, et aucun élément nav n'est nécessaire pour ces liens.
Ensuite, nous voulons que le corps principal du balisage indique où est-ce que nos entrées de blogs seraient rendues, suivies par la section de bas de page finale qui contiendra une notice de copyright et des crédits lorsque cela sera nécessaire :
<!DOCTYPE html><html><head>    <meta name="language" content="en" />    <meta charset="utf-8"/>    <title>Pádraic Brady's Blog</title>    <link href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="screen" rel="stylesheet" type="text/css" />    <link href="http://yui.yahooapis.com/2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />    <!--[if IE]><script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]--></head> <body>     <header>        <h1><a href="/">Pádraic Brady's Blog</a></h1>        <nav>            <ul>                <li><a href="/">Home</a></li>                <li><a href="#">About</a></li>                <li><a href="#">Projects</a></li>                <li><a href="#">Contact</a></li>            </ul>        </nav>    </header>     <section>        <article>            <header>                <h2>One Day...Revisited</h2>                <p>Posted on <time datetime="2009-09-07">Tuesday, 08 September 2009</time></p>            </header>            <div>                <p>I forgot!</p>                <p>I already am writing a book!</p>            </div>            <footer>                <address>By Pádraic</address>            </footer>        </article>         <article>            <header>                <h2>One Day...</h2>                <p>Posted on <time datetime="2009-09-07">Monday, 07 September 2009</time></p>            </header>            <div>                <p>I will write a book</p>            </div>            <footer>                <address>By Pádraic</address>            </footer>        </article>    </section>     <footer>        <p>Copyright © 2009 Pádraic Brady</p>        <p>Powered by <a href="http://www.survivethedeepend.com">ZFBlog        </a></p>    </footer> </body></html>Et bien, en voila des
    nouveautés ! HTML 5 a ajouté de beaucoup d'autres nouveaux éléments, et
    cela commence à être évident, maintenant. De nombreux endroits où nous
    nous serions attendu à voir des balises <div> avec un
    attribut class faisant référence à la raison d'être de la
    <div>. Dans une page vide typique, vous pourriez vous
    attendre à tout simplement encapsuler le contenu avec des balises
    <article>, puisque le contenu serait une seule entrée.
    Pour notre blog, cela dit, nous savons que chaque entrée rendue sur la
    page est elle-même un article, donc nous utilisons de multiples éléments
    <article> et les entourons tous d'un seul élément
    <section> (il pourrait aussi y avoir plusieurs éléments
    <section> si la page était encore plus divisée, et ils
    peuvent être imbriqués ; un peu comme le XML Docbook). Chaque article va
    aussi avoir une en-tête et un pieds de page, et nous pouvons donc utiliser
    les balises HTML 5 correspondant à ces situations. Finalement, toute
    référence aux détails de l'auteur sont encapsulées dans des balises
    <address>. Même si cela peut être source de confusion,
    la balise <address> ne fait pas référence à une adresse
    physique : il s'agit plutôt des autres détails d'une personne, comme son
    nom, son site web, son e-mail, sa description, etc.
Penchons-nous maintenant sur les définitions de ces nouveaux éléments à partir des spécifications HTML 5 :
L'élément section représente un document générique ou une section d'application. Une section, dans ce contexte, est un regroupement thématique de contenu, typiquement avec une en-tête, et possiblement avec un pieds de page.
L'élément article représente une section de page qui consiste en une composition qui forme une partie indépendante d'un document, d'une page, d'une application, ou d'un site. Il pourrait s'agir d'un post de forum, d'un article de magazine ou de journal, d'une entrée de Web log, d'un commentaire soumis par un utilisateur, d'un widget interactif, ou de tout autre élément de contenu indépendant. Un élément article est "indépendant" dans le sens où son contenu pourrait être présenté seul, par exemple via un mécanisme de syndication, ou comme composant interchangeable sur une page portail configurable par l'utilisateur.
L'élément footer représente un pieds de page pour son ancêtre de section de contenu le plus proche. Un footer contient typiquement des informations à propos de sa section, comme qui l'a écrit, des liens vers des documents liés, des données de copyright, et équivalents. Les informations de contact appartiennent à un élément address, idéalement lui-même placé dans un footer.
L'élément address représente les informations de contact pour son article le plus proche ou l'élément corps ancêtre. S'il s'agit de l'élément body, alors l'information de contact s'applique au document dans son ensemble. L'élément address ne doit pas être utilisé pour représenter une adresse arbitraire (comme une adresse postale), à moins que cette adresse ne soit effectivement l'information de contact attendue.
L'élément time représente soit une heure sur une horloge 24 heures, soit une date précise dans le calendrier Grégorien, éventuellement accompagnée d'une date et d'une time-zone. Cet élément est pensé comme un moyen d'encoder les dates et heures modernes d'une manière lisible par les machines, afin que les navigateurs puisse proposer leur ajout au calendrier de l'utilisateur. Par exemple, en ajoutant des rappels d'anniversaire, ou des événements planifiés.
Notons tout de même au
    passage que le nouvel élément <time> n'est pas dépourvu
    de problème. Il est strictement lié au calendrier Grégorien, ce qui
    signifie qu'il ne peut pas représenter de date non-grégorienne datant
    d'avant l'introduction de celui-ci. Puisque <time> a
    été pensé pensé pour remplacer l'élément de microformat
    <abbr> dans hCalendar, c'est réellement un échec : il
    ne peut pas représenter de dates historiques. Espérons que la
    recommandation finale de HTML 5 corrigera ce point.
Ouvrez votre navigateur
    et naviguez vers http://zfblog.tld. Vous devriez voir
    le nouveau template rendu avec les styles CSS par défaut de YUI appliqués
    :
Maintenant que nous avons un design de départ, il nous faut identifier quelles portions de ce template sont statiques ; autrement dit, quelles portions ne changeront que peu de page en page. Un coup d'oeil rapide suffit pour voir que pour notre page d'accueil, seules les entrées sont susceptibles de changer. Nous pourrions en avoir un nombre variable à afficher, et leur contenu sera fréquemment mis à jour. Sachant cela, nous pouvons alléger notre template d'index pour n'inclure que les parties qui sont dynamiques. Nous regrouperons momentanément le reste dans un layout.
Voici le template
    /application/views/scripts/index/index.phtml une fois
    revu :
<article>    <header>        <h2>One Day...Revisited</h2>    </header>    <div>        <p>I forgot!</p>        <p>I already am writing a book!</p>    </div>    <footer>        <address>By Pádraic</address>    </footer></article> <article>    <header>        <h2>One Day...</h2>    </header>    <div>        <p>I will write a book</p>    </div>    <footer>        <address>By Pádraic</address>    </footer></article>Déplaçons le code de
    balisage restant vers un nouveau template, auquel nous ferons référence
    sous le terme de Layout, dans
    /application/views/layouts/default.phtml :
<!DOCTYPE html><html><head>    <meta name="language" content="en" />    <meta charset="utf-8"/>    <title>Pádraic Brady's Blog</title>    <link href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="screen" rel="stylesheet" type="text/css" />    <link href="http://yui.yahooapis.com/2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />    <!--[if IE]><script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]--></head> <body>     <header>        <h1><a href="/">Pádraic Brady's Blog</a></h1>        <nav>            <ul>                <li><a href="/">Home</a></li>                <li><a href="#">About</a></li>                <li><a href="#">Projects</a></li>                <li><a href="#">Contact</a></li>            </ul>        </nav>    </header>     <section>        <?php echo $this->layout()->content ?>    </section>     <footer>        <p>Copyright © 2009 Pádraic Brady</p>        <p>Powered by <a href="http://www.survivethedeepend.com">ZFBlog        </a></p>    </footer> </body></html>Vous remarquerez que
    nous faisons maintenant référence à la méthode
    layout() de Zend_View.
    Comme nous l'avons remarqué lorsque nous parlions des aides de Vues, le
    nom de classe d'une aide de Vue reflète sa méthode primaire, qui est
    typiquement utilisée, au minimum, pour obtenir une instance de l'objet
    d'aide de Vue. Ici, nous récupérons une instance de
    Zend_View_Helper_Layout et affichons ce qui est
    contenu dans sa propriété publique $content.
Pour comprendre comment
    notre template d'index est propulsé dans cette propriété, il nous faut
    comprendre que tout le rendering est, par défaut, géré par les Contrôleurs
    en utilisant l'aide de Vue ViewRenderer. Cette aide va rendre un template
    pour le Contrôleur et Action courants vers une réponse.
    Zend_Layout pourra alors intercepter le corps de la
    réponse rendue et l'injecter dans le template de layout. Le layout complet
    sera ensuite rendu à son tour, pour finalement produire la page
    entière.
Vous devriez aussi noter
    que les Layouts, comme tous les templates, sont exécutés au sein de la
    portée de variables d'un objet Zend_View, ce qui
    signifie que toutes les aides de Vues et les méthodes de
    Zend_View sont toujours accessibles depuis vos
    Layouts.
Pour activer
    Zend_Layout, il nous faut le configurer pour qu'il
    soit utilisé. Typiquement, cela passe par un appel à la méthode statique
    Zend_Layout::startMvc(), suivi par toute
    configuration de l'instance ; mais, puisque nous utilisons
    Zend_Application pour notre bootstrapping, nous
    avons juste besoin de définir quelques valeurs de configuration pour une
    Ressource Layout dans /config/application.ini. Les
    deux options requises, et ajoutées en bas de notre section "Options de
    Ressources Standard", définissent le nom du template de Layout utilisé par
    défaut en omettant l'extension de fichier .phtml. La seconde configure le
    chemin sous lequel les templates de Layout peuvent être trouvés :
[production]; PHP INI SettingsphpSettings.display_startup_errors = 0phpSettings.display_errors = 0 ; Bootstrap Locationbootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"bootstrap.class = "ZFExt_Bootstrap" ; Standard Resource Optionsresources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"resources.view.encoding = "UTF-8"resources.modifiedFrontController.contentType = "text/html;charset=utf-8"resources.layout.layout = "default"resources.layout.layoutPath = APPLICATION_PATH "/views/layouts" ; HTML Markup Optionsresources.view.doctype = "HTML5" ; Autoloader OptionsautoloaderNamespaces[] = "ZFExt_" [staging : production] [testing : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1 [development : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1En retournant à votre
    navigateur, rendez-vous vers http://zfblog.tld, et vous devriez
    voir la même page que précédemment, sans aucune différence. Notre template
    d'index est maintenant rendu dans le nouveau Layout.
Pour aller un peu plus
    loin, profitons de cette opportunité pour éditer le second template de
    page de notre application,
    /application/views/scripts/error/error.phtml. Vous
    remarquerez qu'il n'est pas entouré de balises
    <article> puisqu'il s'agit d'un message générique, et
    pas d'un document article à part entière. J'ai aussi fait usage d'une
    en-tête de second niveau <h2> entourée par un élément
    <header> pour l'<article>.
<header>    <h2>An Error Has Occurred</h2></header><div><?php if ($this->statusCode == 500): ?>    <p>We're truly sorry, but we cannot complete your request at this    time.</p>    <p>If it's any consolation, we have scheduled a new appointment for our    development team leader in Torture Chamber #7 to encourage his colleagues    to investigate this incident.</p><?php else: ?>    <p>We're truly sorry, but the page you are trying to locate does not    exist.</p>    <p>Please double check the requested URL or consult your local    Optometrist.</p><?php endif; ?></div>Lancez-vous et essayez
    cette page, en utilisant une URL invalide telle que http://zfblog.tld/foo/bar.
    Rappelez-vous qu'il vous faudra temporairement désactiver la levée
    d'Exceptions dans application.ini pour voir la page
    d'erreur.
Notre Layout est à
    présent en place, mais nous pouvons aller plus loin et nous assurer que
    les templates de pages, ou même les partials, peuvent modifier des
    portions du Layout à la demande, en particulier pour ce qui est des
    feuilles de styles, titres de pages, Javascript et balises meta. Zend
    Framework permet ce genre de modifications via l'utilisation de
    Placeholders, en particulier avec un certain nombre d'implémentations
    concrètes telles que Zend_View_Helper_HeadTitle,
    Zend_View_Helper_Doctype, etc. Comme je le
    mentionnais plus haut, les Placeholders permettent aux templates
    d'ajouter, de supprimer, ou même de remplacer des éléments au sein d'un
    conteneur de données, dont le contenu sera finalement rendu sous sa forme
    finale dans une couche externe de la hiérarchie de templates.
Commençons par modifier
    notre Layout dans
    /application/views/layouts/default.phtml pour
    utiliser plusieurs de ces aides de Vues Placeholders :
<?php echo $this->doctype() . "\n" ?><html><head><?phpecho $this->headMeta()->setIndent('    ') . "\n";echo $this->headTitle('Pádraic Brady\'s Blog')    ->setSeparator(' / ')->setIndent('    ') . "\n";echo $this->headLink()->setIndent('    ')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/base/base-min.css') . "\n    ";echo $this->headScript()->prependFile(    'http://html5shiv.googlecode.com/svn/trunk/html5.js',    'text/javascript',    array('conditional'=>'IE')) . "\n";?></head><body>     <header>        <h1><a href="/">Pádraic Brady's Blog</a></h1>        <nav>            <ul>                <li><a href="/">Home</a></li>                <li><a href="#">About</a></li>                <li><a href="#">Projects</a></li>                <li><a href="#">Contact</a></li>            </ul>        </nav>    </header>     <section>        <?php echo $this->layout()->content ?>    </section>     <footer>        <p>Copyright © 2009 Pádraic Brady</p>        <p>Powered by <a href="http://www.survivethedeepend.com">ZFBlog        </a></p>    </footer> </body></html>Zend_View_Helper_Doctype
    et Zend_View_Helper_HeadMeta sont tous deux
    affichés sans modification, mis à part pour ajouter quelques indentations
    et retours à la ligne là où c'est approprié. Cela permet juste de
    s'assurer que le balisage produit soit plus facile à lire. Plutôt que de
    définir une quelconque valeur directement dans le Layout pour ces deux là,
    nous les configurerons depuis notre bootstrap en utilisant des options
    depuis application.ini.
Zend_View_Helper_HeadTitle
    est aussi appelé pour définir une partie de Titre intitulée "Pádraic
    Brady's Blog". Vous pouvez définir des portions supplémentaires, et elles
    seront rendues au sein d'un élément <title>, séparées
    par tout séparateur que vous pourriez choisir de configurer en utilisant
    Zend_View_Helper_HeadTitle::setSeparator(). Le
    second argument optionnel à la méthode primaire
    headTitle() peut être défini à
    "APPEND", la valeur par défaut, ou "PREPEND"
    pour qu'une partie du titre soit rendue au début de celui-ci.
Zend_View_Helper_HeadLink
    peut être utilisé pour ajouter des liens vers des feuilles de styles, ou
    en modifier. Il fonctionne aussi pour d'autres types de liens génériques
    en passant une liste d'attributs et de valeurs à
    Zend_View_Helper_HeadLink::headLink(). Des liens
    alternatifs peuvent être définis en utilisant
    appendAlternate() ou ses équivalents
    (prepend/set/offsetSetAlternate()). Lorsque nous
    ajoutons des feuilles de styles, les paramètres optionnels incluent les
    attributs media et type, ainsi que la définition
    d'une liste de conditions (Comme celle que nous avons utilisée plus bas
    pour inclure un fichier Javascript uniquement pour Internet Explorer). Si
    vous avez besoin d'ajouter un bloc de styles, ceci peut être effectué en
    utilisant, à la place, Zend_View_Helper_Style. Pour
    finir, Zend_View_Helper_Script permet d'inclure des
    scripts, tels des fichiers Javascript.
Vous pouvez noter
    quelques similarités entre des implémentations de Placeholders concrets.
    Ils ont des méthodes pour définir des éléments, et en ajouter en début ou
    fin de liste. Beaucoup fournissent aussi des méthodes
    offsetSet*() pour définir des éléments à des
    emplacements précis de la liste d'éléments à rendre.
Un élément de balisage
    que j'ai omis est la balise <meta> de charset. Elle ne
    peut pour l'instant pas être rendue par Zend Framework, mais nous
    résoudrons ce problème dans la section suivante. Pour l'instant, apportons
    quelques modifications à notre méthode
    ZFExt_Bootstrap::_initView() pour configurer les
    balises meta et le Doctype à rendre :
<?php class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap{     // ...     protected function _initView()    {        $options = $this->getOptions();        $config = $options['resources']['view'];        if (isset($config)) {            $view = new Zend_View($config);        } else {            $view = new Zend_View;        }        if (isset($config['doctype'])) {            $view->doctype($config['doctype']);        }        if (isset($config['language'])) {            $view->headMeta()->appendName('language', $config['language']);        }        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(            'ViewRenderer'        );        $viewRenderer->setView($view);        return $view;    }     // ... }Nous pouvons maintenant
    modifier application.ini pour ajouter au moins un
    élément d'information meta, la langue de la page. Il ne s'agit pas d'une
    option de configuration standard de Zend_View, mais
    uniquement d'une valeur par défaut que nous imposons depuis notre classe
    de bootstrap lorsque nous définissons les informations meta pour la
    page.
[production]; PHP INI SettingsphpSettings.display_startup_errors = 0phpSettings.display_errors = 0 ; Bootstrap Locationbootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"bootstrap.class = "ZFExt_Bootstrap" ; Standard Resource Optionsresources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"resources.view.encoding = "UTF-8"resources.modifiedFrontController.contentType = "text/html;charset=utf-8"resources.layout.layout = "default"resources.layout.layoutPath = APPLICATION_PATH "/views/layouts" ; Autoloader OptionsautoloaderNamespaces[] = "ZFExt_" ; HTML Markup Optionsresources.view.doctype = "HTML5"resources.view.language = "en" [staging : production] [testing : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1 [development : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1Pour le moment tout au moins, le support de HTML 5 par Zend Framework ne va pas plus loin que la fourniture d'un Doctype HTML. Le Doctype utilisé est réellement important : tout d'abord, parce qu'il est rendu en haut de votre Layout, mais surtout parce qu'il détermine comment les balises sont construites par un certain nombre de Views Helpers. Comme je l'ai dit précédemment, je préfére une approche de HTML 5 avec un style XHTML utilisant des balises fermées, des noms d'attributs en minuscules, et des valeurs d'attributs entourées de guillemets doubles, entre autres aspects. HTML 5 en lui-même est relativement peu au fait de ces détails ; en fait, il redéfinit une sérialisation XML comme une alternative à la variante HTML standard.
Avec notre Doctype
    défini à "HTML5", cela pose problème lorsque nous testons si nous sommes
    en mode XHTML : les aides de Vues vérifient si le nom du Doctype commence
    par "XHTML". HTML5 (notre valeur actuelle de Doctype) ne correspond de
    toute évidence pas. Pour résoudre ce problème, nous devrions ajouter une
    implémentation personnalisée du
    Zend_View_Helper_Doctype standard (une simple
    sous-classe est tout ce dont nous avons besoin) pour ajouter une option
    appelée "XHTML5" qui assurera que les règles XHTML seront appliquées à
    toute sortie d'aides de Vues où elles seront appliquées.
Nous enregistrerons
    cette aide de vue personnalisée dans
    /library/ZFExt/View/Helper/Doctype.php. Pour que
    notre application puisse indiquer à Zend_View où
    trouver ces aides personnalisées, nous pouvons définir quelques nouvelles
    options dans le fichier application.ini, dans notre
    section "Options de Ressources Standard". Celles-ci indiqueront un nouveau
    chemin où les Aides de Vues personnalisées peuvent être trouvées, et le
    préfixe de classes qu'elles utilisent. Aucune autre modification n'est
    ensuite nécessaire : les View Helpers dont le nom reflète celui de
    n'importe quelle aide de vue de Zend Framework (autrement dit, qui se
    terminent par le même terme en camel case) écrasent les aides fournies par
    le Framework. Donc, appeler Zend_View::doctype()
    devrait maintenant commencer par rechercher une aide de vue personnalisée
    répondant à ce nom.
[production]; PHP INI SettingsphpSettings.display_startup_errors = 0phpSettings.display_errors = 0 ; Bootstrap Locationbootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"bootstrap.class = "ZFExt_Bootstrap" ; Standard Resource Optionsresources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"resources.view.encoding = "UTF-8"resources.modifiedFrontController.contentType = "text/html;charset=utf-8"resources.layout.layout = "default"resources.layout.layoutPath = APPLICATION_PATH "/views/layouts"resources.view.helperPath = "ZFExt/View/Helper/"resources.view.helperPathPrefix = "ZFExt_View_Helper_" ; Autoloader OptionsautoloaderNamespaces[] = "ZFExt_" ; HTML Markup Optionsresources.view.doctype = "HTML5"resources.view.language = "en" [staging : production] [testing : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1 [development : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1Implémenter cette
    nouvelle Aide de Vue répond à un seul besoin : permettre l'utilisation
    d'un nouveau Doctype XHTML5 qui renvoit en sortie le doctype HTML5
    standard qui sera affiché dans la sortie. Toute récupération du Doctype
    devrait aussi retourner la valeur correcte, qui passe le test XHTML
    utilisé par les autres View Helpers. Si vous avez suivi la mise en place
    d'un nouveau fichier AllTests.php pour le Modèle un
    peu plus tôt, vous pouvez maintenant répéter ce processus pour toute aide
    de vue personnalisée que nous prévoierons d'ajouter. Voici nos tests
    unitaires pour une implémentation, stockés dans
    /tests/ZFExt/View/Helper/DoctypeTest.php :
<?php class ZFExt_View_Helper_DoctypeTest extends PHPUnit_Framework_TestCase{     protected $helper = null;     public function setup()    {        $this->helper = new ZFExt_View_Helper_Doctype;    }     public function testRendersHtml5DoctypeForXhtmlSerialisation()    {        $this->helper->doctype('XHTML5');        $this->assertEquals('<!DOCTYPE html>', (string) $this->helper);    }     public function testReturnsXhtmlDoctypeName()    {        $this->helper->doctype('XHTML5');        $this->assertEquals('XHTML5', $this->helper->getDoctype());    } }Implémenter cette aide
    de vue ne requiert qu'une simple sous-classe, définie dans
    /library/ZFExt/View/Helper/Doctype.php :
<?php class ZFExt_View_Helper_Doctype extends Zend_View_Helper_Doctype{    const XHTML5 = 'XHTML5';     public function __construct()    {        parent::__construct();        $this->_registry['doctypes'][self::XHTML5] = '<!DOCTYPE html>';    }     public function doctype($doctype = null)    {        if (null !== $doctype && $doctype == self::XHTML5) {            $this->setDoctype($doctype);        } else {            parent::doctype($doctype);        }        return $this;    } }Comme vous pouvez le voir à partir de la classe, en dehors de toute gestion spécifique du XHTML5, nous avons juste passé le contrôle à la classe mère pour tous les autres types possibles de Doctypes.
Un autre aspect du
    support HTML5 sur lequel nous pouvons maintenant nous pencher est le
    nouvel attribut charset que nous avons utilisé dans une
    balise <meta>. En pratique, l'objectif est de remplacer
    ceci :
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>Avec une forme plus
    courte, qui ne pré-suppose pas un Content-type. Bien entendu,
    vous pouvez toujours ajouter le Content-type si vous en avez
    besoin, mais l'application Web devrait de toute manière être servie sous
    le bon type.
<meta charset="utf-8"/>Malheureusement,
    Zend_View_Helper_HeadMeta, le View Helper qui
    permet aux templates et aux Layouts d'ajouter des informations meta ne
    reconnait pas l'attribut charset comme étant valide. Nous
    pouvons le persuader du contraire en ajoutant une autre aide de vue
    personnalisée, encore une fois en surchargeant la classe d'origine. Ici
    encore, nous sous-classerons la classe de départ pour minimiser la
    quantité de code que nous aurons besoin d'écrire.
Voici les tests
    unitaires correspondant, enregistrés dans
    /tests/ZFExt/View/Helper/HeadMetaTest.php :
<?php class ZFExt_View_Helper_HeadMetaTest extends PHPUnit_Framework_TestCase{     protected $helper = null;     protected $view = null;     public function setup()    {        foreach (array(Zend_View_Helper_Placeholder_Registry::REGISTRY_KEY, 'Zend_View_Helper_Doctype') as $key) {            if (Zend_Registry::isRegistered($key)) {                $registry = Zend_Registry::getInstance();                unset($registry[$key]);            }        }        /**         * This custom helper only concerns (X)HTML 5 support         * using the ZFExt doctype helper for the XHTML flavour         */        $this->view = new Zend_View();        $this->view->addHelperPath('ZFExt/View/Helper', 'ZFExt_View_Helper');        $this->helper = new ZFExt_View_Helper_HeadMeta();        $this->helper->setView($this->view);    }     public function testRendersHtml5CharsetMetaElement()    {        $this->view->doctype('HTML5');        $this->helper->setCharset('utf-8');        $this->assertEquals('<meta charset="utf-8">', (string) $this->helper);    }     public function testRendersXhtml5CharsetMetaElement()    {        $this->view->doctype('XHTML5');        $this->helper->setCharset('utf-8');        $this->assertEquals('<meta charset="utf-8"/>', (string) $this->helper);    } }L'implémentation est un
    peu plus longue que d'habitude, puisque nous ajoutons le support complet
    pour un autre type de balise <meta> initialement non
    acceptée. La nouvelle classe est écrite dans le fichier
    /library/ZFExt/View/Helper/HeadMeta.php :
<?php class ZFExt_View_Helper_HeadMeta extends Zend_View_Helper_HeadMeta{     protected $_typeKeys = array('name', 'http-equiv', 'charset');     public function setCharset($charset)    {        $item = new stdClass;        $item->type = 'charset';        $item->charset = $charset;        $item->content = null;        $item->modifiers = array();        $this->set($item);        return $this;    }     protected function _isValid($item)    {        if ((!$item instanceof stdClass)            || !isset($item->type)            || !isset($item->modifiers))        {            return false;        }         $doctype = $this->view->doctype()->getDoctype();         if (!isset($item->content)        && (($doctype !== 'HTML5' && $doctype !== 'XHTML5')        || (($doctype == 'HTML5' || $doctype == 'XHTML5') && $item->type !== 'charset'))        ) {            return false;        }         return true;    }     public function itemToString(stdClass $item)    {        if (!in_array($item->type, $this->_typeKeys)) {            require_once 'Zend/View/Exception.php';            throw new Zend_View_Exception(sprintf('Invalid type "%s" provided for meta', $item->type));        }        $type = $item->type;         $modifiersString = '';        foreach ($item->modifiers as $key => $value) {            if (!in_array($key, $this->_modifierKeys)) {                continue;            }            $modifiersString .= $key . '="' . $this->_escape($value) . '" ';        }         if ($this->view instanceof Zend_View_Abstract) {            if ($this->view->doctype()->getDoctype() == 'XHTML5'            && $type == 'charset') {                $tpl = '<meta %s="%s"/>';            } elseif ($this->view->doctype()->getDoctype() == 'HTML5'            && $type == 'charset') {                $tpl = '<meta %s="%s">';            } elseif ($this->view->doctype()->isXhtml()) {                $tpl = '<meta %s="%s" content="%s" %s/>';            } else {                $tpl = '<meta %s="%s" content="%s" %s>';            }        } else {            $tpl = '<meta %s="%s" content="%s" %s/>';        }         $meta = sprintf(            $tpl,            $type,            $this->_escape($item->$type),            $this->_escape($item->content),            $modifiersString        );        return $meta;    } }Ne vous inquiètez pas si tout cela a l'air incompréhensible ! Nous verrons quelques aides de vues spécifiques à notre application dans des chapîtres ultérieurs, mais, pour le moment, nous avons juste besoin de ces deux aides personnalisées pour avoir un support plus complet de HTML 5 à notre disposition.
Dans la classe
    ci-dessous, nous remplaçons deux méthodes d'origine :
    _isValid() et
    itemToString(). La première valide les détails
    des informations meta que nous voulons rendre dans une balise
    <meta>. Les données sont stockées sous forme d'une
    instance de stdClass, c'est-à-dire qu'il s'agit
    d'un simple conteneur de données qui aurait tout aussi bien pu être un
    tableau associatif. Le principal ajout que j'ai effectué au niveau de la
    validation est d'autoriser un nouveau type de meta "charset" dans le cas
    où le Doctype est un de ceux correspondant à HTML 5. Dans la méthode
    itemToString(), j'ai ajouté la possibilié de
    composer ce nouveau type meta en une balise <meta>, en
    fermant au passage la balise si le Doctype utilisé est XHTML5.
J'ai aussi ajouté une
    nouvelle méthode, setCharset(), pour que la
    création de cette nouvelle balise soit différenciée des deux autres types
    de meta initialement supportées, c'est-à-dire un simple type clef/valeur
    et http-equiv.
Maintenant que notre
    support HTML 5 est complet, nous pouvons retourner voir
    application.ini et ajouter une nouvelle option de
    balisage HTML nommée "charset", ainsi que notre nouveau Doctype. En fait,
    nous pourrions juste utiliser la configuration de
    Zend_View pour l'encodage de caractères, mais j'ai
    préféré garder ces deux options distinctes, pour que nous puissions
    ajouter la nouvelle balise meta conditionnellement uniquement dans le cas
    où cette option spécifique est définie.
[production]; PHP INI SettingsphpSettings.display_startup_errors = 0phpSettings.display_errors = 0 ; Bootstrap Locationbootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"bootstrap.class = "ZFExt_Bootstrap" ; Standard Resource Optionsresources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"resources.view.encoding = "UTF-8"resources.view.helperPath = "ZFExt/View/Helper/"resources.view.helperPathPrefix = "ZFExt_View_Helper_"resources.modifiedFrontController.contentType = "text/html;charset=utf-8"resources.layout.layout = "default"resources.layout.layoutPath = APPLICATION_PATH "/views/layouts" ; Autoloader OptionsautoloaderNamespaces[] = "ZFExt_" ; HTML Markup Optionsresources.view.charset = "utf-8"resources.view.doctype = "XHTML5"resources.view.language = "en" [staging : production] [testing : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1 [development : production]phpSettings.display_startup_errors = 1phpSettings.display_errors = 1resources.frontController.throwExceptions = 1Nous pouvons maintenant
    modifier la méthode _initView() de notre classe
    de bootstrap pour gérer et utiliser la nouvelle option charset :
<?php class ZFExt_Bootstrap extends Zend_Application_Bootstrap_Bootstrap{     // ...     protected function _initView()    {        $options = $this->getOptions();        $config = $options['resources']['view'];        if (isset($config)) {            $view = new Zend_View($config);        } else {            $view = new Zend_View;        }        if (isset($config['doctype'])) {            $view->doctype($config['doctype']);        }        if (isset($config['language'])) {            $view->headMeta()->appendName('language', $config['language']);        }        if (isset($config['charset'])) {            $view->headMeta()->setCharset($config['charset'], 'charset');        }        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(            'ViewRenderer'        );        $viewRenderer->setView($view);        return $view;    }     // ... }Allons-y, maintenant, rechargeons la page d'accueil de notre blog. Si vous regardez dans le code source, celui-ci devrait à présent réellement correspondre au balisage d'origine introduit au début de ce chapitre.
Utiliser le Framework CSS YUI nous a apporté un style de base pour le blog, qui est très basique, mais agréable sous tous les principaux navigateurs. Nous pouvons enrichir ceci en définissant nos propres styles personnalisés, pour donner à notre blog un aspect un peu plus fini.
Pour cela, nous avons
    besoin de modifier notre template de Layout pour aussi inclure un fichier
    nommé style.css stocké dans /public/css/style.css. Au
    sein de ce fichier, nous pourrons placer des règles CSS supplémentaires.
    Voici la version revue de la section <head> de notre
    Layout :
<?php echo $this->doctype() . "\n" ?><html><head><?phpecho $this->headMeta()->setIndent('    ') . "\n";echo $this->headTitle('Pádraic Brady\'s Blog')    ->setSeparator(' / ')->setIndent('    ') . "\n";echo $this->headLink()->setIndent('    ')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/base/base-min.css')    ->appendStylesheet('/css/style.css') . "\n    ";echo $this->headScript()->prependFile(    'http://html5shiv.googlecode.com/svn/trunk/html5.js',    'text/javascript',    array('conditional'=>'IE')) . "\n";?></head>En gardant à l'esprit le
    fait que nous pourrions vouloir prendre en compte les Yahoo
    Performance Best Practices, ajoutons notre exemple d'aide de vue
    précédent au mélange, avec quelques modifications. Voici les tests
    unitaires utilisés par l'aide personnalisée
    ZFExt_View_Helper_IncludeModifiedDate. Ils
    utilisent un fichier style.css vide que vous pouvez
    enregistrer dans
    /tests/ZFExt/View/Helper/_files/style.css pour
    obtenir une date de dernière modification. Les tests en eux-même sont
    enregistrés dans
    /tests/ZFExt/View/Helper/IncludeModifiedDateTest.php.
    Comme pour tous ces tests, ajoutons les au fichier
    AllTests.php le plus proche, pour qu'ils soient
    exécutés par PHPUnit. Le seul point un tant soit peu compliqué est de
    gérer le répertoire de travail actuellement utilisé par PHP pour que les
    URIs relatives aient un sens dans le test, et atteignent correctement
    notre fichier style.css.
<?php class ZFExt_View_Helper_IncludeModifiedDateTest extends PHPUnit_Framework_TestCase{     protected $helper = null;     private $cwd = null;     public function setup()    {        $this->helper = new ZFExt_View_Helper_IncludeModifiedDate;        $this->cwd = getcwd();        chdir(dirname(__FILE__));    }     public function teardown()    {        chdir($this->cwd);    }     public function testAddsTimestampToFilenameBeforeFileExtension()    {        $file = '/_files/style.css';        $timestamped = $this->helper->includeModifiedDate($file);        $this->assertTrue((bool) preg_match("/^\/_files\/style\.\d{10,}\.css\$/", $timestamped));    }     public function testAddsTimestampToFilenameBeforeFileExtensionWithUriQueryString()    {        $file = '/_files/style.css?version=2.0';        $timestamped = $this->helper->includeModifiedDate($file);        $this->assertTrue((bool) preg_match("/^\/_files\/style\.\d{10,}\.css\?version=2\.0$/", $timestamped));    } }Comme vous pouvez
    probablement le remarquer, nous avons boosté un peu l'aide pour gérer un
    cas supplémentaire où l'URI d'un fichier incluerait déjà une query string
    quelconque. Dans ce cas, nous ajouterons le timestamp de dernière
    modification au sein du nom du fichier, et non dans la query string. Cela
    permet de gérer un scénario où de nombreux proxies ignorent la query
    string lorsqu'ils cachent les fichiers, ce qui signifie que modifier le
    nom du fichier ou son chemin est plus efficace. Bien entendu, nous ne
    modifierons pas physiquement le nom du fichier ; à la place, nous
    ajouterons une nouvelle règle de ré-écriture au fichier
    /public/.htaccess, afin que toute URI vers un fichier
    physique de la forme /name.timestamp.extension (par
    exemple, /style.1252108229.css) soit redirigée vers
    le bon fichier correctement. Voici l'implémentation, enregistrée dans
    /library/ZFExt/View/Helper/IncludeModifiedDate.php
    :
<?php class ZFExt_View_Helper_IncludeModifiedDate extends Zend_View_Helper_Abstract{     public function includeModifiedDate($uri) {        $parts = parse_url($uri);        $root = getcwd();        $mtime = filemtime($root . $parts['path']);        return preg_replace(            "/(\.[a-z0-9]+)(\?*.*)$/",            '.'.$mtime.'$1$2',            $uri        );    } }La modification suivante ajoute le support de ce type de marquage de
    fichiers à /public/.htaccess, afin que nous chargions
    le bon fichier, qui n'a pas de timestamp inclu dans son nom. La règle de
    ré-écriture ne faire guère plus que supprimer la partie timestamp du nom
    de fichier arrivant. Vous pouvez ajouter des exensions de fichiers ou des
    répertoires supplémentaires sur lesquels ceci a besoin d'être exécuté.
    J'ai utilisé par défaut quelques valeurs communes et raisonnables :
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]
    Notre template de layout peut maintenant utiliser le nouvel aide de vue personnalisé de la manière qui suit :
<?php echo $this->doctype() . "\n" ?><html><head><?phpecho $this->headMeta()->setIndent('    ') . "\n";echo $this->headTitle('Pádraic Brady\'s Blog')    ->setSeparator(' / ')->setIndent('    ') . "\n";echo $this->headLink()->setIndent('    ')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/base/base-min.css')    ->appendStylesheet($this->includeModifiedDate('/css/style.css')) . "\n    ";echo $this->headScript()->prependFile(    'http://html5shiv.googlecode.com/svn/trunk/html5.js',    'text/javascript',    array('conditional'=>'IE')) . "\n";?></head>Nous n'utiliserons pas
    notre Aide de Vue avec les URIs des CSS YUI, puisqu'elles ont déjà une
    référence à un numéro de version inclue. Nous pouvons modifier les URIs
    manuellement lorsque de nouvelles versions du Framework CSS seront
    publiées, toujours à partir du template de layout, sans avoir à effectuer
    quelque autre modification que ce soit. Pour garder trace de notre code de
    balisage, voici ce à quoi la sortie du navigateur ressemble lorsque l'on
    regarde le code source de la page affichée, à notre niveau, pour notre
    section <head> :
<!DOCTYPE html><html><head>    <meta name="language" content="en" />    <meta charset="utf-8"/>    <title>Pádraic Brady's Blog</title>    <link href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="screen" rel="stylesheet" type="text/css" />    <link href="http://yui.yahooapis.com/2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />    <link href="/css/style.1252108229.css" media="screen" rel="stylesheet" type="text/css" />    <!--[if IE]> <script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]--></head>Comme vous pouvez le constater, l'URI vers style.css inclut maintenant une date de dernière modification attachée à la query string, comme nous le souhaitions.
Si vous vous demandez
    comment mettre en place l'utilisation des en-têtes Expires et
    Cache-Control dans la pratique, c'est généralement fait à
    partir de la configuration de votre Virtual Host. Il vous faudra
    auparavant activer le module Apache mod_expires dans la
    configuration de votre serveur web.
Voici un exemple de
    configuration pour le Virtual Host de notre blog, implémentant une en-tête
    Expires de six mois à partir du moment où le client accéde à
    un fichier statique. J'ai ajouté deux règles pour illustrer les options :
    définir les fichiers auxquels elles doivent être appliquées via une
    expression rationnelle, ou en faisant référence aux types auxquels
    l'en-tête devrait être appliquée. Les règles sont intégrées à un bloc
    conditionnel, afin qu'elles soient ignorées si le module requis n'est pas
    chargé dans Apache. Il est aussi essentiel de se souvenir que, à moins que
    vous n'utilisiez une date de modification ou un hash dans la query string
    des URIs de vos ressources, le client les cachera pour six mois et
    n'essayera jamais de récupérer une version mise à jour, à moins qu'il ne
    perdre son cache ou qu'un rechargement complet ne soit forcé (par exemple,
    en utilisant CTRL+SHIFT+R sous 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>
    En utilisant cette configuration, les types de fichiers correspondant aux règles seront envoyés au client avec deux nouveau en-têtes. Par exemple :
Cache-Control: max-age=31536000 Expires: Sun, 05 Sep 2010 00:39:27 GMT
Expires
    indique une date six mois dans le futur, à partir de laquelle le fichier
    devrait être considéré comme expiré, et re-téléchargé depuis le serveur,
    et la seconde, Cache-Control, indique la durée de vie
    maximale de la ressource en secondes. A un niveau plus technique,
    l'en-tête Cache-Control est la plus importante, puisque, dans
    la plupart des cas, elle écrase l'en-tête Expires.
Avant que nous
    n'attaquions une feuille de style personnalisée, commençons par utiliser
    ce que nous avons déjà inclu avec Yahoo User Interface CSS. Les
    modifications qui suivent, apportées à notre template de layout par défaut
    /application/views/layouts/default.phtml, vont
    correspondre à une présentation typique de site web, avec une en-tête, un
    pieds de page, un corps de page, et une colonne à gauche pour les liens de
    navigation ou contenus supplémentaires :
<?php echo $this->doctype() . "\n" ?><html><head><?phpecho $this->headMeta()->setIndent('    ') . "\n";echo $this->headTitle('Pádraic Brady\'s Blog')    ->setSeparator(' / ')->setIndent('    ') . "\n";echo $this->headLink()->setIndent('    ')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css')    ->appendStylesheet('http://yui.yahooapis.com/2.7.0/build/base/base-min.css')    ->appendStylesheet($this->includeModifiedDate('/css/style.css')) . "\n    ";echo $this->headScript()->prependFile(    'http://html5shiv.googlecode.com/svn/trunk/html5.js',    'text/javascript',    array('conditional'=>'IE')) . "\n";?></head><body>    <div id="doc" class="yui-t1">         <header id="hd" role="banner">            <hgroup>                <h1><a href="/">Pádraic Brady's Blog</a></h1>                <h2>Musings On PHP And Zend Framework</h2>            </hgroup>            <nav role="navigation">                <ul>                    <li><a href="/">Home</a></li>                    <li><a href="#">About</a></li>                    <li><a href="#">Projects</a></li>                    <li><a href="#">Contact</a></li>                </ul>            </nav>        </header>         <div id="bd">            <div id="yui-main">                <div class="yui-b" role="main">                    <section id ="content" class="yui-g">                        <?php echo $this->layout()->content ?>                    </section>                </div>            </div>            <section class="yui-b">                <nav role="navigation" aria-labelledby="leftnav-label">                    <h2 id="leftnav-label">External Links</h2>                    <ul>                        <li><a href="#">Survive The Deep End</a></li>                        <li><a href="#">Zend Framework</a></li>                        <li><a href="#">Planet-PHP</a></li>                        <li><a href="#">I'm on Twitter!</a></li>                    </ul>                </nav>            </section>        </div>         <footer id="ft" role="contentinfo">            <p>Copyright © 2009 Pádraic Brady</p>            <p>Powered by <a href="http://www.survivethedeepend.com">ZFBlog            </a></p>        </footer>     </div></body></html>Puisque nous utilisons
    HTML 5, une bonne partie du balisage utilise les nouveaux éléments, sauf
    dans les cas où ils ne sont réellement nécessaires que dans le seul but
    d'appliquer la mise en forme de grille. Dans ces cas, j'ai utilisé des
    éléments <div> habituels. Cela permet de conserver un
    ensemble logique d'éléments, où nous n'avons que deux éléments
    <section> distincts. Un pour le contenu principale, et
    l'autre pour la barre à gauche que je viens d'ajouter. Les
    <div> sont alors au relégués au rôle peu intéressant
    qui permet d'ajouter des "hooks" aux balises pour le styling ; ce n'est
    pas vraiment l'approche la plus proche, mais puisque j'utilise un
    Framework CSS, je me ferai à cette solution.
J'ai aussi ajouté des
    rôles à un certain nombre d'éléments, en accord avec un autre nouveau
    standard, Accessible
    Rich Internet Applications Suite (WAI–ARIA 1.0), qui définit une
    manière d'essayer de rendre les contenus et applications web plus
    accessible aux personnes souffrant de handicaps. Ce n'est qu'un geste
    minime à faire, alors que nous adoptons HTML 5 et créons un layout à
    partir de rien. Le standard définit un ensemble de "document landmark
    roles" qui, en théorie, devraient permettre à quelqu'un utilisant les bons
    logiciels de naviguer à travers le réseau de balises vers les parties du
    document HTML qui les intéressent. Les rôles sont donc des plus intuitifs
    : banner, main, navigation, contentinfo, complementary, search, etc. Il
    existe un grand nombre de ceux-ci, mais je n'utilise que le strict
    nécessaire. Si cela peut ajouter un certain poids, ces rôles sont
    actuellement supportés par le lecteur
    d'écran JAWS 10.
Pour ajouter un
    sous-titre au blog, j'ai ajouté un nouvel élément
    <hgroup>. Celui-ci regroupe les en-têtes liées, et est
    défini de la manière suivante par les spécifications HTML 5 :
Rechargez l'URI http://zfblog.tld dans votre
    navigateur pour voir les impacts que tout ceci a eu :
Le design mis à jour est
    en train, doucement, de commencer à ressemble à quelque chose qui
    deviendrait acceptable, si nous étions doté d'une patience infinie.
    Améliorons ceci encore un peu, cette fois en éditant le fichier
    /public/css/style.css :
/**
 * Basic Elements
 */
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 Display
 *
 * Required by most, if not all, browsers until support is added.
 */
article, aside, dialog, figure, footer, header, hgroup, nav, section {
    display:block;
}
/**
 * Page Elements
 */
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;
}
/**
 * Headers
 */
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%;
}
/**
 * Horizontal Header Navigation Menu
 */
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;
}
/**
 * Vertical Sidebar Menu/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;
}
/**
 * Article related styling
 */
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;
}
    Nous avons maintenant un style très basique, mais acceptable, pour travailler pour la suite :
Au cours de ce chapitre,
    nous avons vu les bases qui permettent la création d'un design web simple,
    et l'avons migré vers le système de templating de Zend Framework en
    utilisant Zend_View,
    Zend_Layout, et un certain nombre d'aides de vues.
    Nous verrons beaucoup plus en détails la création de View Helpers
    personnalisés au cours des prochains chapitres. Nous avons aussi vu
    quelques bases de HTML 5, la prochaine mise à jour du standard pour HTML,
    et l'impact qu'il a sur notre implémentation, puisqu'il requiert
    l'utilisation de quelques aides de vues personnalisées. Je n'ai aucun
    doute que le support de HTML 5 au sein de Zend Framework se matérialisera
    dans un futur très proche, cela dit, donc stay tuned
    !
Au chapitre 11, nous
    commencerons à travailler sur l'ensemble de fonctionnalités de base. Nous
    apprendrons à afficher des entrées de blog à partir de notre Base de
    Données, et, bien entendu, nous verrons comment permettre leur saisie !
    Nous regarderons donc comment utiliser notre Modèle, et comment gérer
    l'écriture de nouvelles entrées de blog en utilisant les formulaires
    générés avec Zend_Form.