Table des matières
Proposer un exemple le plus simple qui soit est devenu une tradition pour les livres de programmation, quelque soit le langage, le framework, ou la bibliothèque concerné. En général, cela signifie en faire juste assez pour afficher "Hello World" à l'écran, afin que vous puissiez voir comment les fonctionnalités de base doivent être utilisées. Ce n'est pas vraiment l'exemple le plus réaliste qui soit lorsqu'on l'applique à des Frameworks, mais c'est un point de départ qui en vaut un autre !
Avant que nous ne nous lancions dans le code source, il est généralement recommandé de développer en utilisant un environnement un brin plus proche de ce qui sera, typiquement, notre environnement de production. Tout au long de ce livre, je développerai des applications au sein du document root d'un serveur web, plutôt que dans un sous-répertoire. Bien que vous puissiez utiliser un sous-répertoire, cela peut vous demander un peu plus de travail lorsqu'il s'agira de configurer des références d'URI à travers l'application - quelque chose que je couvrirai un peu plus en détails plus loin.
Pour l'instant, jetons
un oeil à l'Annexe A :
Créer un domaine local, en utilisant des Virtual Hosts Apache
, qui
détaille un processus simple pour la mise en place d'un domaine local,
avec son propre document root distinct pour cet exemple. En utilisant ce
domaine et un Virtual Host Apache, nous pouvons servir cet exemple depuis
le domaine http://helloworld.tld
sur notre
machine de développement.
L'arborescence des
répertoires de notre projet d'exemple est le point à traiter ensuite. Pour
commencer, créons un nouveau répertoire correspondant au chemin (en
excluant le sous-répertoire /public
final) que vous
avez utilisé plus haut comme Document Root pour notre Virtual Host
helloworld. C'est là que les fichiers de notre projet seront enregistrés.
Sous Ubuntu, je pourrais créer ce répertoire dans
/home/padraic/www/helloworld
, et, sous Windows, je
pourrais utiliser C:\projects\helloworld
.
Il y a toujours pas mal
de discussions sur la structure à adopter pour l'arborescence des
répertoires, mais le plus gros à ce niveau est en train d'être formalisé
par l'effort entrepris par Ralph Schindler autour de
Zend_Tool
, qui permettra d'obtenir un outil en
ligne de commande complet pour la génération et la manipulation de
projets. Jusqu'à ce qu'il soit réellement stable et documenté, nous ne
l'utiliserons pas ici, ce qui signifie que nous allons devoir effectuer un
peu de travail manuel, pour ce qui est de la création de l'arborescence de
répertoires.
Voici l'arborescence de répertoires que je suggère. Suivre celle-ci n'est aucunement obligatoire, et vous pouvez tout à fait utiliser quelque chose de différent, en fonction de vos préférences et d'autres applications. Pour les besoins de notre exemple, nous n'avons besoin que d'un sous-ensemble de ces répertoires.
Comme vous pouvez le
voir, l'ensemble du code source des Contrôleurs et des Vues sera stocké
dans le répertoire /application
. Les Contrôleurs et
les Vues ont des conventions de chargement spécifiques au niveau du
Framework ; suivre cette convention est donc logique. Nous plaçons aussi
là les Modules, qui sont, pour faire simple, des regroupements distincts
de Contrôleurs et de Vues (et, éventuellement, de Modèles et d'aides
spécifiques à notre application et non ré-utilisables). Vous pourriez vous
attendre à trouver un répertoire /application/models
,
à partir de ce que vous auriez pu voir dans la documentation. Je n'utilise
pas ce répertoire pour l'instant, puisque les implémentations de Modèles
sont incroyablement diverses : il n'existe pas une manière unique de les
charger. De plus, je maintiens les Modèles comme composants de ma
bibliothèque générique d'application, /library
, avec
les autres classes spécifiques à cette application. Cela dit, il est
toujours possible de placer les Modèles dans
/application/models
. Vous pourriez préférer placer
les Modèles dans un répertoire nommé de manière un peu plus spécifique,
mais souvenez-vous que chaque répertoire contenant du code source devra
éventuellement devenir une nouvelle entrée dans
l'include_path
de PHP. Plus loin au cours de ce livre, nous
travaillerons avec des Modèles qui pourront être stockés comme
traditionnellement décrit dans le Guide de Référence, puisque
l'introduction de Zend_Loader_Autoload
nous
permettra d'être plus flexibles à ce niveau.
Tout le reste existe au
niveau du répertoire parent, et tout le futur code source de
l'application, en dehors des Contrôleurs, des Vues, et des classes non
ré-utilisables, va être placé soit dans le répertoire
/library
, soit dans le répertoire
/vendor
. Le répertoire library
est celui où je stockerai les classes généralistes utilisées au sein de
cette application. Le répertoire vendor
sera
généralement utilisé pour des classes non-spécifiques, ou des
bibliothèques tierces que je souhaiterai utiliser. La différenciation
entre les deux est totalement arbitraire : j'aime juste garder mes classes
et les bibliothèques tierces distinctes. Je conserve aussi le Zend
Framework lui-même là, sauf s'il est déjà installé sur le serveur à un
emplacement plus central et accessible depuis
l'include_path
.
Les répertoires restant
ne constituent pas un mystère. Le répertoire config
contient les fichiers de configuration. En suivant les standards qui sont
en train d'émerger, ceci pourrait aussi être placé dans
/application/configs
. Le répertoire
data
permet de stocker des données sous forme de
fichiers. Il pourrait s'agir de fichiers de cache, ou d'autres
informations. Le répertoire public
est celui où tous
les fichiers accessibles par le visiteur de notre site web résideront. Le
répertoire scripts
, quant à lui, contient les scripts
d'utilité générale, comme les tâches à lancer en utilisant cron.
Le Bootstrapping est
l'étape à laquelle nous mettons en place l'environnement initial, et
configurons et initialisons le Zend Framework de notre application. Ce
n'est pas un fichier compliqué à écrire, mais il peut devenir de plus en
plus gros au fur et à mesure que votre application devient plus complète.
Pour que cela reste gérable, je vous suggère fortement de l'implémenter
sous forme d'une classe contenant de petites méthodes unitaires. Découper
le Bootstrap fera des merveilles pour votre santé mentale. Plus loin dans
ce livre, nous étudierons la solution proposée par Zend Framework pour
répondre à ce problème, Zend_Application
, qui a été
introduite par Zend Framework 1.8.
Puisque le bootsrap est
une classe, l'emplacement logique où le stocker est dans
/library
. Bien entendu, pour pourriez le placer dans
/application
, mais c'est encore un autre chemin
d'inclusion (à moins que vous n'utilisiez
Zend_Application
qui travaille avec des chemins
absolus vers les classes) et si nous continuons à enregistrer des classes
un peu partout, nous allons finir avec l'include_path de l'enfer ! Pour
l'instant, en considérant que vous n'utilisez pas
Zend_Loader_Autoloader
, gérer les Modèles dans
/library
avec notre classe de bootstrap enlève deux
entrées à l'include_path. Nous allons enregistrer le nouveau bootstrap
comme fichier spécifique à notre application, sous l'espace de noms ZFExt,
ce qui signifiera qu'il sera enregistré dans
/library/ZFExt/Bootstrap.php
.
<?php
/**
* When storing the ZF within /vendor, use an absolute path.
*/
require_once 'Zend/Loader.php';
class ZFExt_Bootstrap
{
public static $root = '';
public static $frontController = null;
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_startup_errors', true);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
self::$root = realpath('..');
define('APP_ROOT', self::$root);
spl_autoload_register(array(__CLASS__, 'autoload'));
}
public static function autoload($class) {
include str_replace('_', '/', $class) . '.php';
return $class;
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
realpath(self::$root . '/application/controllers')
);
$response = new Zend_Controller_Response_Http;
$response->setHeader('Content-Type',
'text/html; charset=UTF-8', true);
self::$frontController->setResponse($response);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$view->doctype('XHTML1_STRICT');
$view->headMeta()->appendHttpEquiv('Content-Type',
'text/html;charset=utf-8');
$viewRenderer =
new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->sendResponse();
}
}
Bien... Vous devez maintenant comprendre pourquoi ce chapitre est appelé tutorial pas si simple !
A priori, vous êtes ici
pour apprendre, cela dit, donc le bootstrap présenté au-dessus est bien
plus complexe que l'exemple minimaliste que vous trouverez dans le Guide
de Référence. Ce que j'ai fait ici passe par deux étapes. Premièrement,
j'ai structuré le bootstrap comme une classe avec des méthodes distinctes
pour les différentes étapes et tâches du bootstrapping. Et, secondement,
j'ai mis en place des valeurs par défaut pour toutes les sorties
efffectuées par l'application, en modification les configurations de
Zend_View
(qui génére la sortie de l'application en
utilisant des templates) et de
Zend_Controller_Response_Http
(qui gère les
en-têtes et les mécanismes de l'envoi effectif des réponses au
client).
Les premières étapes
sont plutôt simples. Notre gestion d'environnement, qui, dans le futur,
devrait être rendue configurable, prépare PHP pour notre application, avec
quelques valeurs de configuration, niveau de rapport d'erreurs,
information de timezone (non nécessaire si définie dans php.ini), et une
fonction d'autoload, pour que nous n'ayons pas besoin d'utiliser
require_once
dans notre code source. Notre autoloader
personnalisé contraste avec le plus utilisé
Zend_Loader
, mais il est bien plus léger que
l'implémentation de Zend_Loader
, qui effectue toute
une série de tâches généralement inutiles (à ce sujet, vous pouvez
consulter l'Annexe
B
). La configuration actuelle d'environnement est pensée pour le
développement, ce qui explique que les erreurs seront affichées.
L'étape suivante, au
sein de la méthode prepare()
, est de configurer
le Front Controller. Comme discuté précédemment, avec une architecture
Modèle-Vue-Contrôleur, l'application n'a généralement qu'un seul point
d'entrée, par lequel toutes les requêtes doivent passer. Pour Zend
Framework, c'est le Front Controller défini par la classe
Zend_Controller_Front
. La classe Front Controller
utilisée par Zend Framework est un singleton (c'est d'ailleurs sa
"fonctionnalité" la plus ennuyeuse - les singletons devraient être évités
lorsqu'ils ne sont pas nécessaires). Dans la méthode
setupFrontController()
, nous paramétrons quelques
flags pour être sûr qu'un objet Response soit retourné, plutôt que de
simplement être affiché, hors de notre contrôle. Nous faisons aussi en
sorte que les Exceptions soient levées de manière visible, plutôt que de
rechercher un ErrorController
qui n'existe pas
encore au sein de notre application. Finalement, nous indiquons au Front
Controller où il peut trouver les Contrôleur et Vues de notre
application.
L'étape finale, où nous
créons une instance distincte de l'objet de Réponse,
Zend_Controller_Http_Response
, existe pour que nous
puissions spécifier une en-tête Content-Type par défaut pour les réponses
de l'application. Cela permet de s'assurer que, sauf si spécifié
autrement, toutes nos sorties seront automatiquement envoyées aux clients
accompagnées d'une en-tête Content-Type valant text/html, avec un encodage
en UTF-8. C'est une bonne pratique, pour éviter toute confusion dans le
cas où nous ne définirions pas ceci explicitement dans l'application. Si
cette valeur n'est pas initialisée, la valeur par défaut de Content-Type
définie dans php.ini peut être utilisée à la place.
Une fois que
l'environnement et le Front Controller sont configurés, l'étape finale est
de mettre en place des valeurs par défaut du même type pour nos Vues.
Actuellement, Zend_View
, contrairement aux autres
composants, utilise par défaut l'encodage ISO-8859-1 (il en va de même
pour les aides de Vues), qui ne fera tout simplement pas l'affaire si vous
souhaitez utiliser en sortie des caractères tenant sur plusieurs octets
(comme celui dans mon nom !). C'est un point quelque peu génant, et un bug
reporté il y a bien longtemps, mais conservé pour éviter tout problème de
rétro-compatibilité avec des versions plus anciennes de Zend Framework.
Pour changer l'encodage par défaut, nous allons devoir obtenir une
nouvelle instance de la classe Zend_View
, déclarer
que nous voulons utiliser un encodage plus approprié, comme l'UTF-8, et
implanter cet objet Vue altéré au sein de l'aide d'Action ViewRenderer.
Cet aide est utilisé automatiquement par tous les Contrôleurs pour gérer
les Vues, ce qui signifie qu'en définissant explicitement un objet Vue à
utiliser, nous éviterons qu'un objet Vue non modifié soit automatiquement
instancié à la place. En supposant que nous générerons du HTML par défaut,
il est aussi fortement recommandé de définir un DOCTYPE HTML par défaut,
puisque cette option de configuration a un impact sur d'autres composants
(Zend_Form
, par exemple) au moment où ils sont
rendus. Encore une fois, c'est génant si vous oubliez ceci, puisque vous
pouvez vous retrouver avec des formulaires générés en utilisant un doctype
différent du reste de la page, par exemple. Bien entendu, nous
préférerions que tout ce qui est rendu en HTML le soit en suivant le
DOCTYPE préféré par les Vues de l'application.
Comme vous pouvez le
voir, écrire un Bootstrap est un peu comme partir en guerre. Il ne survit
jamais au premier contact, et vous serez en permanence en train d'ajouter
des chemins, des options de configuration, et en train d'apporter d'autres
modifications au fur et à mesure que le temps passera. Le code reproduit
plus haut présente une base de départ possible, mais, au prochain
chapître, nous ferons connaissance de
Zend_Application
, qui permet une approche plus
standard.
Nous avons un Bootstrap
en place, mais il n'est utile que si nous l'exécutons quelque part. Comme
nous l'avons vu précédemment, toutes les applications basées sur une
architecture Modèle-Vue-Contrôleur (MVC) ont un point d'entrée unique.
Dans le cas de PHP, c'est quasiment toujours un fichier nommé
index.php
, le fichier d'Index.
Puisque l'index est
chargé automatiquement par quasiment n'importe quel serveur web supportant
PHP, si aucun autre chemin de fichier n'est donné, c'est l'emplacement où
nous application sera chargée en premier lieu. C'est aussi l'endroit où
nous pouvons effectuer toute modification manuelle de
l'include_path
de PHP qui serait nécessaire par rapport à
l'environnement de notre serveur local (autres que les chemins que notre
boostrap peut automatiquement charger, de par sa conception). Cela dit, je
préfére ne pas casser la convention PEAR sans bonne raison, donc, la seule
chose que nous devrions effectuer dans index.php
est
de lancer le Bootstrap !
Créons le fichier
index.php
dans le répertoire
/public
de notre projet, contenant :
<?php
require realpath('..') . '/library/ZFExt/Bootstrap.php';
ZFExt_Bootstrap::run();
Si vous utilisiez des bibliothèques avec des chemins d'inclusion non conventionnels, il vous faudrait ajouter ces répertoires d'inclusion ici, ou les configurer au sein de la classe de bootstrap. Pour le moment, cela dit, tout ce que nous utilisons peut être chargé automatiquement par le bootstrap.
Nous avons un petit
problème avec notre théoriquement unique point d'entrée : il va à
l'encontre de la manière dont HTTP fonctionne en pratique. Comment
pouvons-nous communiquer les informations indiquant quelle partie de
l'application nous essayons d'atteindre, si le seul fichier disponible est
index.php
? Nous avons besoin de pouvoir utiliser une
URL qui, par défaut, suit la forme
http://domain.tld/controllername/actionname
pour que le
Routeur de l'application puisse appeler la bonne méthode d'action de la
bonne classe Contrôleur. Quelque soit le serveur web, c'est voué à
l'échec, puisqu'il croira que nous essayons de joindre un chemin de
fichier correspondant au chemin de l'URL - c'est-à-dire, le répertoire
controllername/actionname/
. Pour contourner ce
problème, il faut que nous puissions passer le chemin depuis l'URL à
index.php
, tout en faisant en sorte que le serveur
web comprenne que nous effectuons effectivement toutes nos requêtes via
index.php
.
Heureusement, les serveurs web fournissent une fonctionnalité nommée
ré-écriture d'URL, qui nous permet de transformer toutes les URLs
(correspondant à certains critères) en une nouvelle URL qui pointe sur
index.php
, pour créer une URL finale qui ressemble
plus à quelque chose du genre de
http://domain.tld/index.php/controllername/actionname
. Bien
entendu, ce critère doit exclure toutes les URLs de ressources que nous ne
souhaitons pas voir passées à l'application comme un simple paramètre,
comme nos feuilles de styles, nos javascripts et nos images. Celles-là
doivent toujours être servies directement.
En supposant que vous utilisiez Apache comme server web, il se peut que vous ayez à activer la ré-écriture d'URL en activant le module rewrite. Ce module est désactivé par défaut sur un grand nombre d'instances d'Apache fraichement installées. Cela passe par la modification du fichier de configuration d'Apache, ou, sous Ubuntu, par l'utilisation de la commande a2enmod (qui est plutôt pratique).
Si vous utilisez Ubuntu, essayez ceci :
sudo a2enmod rewrite
Cette commande joue avec un lien symbolique et ajoute le fichier de configuration de rewrite de manière à ce qu'il soit automatiquement chargé comme faisant parti de la configuration globale d'Apache. Vous devrez recharger Apache pour que toute modification au niveau des modules prenne effet, ce qui se fait, sous Ubuntu, en utilisant :
sudo /etc/init.d/apache2 reload
S'il vous faut modifier manuellement le fichier de configuration, vous ne devriez normalement avoir qu'à ajouter quelque chose du style de la ligne suivante (ici encore, rechargez Apache ensuite) :
LoadModule rewrite_module modules/mod_rewrite.so
Pour ceux d'entre vous
qui n'utilisent pas Apache, veuillez consulter la section sur
Zend_Controller
du Guide de Référence pour obtenir
des informations spécifiques à votre environnement.
Pour pouvoir utiliser
les ré-écritures d'URL, ajoutez le fichier suivant, nommé
.htaccess
, au répertoire /public
:
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ /index.php [NC,L]
Si vous comptez héberger votre application dans un sous-répertoire,
il vous faudra aussi ajouter une directive RewriteBase
, pour
être sûr que les règles excluent cette portion des URLs, et ne prennent en
compte que les éléments de chemins après ce point au moment où les
ré-écritures vers index.php
se feront.
RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteBase /subdirectory/ RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ /index.php [NC,L]
Le fichier
.htaccess
défini plusieurs règles de ré-écriture, qui
assurent que toutes les requêtes soient renvoyées vers le fichier index de
notre application lorsque c'est approprié. Il est conçu de manière à faire
correspondre toutes les URLs, sauf celles spécifiées par les conditions de
ré-écriture qui précédent - ici, si l'URL fait référence à un fichier qui
existe (avec une taille supérieure à zéro), à un répertoire, ou à un lien
symbolique. Cela permet de s'assurer que vos javascript, css, et tout
autre fichier non-PHP, pourront être servis directement, même s'ils sont
centralisés ailleurs, et référencés seulement via des liens
symboliques.
Lorsque nous discutions du Modèle-Vue-Contrôleur (MVC), nous avons noté que le Contrôleur était la partie responsable du câblage de l'application, permettant aux saisies utilisateur d'être mises en correspondance avec le Modèle, et les Modèles liés aux Vues lorsque c'est nécessaire. Le Contrôleur est donc l'un des premiers composants avec lesquels vous allez travailler, pour toutes les fonctionnalités que vous allez ajouter à une application.
Avec Zend Framework,
tous les Contrôleurs sont nécessairement des sous-classes de
Zend_Controller_Action
. Cette classe de base force
quelques conventions par défaut, que vous devriez garder à l'esprit. Par
exemple, il a été décidé que la comportement par défaut de tous les
Contrôleurs serait de générer automatiquement un rendu via des Vues. Cela
signifie que vous n'avez pas besoin de vous soucier de spécifier
manuellement quels templates de Vues doivent être rendus. A la place, une
aide d'action (Action Helper) nommée View Renderer
(Zend_Controller_Action_Helper_ViewRenderer
) est
automatiquement appelée. Dans notre classe de bootstrap définie
précédemment, nous avons ajusté le comportement par défaut, en s'assurant
que le View Renderer utilise une instance de
Zend_View
ayant un encodage par défaut en UTF-8
(plutôt que ISO-8859-1). Il existe des solutions pour désactiver le
rendering automatique des Vues, comment documenté dans le Guide de
Référence, ce qui est souvent utile lorsque vous avez besoin de contourner
Zend_View
(par exemple, pour envoyer en sortie des
documents JSON ou du XML, qui ne sont pas basés sur un template, et n'ont
pas besoin de traitement supplémentaire).
Déterminer quel
Contrôleur doit être utilisé est le travail du Routeur
(Zend_Controller_Router_Route
et les classes qui
lui sont proches), qui utilise la valeur de l'URL qui contient le nom de
la classe du Contrôleur et de la méthode à utiliser, ou qui fait la
correspondance avec une Route configurée de manière à fournir les noms des
Contrôleur et Action à utiliser par défaut. En l'absence de telles
informations, le Routeur utilisera par défaut la valeur "index" pour
chacun des deux. Puisque nous n'avons l'intention d'effectuer des requêtes
que vers la racine de http://helloworld.tld
, nous
aurons besoin d'un Contrôleur Index contenant une Action Index ;
c'est-à-dire un Contrôleur qui puisse répondre aux valeurs par défaut du
Routeur, qui considère en fait que cette URL est identique à http://helloworld.tld/index/index
.
Voici le code source pour le fichier
/application/controllers/IndexController.php
:
<?php
class IndexController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
}
}
La méthode
init()
peut être ignorée, puisqu'elle ne sert
qu'à initialiser des données spécifiques à ce Contrôleur, et que nous n'en
n'avons, ici, aucune. La méthode indexAction()
,
elle, est plus intéressante, même si elle n'a aucun contenu, ce qui est
tout à fait acceptable, puisqu'elle n'a besoin de contenir du code que si
nous souhaitons interagir avec un Modèle ou d'autres classes. Ici, nous
n'avons rien de tout cela. En fait, ici, cette méthode agit uniquement
comme une étiquette indiquant à l'aide d'action ViewRenderer qu'il faudra
rendre la Vue portant le nom que la-dite méthode.
Pour expliquer la mise
en correspondance, un Contrôleur Index contenant une méthode
indexAction()
correspond à un template de Vue
enregistré dans le fichier
/application/views/scripts/index/index.phtml
. Cela
signifie que ErrorController::indexAction()
correspondrait au template
/application/views/scripts/error/index.phtml
, et
ainsi de suite. C'est une convention toute simple. Créons maintenant un
template pour l'action
IndexController::indexAction()
; il sera
enregistré sous
/application/views/scripts/index/index.phtml
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
Avec Zend Framework, les
templates fonctionnent en étant inclus dans la portée de l'objet
Zend_View
. En conséquence, toutes les propriétés et
méthodes accessibles par Zend_View
sont aussi
accessibles depuis les templates, en utilisant $this
(pour
faire référence à l'instance courante de Zend_View
pour laquelle la portée des variables s'applique).
C'est un template
parfaitement valide et qui fonctionne ; mais quelque chose que nous
verrons plus en détail ultérieurement est le concept d'aides de Vues (View
Helpers). Pour faire court, une aide de Vue encapsule des tâches de
présentation fréquemment utilisées au sein d'une classe d'aide. Par
exemple, allons-nous ajouter la chaîne de Doctype à des dizaines de
templates ? Si nous faisons cela, et que le Doctype change, nous aurons
des dizaines de templates à modifier. Donc, non, nous ne ferons pas comme
ça. Zend_View
a une aide que nous avons utilisé
dans le bootstrap pour définir un Doctype par défaut. De la même manière,
nous avons utilisé une autre aide dans le bootsrap pour ajouter une
en-tête http-equiv avec un Content-Type par défaut à la section des
informations meta du head de notre page. En utilisant ces deux aides, qui
peuvent être affichés directement du fait qu'ils implémentent
indirectement la méthode magique __string()
, nous
pouvons réduire notre template à :
<?php echo $this->doctype() ?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php echo $this->headMeta() ?>
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
Maintenant, nous avons toujours "en" en trois emplacement différents ; nous pourrions automatiser cela avec une aide de Vue pour être sûr qu'ils soient tous les trois mis à jour en fonction de la langue de notre contenu. Cela montre pourquoi les aides de Vues sont utiles : nous pouvons leur confier des tâches en rapport avec la modification de Vue, afin qu'elles soient réutilisables un grand nombre de fois, y compris dans différentes Vues. Il n'est pour l'instant pas nécessaire de comprendre en détail le fonctionnement des aides de Vues ; pour l'instant, tout ce que vous avez besoin de savoir est qu'elles existent !
Maintenant, éditons
IndexController.php
pour passer le titre de la page
(référencé par $this->title
dans le script de Vue) à la
Vue :
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Hello World!';
}
}
Tous les Contrôleurs
doivent hériter de Zend_Controller_Action
, puisque
cette classe contient toutes les méthodes internes et propriétés communes
à tous les Contrôleurs. Si vous avez besoin de tuner le Contrôleur de base
pour ajouter de nouvelles méthodes ou propriétés, vous pouvez le faire en
définissant une sous-classe de
Zend_Controller_Action
, et, ensuite, en faisant en
sorte que tous les Contrôleurs de votre application héritent de cette
sous-classe à la place. Toutefois, soyez extrêmement prudent lorsque vous
sous-classez de cette façon : c'est utile pour ajouter de nouvelles
propriétés à la classe, mais les nouvelles méthodes sont souvent mieux
ajoutées en tant qu'aides d'actions distinctes, pour éviter d'en arriver à
un Contrôleur père qui soit à peu de chose près un gros paquet de code
spaghetti peu cohérent et difficile à maintenir.
Notre nouvelle méthode d'action ne gère pas du tout le rendering : comme dit plus haut, celui-ci est effectué automatiquement via le View Renderer qui est activé par défaut. Tout ce que nous avons à faire est renseigner les données dont nous avons besoin dans la Vue. Vous pouvez ajouter à la Vue à peu près tout ce que vous voulez de cette manière, puisque vos données devriendront simplement des propriétés dans le template de la Vue. Donc, n'hésitez pas à ajouter des objets et des tableaux. Si vous utilisiez Smarty ou PHPTAL, les choses pourraient être un peu différentes. On en revient finalement au concept de la Vue utilisant des Modèles, dont nous avons discuté plus tôt : de manière générale, un Modèle est un objet ; il n'y a donc aucune raison de limiter les Vues à de simples arrays lorsqu'il est plus simple ou pratique de passer des objets.
Plus haut, nous avons
dit à la Vue que les templates pouvaient faire référence à une propriété
publique nommée "title", et lui avons affecté la valeur "Hello, World!".
Une fois que la méthode d'Action est terminée, le View Renderer va entrer
en action, et essayer de rendre un template localisé à
/application/views/scripts/index/index.phtml
.
Au sein du script de
Vue, nous échappons le titre, en considérant qu'il pourrait ne pas être
sûr. Il est à peu près obligatoire de tout échapper de cette manière
lorsque vous générez une sortie HTML. En interne,
Zend_View
utilise
htmlspecialchars()
par défaut, et lui passe
l'encodage de caractères de l'instance courante. C'est encore un de ces
petits ennuis : avec Zend Framework 2.0, cet échappement sera fait
automatiquement, sans que nous ayons à continuellement appeler la méthode
escape()
.
Dans ce chapitre, nous avons vu un exemple relativement simple (haha !) de la manière dont Zend Framework fonctionne lorsque nous construisons une application. Nous avons aussi mis en évidence l'importance du processus de bootstrapping, puisque c'est là où la quasi-totalité de notre configuration est regroupée, y compris la mise en place de valeurs par défaut ou le travail de configuration nécessaire pour les composants de Zend Framework. Tout au long de ce livre, la gestion de plugins personnalisés, d'aides d'actions, ou d'autres classes similaires et/ou non-standard de Zend Framework se fait dans le Bootstrap, puisque c'est l'emplacement le plus logique pour s'occuper à la fois de leur configuration et de leur inclusion dans l'application.
Au chapitre suivant,
nous verrons un point que nous avons omis ici, mais qui est évident pour
quiconque suivant les versions récentes de Zend Framework : comment gérer
la complexité de ce bootsrap en utilisant
Zend_Application
, avant de poursuivre en expliquant
comment nous pouvons gérer les erreurs applicatives. Peu après, nous
commencerons à creuser en direction d'une application réelle, puisque j'ai
désespérément besoin d'un remplacement pour mon blog, et que, par chance,
c'est une application simple qui nous permettra d'explorer d'autres
composants de Zend Framework plus en profondeur.