Vous êtes sur la page 1sur 25

Tutoriel premier en MVC avec Zend Framework

Olivier Capuozzo 17 fvrier 2010 Rsum Support de cours destination d'tudiants en formation professionnelle (BAC +2), BTS Informatique de Gestion option Dveloppeur d'Applications. Prrequis : Concepts de base de l'objet, premire exprience dans un langage (Java) HTML et SQL dans les grandes lignes Objectifs : Dcouvrir par la pratique le potentiel structurant d'une architecture MVC et de la programmation objet (PHP >=5) Approche progressive : Vue et Contrleur et Modle avec donnes sont abords en premier, puis la navigation dans le Modle et Formulaire A la fin de cet apprentissage, l'tudiant dtient des cls pour : Approfondir l'utilisation des objects techniques prsents . Exploiter la documentation officielle du framework : API et Manuel tendre sa recherche vers d'autres modules du framework (Zend_Auth , Zend_Acl, Zend_Layout, Zend_Pdf, Zend_Cache, ...) Document au format docbook , mis en page avec le processeur xsltproc et les feuilles de styles de Norman Walsh. Ce document est plac sous la Licence GNU Free Documentation License, Version 1.1 ou ultrieure publie par la Free Software Foundation. Table des matires
1. Prsentation des principaux concepts objet - PHP5/Java 2. Une typologie des applications 3. Architecture Bazar 4. MVC 5. Zend Framew ork 6. Installation des composants 7. Structures des rpertoires de l'application 8. Contrle de connaissances 9. Architecture MVC : Modle et contrat : Vues - Contrleur 10. Cration d'un projet 11. Le Modle 11.1. Installation de w orld 11.2. Les tables 11.3. Paramtres de conf iguration la base de donnes 11.4. Rles du modle 11.5. Utilisation de Zend_Db_Table, Zend_Db_Row 12. Mise en place d'un contrleur 12.1. Mthode d'Action 12.2. Vue d'action 12.3. Rsum 13. Contrleur et Vues 13.1. Variables passes la vue 13.2. La vue exploite les donnes 13.3. Rsum 14. Passage d'arguments via l'url 14.1. Conception de la vue 14.2. Passage d'arguments 15. Exercices 16. Oprations CRUD 16.1. Slectionner une ligne, une objet mtier 16.2. Modif ier un objet mtier, une ligne 16.3. Cration d'un objet mtier, une ligne 16.4. Suppression d'un objet mtier, une ligne 16.5. Transaction 16.6. Quelques liens utiles 17. Introduction la navigation dans le modle 17.1. Dclaration des relations dans le modle 17.2. Relation Un--Un (One To One) 17.3. Relation Un--Plusieurs (One to Many) 17.3.1. Vue de la liste des villes d'un pays 17.3.2. Mthode d'action du contrleur liant modle et vue 17.3.3. Mthode de liaison entre modle 17.4. Relation Plusieurs--Plusieurs (Many-To-Many) 17.5. Mthodes magiques 18. Formulaire et Validateurs 18.1. Qu'est-ce qu'un f iltre ? 18.2. Qu'est-ce qu'un validateur ? 18.3. Exemple de code d'exploitation du formulaire 19. Quelques modules (guide, tutoriel) explorer, dans la continuit de ce tutoriel. 19.1. Gestion des droits utilisateur rapprocher avec la notion UML d'acteur/cas d'utilisation 19.2. Gestion du suivi de sessions 19.3. Structuration de blocs de prsentation 20. Conclusion

1. Prsentation des principaux concepts objet - PHP5/Java

Figure 1. Concepts Objet avec PHP5

Un version PDF ici (une contribution de Boris Vacher) : tabPooPhpJava.pdf

2. Une typologie des applications


D'un point de vue "physique", les architectures d'applications sont traditionnellement rparties en trois catgories : Autonome C'est le cas d'applications qui ne dpendent pas de systmes tiers, autres que ceux gnralement offerts par un systme d'exploitation. Client/Serveur Modle phare des annes 80, o le systme d'information est centr sur les donnes. La logique mtier est rpartie, plus ou moins, entre le client (qui dtient/excute les formulaires) et le serveur (SGBDR). Architecture 3 tiers (et plus) Tiers veut dire parties. Alors que les applications C/S sont de types 2 parties (le client IHM et le SGBDR), les architectures n tiers (pour n > 2) font intervenir un middleware applicatif responsable de la logique applicative. Le terme middleware est prendre dans le sens intermdiaire de communication entre 2 niveaux. Cette vue par "parties" (tiers) correspond un changement de niveau ds qu'un module logiciel doit passer par un intermdiaire de communication (middleware) pour en invoquer un autre - ref : w w w .octo.com. Le modle n tiers le plus rpandu des annes 2000 est le modle 3 tiers Web, typiquement reprsent par :

Navigateur prsentation coordination

<----> http

Serveur d'applications pages dynamiques composants logiciels

<-----------> SGBDR middleware SGBD

C'est ce modle que nous vous proposons d'exploiter, en environnement Php.

3. Architecture Bazar
Le succs de PHP vient de sa faciliter combiner, dans un mme document, la fois du HTML, CSS, instructions PHP et requtes SQL. Si le dveloppeur ne met pas un peu d'ordre il en rsulte un vrai bazar fragile, difficilement extensible, peu modulable et trs difficile maintenir. Figure 2. Organisation bazar (http://w w w .manning.com/allen/)

Oui, mais comment mettre de l'ordre ? une rponse courante ce problme consiste sparer physiquement les diffrentes logiques. Typiquement on distingue : La logique de prsentation : partie de l'application en interaction avec l'utilisateur La logique de traitement : partie de l'application qui ragit aux vnements La logique de persistance : concerne la logique de stockage des donnes (via un SGBDR par exemple) il est alors facile d'identifier ces parties avec les langages mis en oeuvre dans des applications Web... sauf que l aussi, plusieurs modles sont possibles. Client lger (ou pauvre) : la partie IHM est en (X)HTML + CSS Client riche : utilise un maximum de ressources du client Web (JavaScript, Ajax...) avec un minimum de problmatique de dploiement (respect des standards du W3C). Exemples : Netvibes, GMail. Nous nous concentrerons sur le client lger. Remarque : le modle client lourd concerne le client qui excute de la logique de traitement en s'appuyant sur des composants ddis et dploys pour l'occasion (le navigateur ne suffit pas). Exemple application Swing.

4. MVC
Le model MVC (Model View Controller) adapt au Web est une rponse une division des responsabilits. Figure 3. Organisation MVC (http://w w w .manning.com /allen/)

Dans ce contexte : la partie Vue est prise en charge par un script (PHP ou autre langage) gnrant du HTML (sans autre logique de traitement) la partie contrleur est reprsente par un script PHP qui dclenche des traitements lis aux services (Use Case) auquel il est attach (Un contrleur par Use Case) la partie Modle est reprsente par des scripts PHP grant les accs au SGBDR. Cela peut tre par exemple des classes Mtier qui implmentent des fonctions CRUD vers un SGBDR. Remarquez que les framework applicatifs (CMS, Forum, ...) sont gnralement bass sur une architecture MVC. D'autres exemples ici : http://f r.w ikipedia.org/w iki/Liste_de_framew orks_PHP

5. Zend Framework
Pourquoi utiliser un framework ? En informatique, un framework peut tre vu comme un outil de travail adaptable. C'est un ensemble de bibliothques, d'outils et de conventions permettant le dveloppement rapide d'applications. Un framework est compos de briques logicielles organises pour tre utilises en interaction les unes avec les autres. L'utilisation d'un framework permet le partage d'un savoir-faire technologique et impose suffisamment de rigueur pour pouvoir produire une application aboutie et facile maintenir ( voir Framew ork sur Wikipedia). Nous utiliserons Zend Framework. ZF est un framework Open Source de la socit Zend qui se veut modulaire et modulable. Tableau 1. Voici quelques modules du Zend Framework Core: Zend_Controller Authentication and Access: Zend_View Zend_Acl Zend_Db Zend_Auth Zend_Locale Zend_Date Internationalization:

Zend_Config Zend_Filter Zend_Valdiate Zend_Registry

Zend_SessionZend_Controller

Zend_Measure

Inter-application communication: Http: Zend_Json Zend_Http_Client Zend_XmlRpc Zend_Http_Server Zend_Soap Zend_UriZend_Controller Zend_Rest Advanced: Zend_Cache Misc! Zend_Search Zend_Measure Zend_Pdf Zend_Mail/Zend_Mime

Web Services: Zend_Feed Zend_Gdata Zend_Service_Amazon Zend_Service_Flickr Zend_Service_Yahoo

6. Installation des composants


Une solution (L|M|W)AMP est requise. La version de PHP >= 5.2.4 Le mode rewrite d'apache doit tre activ. Plus d'info ici. La dernire version du framework http://f ramew ork.zend.com/dow nload/ Ainsi que la documentation API : http://framew ork.zend.com/apidoc/core/ et Guide du programmeur : http://f ramew ork.zend.com/manual/f r/ Voir ce site ddi au framework PHP en franais : http://w w w .z-f.fr/

7. Structures des rpertoires de l'application


L'objectif est d'isoler physiquement les diffrentes logiques (vues, contrleurs, modles, zone publique, librairies) Pour ce faire, il est ncessaire d'tablir une arborescence de rpertoires . Nous utiliserons celle-ci :

NomDuProjet |-- application | |-- configs | |-- controllers | |-- forms | |-- layouts | |-- models | `-- views |-- library | `-- Zend (racine - ou lien vers - des librairies de ZF) `-- public | |-- css `-- scripts |-- (des scripts sql par exemple)

La zone publique contiendra des ressources directement accessibles. Par exemple via l'url http://localhost/~kpu/ZFQuickstart/public/myImage.png. On veillera interdire l'accs aux autres rpertoires.

8. Contrle de connaissances
POO 8.1. Le modle objet de Php implmente une logique par rfrence ? 8.2. En POO Php, this et self f ont-ils rf rence l'instance courante ? 8.3. Quel est le rle de la mthode toString ? 8.1. Le modle objet de Php implmente une logique par rfrence ? Oui 8.2. En POO Php, this et self font-ils rfrence l'instance courante ? FAUX. this a le mme rle qu'en Java, et self fait rfrence l' instance de la classe charge en mmoire. 8.3. Quel est le rle de la mthode toString ? Reprsenter textuellement l'tat de l'instance concerne par l'appel. ZF 8.1. L'"architecture bazar" permet un dveloppement rapide d'application 8.2. Avec ZF/MVC seules certaines ressources sont directement accessibles par l'internaute. 8.1. L'"architecture bazar" permet un dveloppement rapide d'application En apparence seulement ! cette approche du dveloppement gnre une complexit (tout est mlang) qui tend au blocage total, proportionnellement l'accroissement de la couverture fonctionnelle de l'application. 8.2. Avec ZF/MVC seules certaines ressources sont directement accessibles par l'internaute. VRAI, moyennant quelques prcautions ct configuration du serveur HTTP (.htaccess par exemple). Dans notre exemple, elles sont situes dans l'arborescence ayant public comme racine.

9. Architecture MVC : Modle et contrat : Vues - Contrleur


On entend par modle la couche ayant en charge les donnes. Cette couche de service interagit avec le systme de persistance, les contrleurs et les vues. Un contrleur est le point d'entre d'un cas d'utilisation. Les responsabilits d'un contrleur de cas d'utilisation sont : Prendre en charge la logique du service demand (les traitements) Interagir pour cela avec les donnes de l'application, reprsentes par le modle (le M de MVC)

Transmettre la vue les informations dont elle a besoin pour une rponse personnalise en direction du demandeur (client web) Les responsabilits d'une vue sont : Prsenter les informations dont elle dispose (voir contrleur). Grer la prsentation de l'absence possible de certaines informations (liste vide par exemple) Contrleurs et Vues sont donc lis par un contrat : La vue prend en charge la prsentation des informations Le contrleur, aprs traitement, est tenu de transmettre la vue les informations dont elle a la charge Comme on le verra bientt, les contrleurs sont implments sous forme d'objet (classe spcialise) et leurs actions sous forme de mthodes d'instance, dites mthode d'action. Les informations peuvent provenir d'une base de donnes, et/ou de donnes calcules, prpares par la mthode d'action. Le schma gnral est : Figure 4. Interaction type d'un systme MVC avec les donnes

On trouvera d'autres reprsentations ici. L'tape suivante vise installer ces notions, indpendamment du contexte du quickstart, l'ide tant de nous accrocher une base de donnes, avec une technique d'implmntation du modle plus rudimentaire que celle propose par l'application quickstart (qui fait rfrence au modle de conception Data Mapper de Martin Fow ler ).

10. Cration d'un projet


Certains dtails de la procdure dcrite ici concerne essentiellement ceux qui ont un compte sur un serveur unix ou GNU/Linux, en non root, avec le mode userdir d'apache activ avec les paramtres par dfaut - bref ceux qui ont leur racine de publication nomme public_html dans leur home directory. Nous proposons dans ce tutoriel une approche progressive des concepts et techniques mis en oeuvre par le framework, destination des dbutants. en ligne de commande # dans le dossier bin de ZF ZendFramework-1.9.5/bin$ ./zf.sh create project zf-tuto # on deplace le projet la racine de publication mv zf-tuto/ ~/public_html # aller dans le dossier cd ~/public_html/zf-tuto # dplacer dans les fichiers de dmarrage la racine mv public/index.php . mv public/.htaccess . # modifier les chemins dans index.php // Define path to application directory defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/application')); # ajouter le chemin des modeles // Ensure library/ is on include_path set_include_path(implode(PATH_SEPARATOR, array( realpath(APPLICATION_PATH . '/../library'), realpath(APPLICATION_PATH . '/models'), get_include_path(), )));

# modifier .htaccess RewriteBase /~XXX/zf-tuto # dans library/ crer un lien symbolique vers ou copier y le dossier ZendFramework-1.9.5/libray/Zend

Voila, l'installation du squelette du projet est termine. Vous remarquerez que le fichier index.php ne comporte aucune logique de l'application ; en effet son rle est principalement technique. Le terme technique est, dans ce contexte, oppos au terme mtier, ce dernier terme faisant rfrence la logique de l'application rgie par les rgles de gestion lies au problme rsoudre. Faire un test : http://localhost/~XXX/zf -tuto Paralllement la cration du projet, nous installons la base de donnes world sur un serveur MySQL.

11. Le Modle

world est une base de donnes statistiques go-politiques sur le monde datant de 2006, d'aprs Off icial Statistics of Finland. La base de donnes que nous installons est issues de MySql : w orld - lgrement modifie pour l'occasion (vous trouverez ici les grandes lignes de la modif cation) . 11.1. Installation de world Instructions rapides : tlcharger le script w orld2.zip et le dcompresser. mysql -u root < world2-schema.sql mysql -u root < world2-data.sql

11.2. Les tables Figure 5. tables

Voici une description SQL de la structure de la table Country (on aurait pu en choisir une autre)

mysql> describe Country; +----------------+--------------------------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+--------------------------------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | Code | char(3) | YES | | NULL | | | Name | char(52) | NO | | | | | Continent | enum('Asia','Europe', | | | | | | | 'North America', 'Africa','Oceania', | | | | | | | 'Antarctica','South America') | NO | | Asia | | | Region | char(26) | NO | | | | | SurfaceArea | float(10,2) | NO | | 0.00 | | | IndepYear | smallint(6) | YES | | NULL | | | Population | int(11) | NO | | 0 | | | LifeExpectancy | float(3,1) | YES | | NULL | | | GNP | float(10,2) | YES | | NULL | | | GNPOld | float(10,2) | YES | | NULL | | | LocalName | char(45) | NO | | | | | GovernmentForm | char(45) | NO | | | | | HeadOfState | char(60) | YES | | NULL | | | Capital | int(11) | YES | | NULL | | | Code2 | char(2) | NO | | | | +----------------+--------------------------------------+------+-----+---------+----------------+

Par simple commodit ici, nous utiliserons l'utilisateur 'root' alors que nous devrions crer d'un utilisateur ou plusieurs utilisateurs spcifiques pour cette base (par exemple via l'entre "User Administration" de MySQL Administrator . Combien de lignes contient cette table ? $ mysql -u root world2 mysql> select count(*) as `nombre de pays` from Country; +----------------+ | nombre de pays | +----------------+ | 239 | +----------------+ Il est temps maintenant de configurer la connexion la base de donnes. 11.3. Paramtres de configuration la base de donnes Nous ajoutons quelques lignes application/configs/application.ini (ZF utilise PDO pour l'accs aux donnes) [production] ... database.adapter = PDO_MYSQL database.params.host = localhost database.params.username = root database.params.password = database.params.dbname = world2

Puis nous exploitons ces informations au dmarrage (dans index.php)

-- dans index.php // Chargement automatique de Zend_Db_Adapter_Pdo_Mysql, et instanciation. $config = new Zend_Config_Ini('./application/configs/application.ini', 'production'); $db = Zend_Db::factory($config->database->adapter,array( 'host' => $config->database->params->host, 'username' => $config->database->params->username, 'password' => $config->database->params->password,

'dbname' ) );

=> $config->database->params->dbname,

// placons la connexion dans un registre global l'application $registry = Zend_Registry::getInstance(); $registry->set('db', $db); // en faire la connexion par defaut Zend_Db_Table::setDefaultAdapter($db);

Nous dfinissons un objet Zend_Db_Adapter (voir Manuel en ligne). On remarquera la souplesse d'utilisation de Zend_Config_Ini : les cls du fichier de configuration sont traduites en proprits public, ce qui nous permet de consulter une valeur associe une cl par : instance_Zend_Confi_Ini->cl Nous allons voir maintenant comment lier une classe cette table. 11.4. Rles du modle Dans une application de gestion typique (interface avec un SGBDR), il n'est pas rare d'avoir un modle qui colle aux tables concernes par les responsabilits de l'application. Dans ce contexte, il parait alors logique de charger le modle des responsabilits d'accs aux donnes : create, retreive, update, delete (CRUD). Il est commode alors de faire hriter les classes du modle par une classe prenant en charge les interactions typiques avec un SGBDR. ZF propose la classe Zend_Db_Table_Abstract comme parent de base des classes du modle. 11.5. Utilisation de Zend_Db_Table, Zend_Db_Row Nous avons avec la base de donnes (le SGBDR), deux types de structure grer : La table et ses lignes ZF met notre disposition deux super-classes Zend_Db_Table_Abstract et Zend_Db_Table_Row_Abstract Concevons une classe modle reprsentant une instance d'un pays : CountryRow (les raisons de ce nommage seront justifies ultrieurement) : Remarque : les classes du modle seront places dans le dossier models ZFQuickstart |-- application | |-- controllers | |-- models | | |--CountryRow.php | `-- views |-- library | `-- Zend (racine des librairies de ZF) `-- public |-- styles |-- images `-- js

<?php // CountryRow.php class CountryRow extends Zend_Db_Table_Row_Abstract { public function getNbCities(){ // TODO return -1; } }

Nous concevons en plus la classe Country que nous lions CountryRow :

// Country.php class Country extends Zend_Db_Table_Abstract { protected $_name = 'Country'; protected $_primary = 'id'; protected $_rowClass = 'CountryRow'; }

C'est tout ? Pour l'instant OUI. Les attributs de la classe CountryRow sont auto-gnrs partir du nom des attributs de la table (les colonnes).

Avertissement
Vous devez concevoir toutes les classes du modle AVANT de poursuivre ce tutoriel. A savoir : Country, CountryRow, Language, LanguageRow, City, CityRow, CountryLanguage

12. Mise en place d'un contrleur


Nous allons concevoir la Consultation de l'ensemble des pays. C'est un cas d'utilisation du sytme. Nous allons donc mettre en place un contrleur de cas d'utilisation. Ce contrleur sera accessible par l'url : http://localhost/~kpu/zf-tuto/country

Par dfaut, ZF fait corres pondre cette ressource un script PHP bien particulier. Hormis le fait que le nom de ce script est bas sur le nom de la ressource (ici Country) suivi du mot Controller (on remarquera la capitalisation des termes), ce s cript, qui est en fait une classe hritant de Zend_Controller_Action, sera plac dans la branche controllers :

ZFQuickstart |-- application | |-- controllers | | |--CountryController.php | | | |-- models | `-- views |-- library | `-- Zend (racine des librairies de ZF) `-- public |-- styles |-- images `-- js

Voici le contrleur en question :

<?php class CountryController extends Zend_Controller_Action { public function init() { } public function preDispatch() { } public function indexAction() { $this->render(); } public function postDispatch() { }

Prsentons rapidement ce script PHP C'est une classe qui hrite de Zend_Controller_Action, une classe de framework. init: appel une seule fois, l'instanciation de la classe ( l'image d'un constructeur) preDispatch: appel avant un appel automatique une mthode d'action xxAction : des mthodes d'action postDispatch: appel aprs un appel automatique une mthode d'action Il est important de comprendre que vous ne ferez quasiment jamais appel explicitement ces mthodes ! elles font partie de la logique du framework, et c'est ce dernier qui prend en charge les appels. Votre rle, en tant que dveloppeur, consiste dfinir (concevoir, dclarer) les bonnes classes, les bonnes mthodes. 12.1. Mthode d'Action Une Mthode d'Action est une mthode d'une sous-classe de Zend_Controller_Action identifie par un nom se terminant par Action. Par exemple : ajouterAction, supprimerAction. Qu'est-ce qu'une Action ? C'est le service associ une ressource. Par exemple, dans la requte suivante : http://serveur/racineApplication/document/obtenir la ressource demande est document et le service sollicit est obtenir. Dans un contexte ZF de base, le contrleur DocumentController.php (qui sera automatiquement charg, suivi d'une instanciation) devra contenir la mthode obtenirAction, car c'est cette dernire qui sera appele par le framework. L'action par dfaut : En abscence de nom de service l'action indexAction sera invoque. Par exemple, dans la requte suivante : http://serveur/racineApplication/document la mthode indexAction de l'objet, instance de DocumentController (ou IndexController par dfaut), sera appele. Il en va de mme avec la ressource. En abscence de nom de ressource, c'est la ressource index qui sera sollicite. Par exemple, dans la requte suivante : http://serveur/racineApplication/ sera interprt comme: http://serveur/racineApplication/index/index Cela suppos e donc que le contrleur IndexController et sa mthode indexAction existent ! Sollicitons notre contrleur CountryController : Figure 6. Interaction avec le contrleur

Nous avons droit une erreur, c'est tout fait NORMAL ! Explications : En analysant la premire ligne (voir encadr) du rapport d'erreur (pas d'affolement, l'analyse de la premire ligne suffit dans la majorit des cas), nous constatons que le systme recherche le fichier ./application/views/scripts/country/index.phtml. C'est en fait le script responsable de la VUE (le V de MVC), le contrleur de cas d'utilisation (le C de MVC) ne s'occupant que de la logique de type conversationnel et traitement. En effet, il ne devrait pas y avoir d'instruction echo ou print dans un contrleur (mais parfois des Zend_Debug::dump() ;-). 12.2. Vue d'action Comme on a pu le constater, ZF, par dfaut, s'attend ce que le dveloppeur ait conu des vues pour chaque mthode d'action.

ZFQuickstart |-- application | |-- controllers | | |--CountryController.php | | | |-- models | `-- views | | |--scripts | | | |--country | | | | |--index.phtml |-- library | `-- Zend (racine des librairies de ZF) `-- public |-- styles |-- images `-- js

Les vues sont stockes dans la branche racine/application/views/scripts/xxx/yyy, ou xxx est le nom de la ressource sollicite (en rfrence au contrleur), et yyy le nom de la vue en relation, de la forme nomDuService.phtml . Le nom du service tant la partie gauche du nom de la mthode d'action. Par exemple : http://serveur/racineApplication/document/ajouter la mtode ajouterAction de l'objet, instance de DocumentController, sera appele, puis la vue application/views/scripts/document/ajouter.phtml sera traite et retourne au client web. Bien entendu, toute cette logique de traitement est entirement prise en charge par le framework. Voici un exemple de vue ./application/views/scripts/country/index.phtml :

<h1>Les pays</h1>

Sollicitons de nouveau notre contrleur CountryController : Figure 7. Interaction avec le contrleur : http://.../zf-tuto/country

12.3. Rsum Nous avons vu : Comment concevoir, par hritage, un contrleur de cas d'utilisation. Comment solliciter, par l'URL, une action d'un contrleur de cas d'utilisation. Comment associer une vue une action du contrleur Cas particuliers Le contrleur, aprs traitement, n'a pas de donne transmettre la vue : => il peut passer la main une autre mthode d'action :

... public function supprimerAction() { ... $this->_forward('index'); // soustraite (renvoi) une autre action de ce contrleur }
La vue n'attend rien du contrleur. Pas de problme, c'est ce que nous avons fait jusqu' prsent. Slection d'une autre vue (lie au mme contrleur) que celle base s ur le nom de l'action : $this->render('autrevue'); - plus d'infos ici zend.controller.action.html

13. Contrleur et Vues


Contrleurs et Vues sont lis par contrat. Le contrleur passe des donnes la vue, cette dernire doit en connatre la nature, c'est dire le nom et le type associ . 13.1. Variables passes la vue Le contrleur passera un objet de type "RowSet" (ens. de lignes) d'objets de type "Row" (ligne). Les objets de type Zend_Db_Table_Row , sont automatiquement spcialiss par ZF en fonction du modle. Il est temps maintenant de coder le contrleur :

require_once('Country.php'); class CountryController extends Zend_Controller_Action { public function init() { } public function indexAction() { // on retrouve le registre global $registry = Zend_Registry::getInstance(); // et notre connexion $db = $registry->get('db'); // que l'on passe au constructeur (hrit) de notre modle $ct = new Country($db); // obtenons un objet Zend_Db_Select (capable d'appliquer des restrictions) $select = $ct->select(); // aucune clause where, donc cette variable est inutile ici // nous transmettons sous le nom 'countries' la vue l'ensemble // des lignes de la table Country soit un objet Zend_Db_Table_Rowset // (colection d'objets CountryRow - des Zend_Db_Table_Row) $this->view->countries=$ct->fetchAll($select);

$this->render(); } [...]

Connexion par dfaut


Si l'application n'accde qu' une seule base de donnes, on peut, par exemple dans le bootstrap (index.php), spcifier une connexion par dfaut.

Zend_Db_Table::setDefaultAdapter($db); Zend_Registry::set('dbAdapter', $db);


Ainsi, dans le contrleur, le code se trouve encore rduit : :

// SANS connexion par dfaut // on retrouve le registre global $registry = Zend_Registry::getInstance(); // et notre connexion $db = $registry->get('db'); // que l'on passe au constructeur (hrit) de notre modle $ct = new Country($db); //AVEC connexion par dfaut (une seule instruction !) // Aucun argument la construction. // Liaison automatique via la connexion par dfaut $ct = new Country(); $this->view->countries=$ct->fetchAll();
13.2. La vue exploite les donnes Le script de la vue associ l'action est : views/country/index.phtml. En effet, les attributs automatiques de notre modle sont directement accessibles (non privs).

<table> <?php foreach ($this->countries as $country) : ?> <tr> <td> <?php echo $this->escape($country->id) ?> </td> <td> <?php echo $this->escape($country->Name) ?> </td> <td> <?php echo $this->escape($country->Continent) ?> </td> <td> <?php echo $this->escape($country->LocalName) ?> </td> </tr> <?php endforeach; ?> </table>
Ce script utilise la mthode escape pour chapper les caractres (Par dfaut, la mthode escape() utilise la fonction PHP htmlspecialchar() pour l'chappement). A noter la syntaxe la endif , voir doc syntaxe PHP qui favorise la lecture (les accolades ouvrantes et fermantes sont parfois difficiles reprer dans la vue). Figure 8. country/index.phtml

Ce script de vue exploite une collection d'objets de type Country.

Le contrat qui lie la vue et le contrleur est simple : La vue considre qu'un contrleur lui a mis disposition, sous l'appellation countries, une collection d'objets de type Country. Remarque : Dans cette configuration, Contrleur et Vue sont dpendants du Modle. 13.3. Rsum On a vu : Les devoirs du contrleur, de la vue. Les droits de la vue (ceux du contrleur tant lis aux donnes reues ainsi qu'au modle) La faon dont le contrleur passe des donnes la vue. La faon dont la vue exploite des donnes. ... et pour les plus observateurs, la faon d'envoyer des donnes de dbogage (Zend_Debug::dump).

14. Passage d'arguments via l'url


Nous souhaitons ne pouvoir consulter qu'un seul pays la fois (et non une liste comme prcdemment) .

Autre service -> autre UC


Nous considrons cette demande comme un nouveau cas d'utilisation du systme spcifique au contrleur Country. Nous concevons alors une nouvelle mthode d'action : voirPays. 14.1. Conception de la vue Par convention, les noms capitaliss sont associs des noms o les caractres majuscule sont remplacs par le caractre minus cule correspondant prcd d'un tiret (voir doc ici). :action: MixedCase and camelCasedWords are separated by dashes; non-alphanumeric characters are translated to dashes, and the entire string cast to lower case. Examples: "fooBar" becomes "foo-bar"; "foo-barBaz" becomes "foo-bar-baz".

Fichier voir-pays.phtml

<style type="text/css"> <!-.pays { margin-left: 40px; } --> </style> <div class='pays'> <h2>Pays</h2> Pays : <?= $this->pays->Name ?> <br> Continent : <?= $this->pays->Continent ?> <br> Nombre de villes inscrites : <?= $this->pays->getNbCities() ?> <br> </div>

Le problme se pose ct contrleur : Quel pays transmettre la vue ? Le service devra donc tre paramtr. Le contrleur attend donc une donne lui permettant de dterminer le bon pays transmettre la vue. Par dfaut le contrleur trans mettre le premier de la liste (id=1). Voici la mthode d'action en charge de ce cas d'utilisation du contrleur CountryController :

public function voirPaysAction() { if ($this->_hasParam('id')) $index = $this->_getParam('id'); else $index = 1; $ct = new Country(); $pays = $ct->find($index)->current(); $this->view->pays=$pays; $this->render(); }

La mthode vrifie qu'elle dispose bien d'un argument ('id'). Si ce n'est pas le cas, elle choisira le premier pays (index=1), sinon, elle utilise directement la valeur reue index (trs dangereux, ne pas reproduire !) Nous obtenons : Figure 9. http://localhost/~kpu/zf-tuto/country/voir-pays

Noter que la doc API (method _getParam) nous indique que la mthode _getParam admet comme second argument optionnel un valeur par dfaut. Ainsi aurions-nous pu crire :

$index = $this->_getParam('id', 0);

14.2. Passage d'arguments Nous passerons les arguments par l'URL. Le mode rewrite tant activ sur le serveur HTTP, les arguments peuvent tre insrits dans une expression de chemin : http:// ... /zf-tuto/country/voir-pays/id/1

quivalent : http:// ... /zf-tuto/country/voir-pays?id=1

Sauf que l'expression de chemin est d'un apparence plus stable, donc plus facile grer pour les moteurs de recherche, entre autres. La syntaxe gnrale de passage d'arguments : .../nom1/val1/nom2/val2/.../nomN/valN Nous testons le contrleur en demandant de consulter un autre pays (id=3) Figure 10. http://localhost/.../zf-tuto/country/voir-pays/id/3

Enfin, voici les liens logiques liant les lments de la requte et le contrleur. Figure 11. http://localhost/.../zf-tuto/country/voir-pays/id/3

15. Exercices
Avant d'interagir avec le modle, et maintenant que vous avez compris l'essentiel (rle d'une vue, d'un contrleur, passage d'arguments), vous raliserez les cas d'utilisation suivant: 1. Permettre un utilisateur de consulter les caractristiques d'UN pays. Prvoir une nouvelle action : http:// ... /zf-tuto/country/voir-details

2. Permettre un utilisateur, lorsqu'il consulte les caractris tiques d'UN pays, de naviguer vers le prochain ou le prcdent. Prvoir une nouvelle action : http:// ... /zf-tuto/country/voir-details-nav

Indication : la vue dis pose d'une mthode, nomme url, prenant en argument un tableau associatif spcifiant des composantes ZF-MVC de l'url. Exemple :

// dans un script de vue .phtml ... <a href='<?=$this->url(array('controller'=> 'index' , 'action'=> 'lister' , 'param1' => 'valParam1' , 'param2' => 'valParam2'));?>'>un lien</a> ... // produit la sortie : // <a href='http://.../index/lister/param1/valParam1/param2/valParam2'>un lien<a>
La difficult consiste trouver le moyen de slectionner le prochain (et le prcdent) pays correspondant au pays courant, car il peut y avoir des "trous" dans les valeurs d'id (en cas

de suppression par exemple). Ci-dessous, quelques lments qui pourrait bien vous donner des ides de solution :

... // on retrouve le registre global $registry = Zend_Registry::getInstance(); // et notre connexion $db = $registry->get('db'); $this->index = $this->_getParam('id', 1); $pt = new Country($db); $pays = $pt->find($this->index)->current(); //Recuperation de l'id max $max = $db->fetchOne('SELECT MAX(id) as id FROM Country');
Vous trouverez ici : le rle de fetchOne . 3. Reprendre l'exercice prcdent mais ne pas proposer l'utilisateur la possibilit de naviguer avant le premier et aprs le dernier pays. Indication : Conformment au paradigme MVC, on veillera ne pas charger la vue en responsabilit (m tier/technique).

16. Oprations CRUD


Les oprations de base SQL sur les donnes (interrogation, cration, modification, suppression) sont assistes par des mthodes hrites de Zend_Db_Table_Abstract. En effet, ces responsabilits sont conceptuellement soit de niveau table , soit de niveau objet mtier ('tuple') . En voici quelques exemples : 16.1. Slectionner une ligne, une objet mtier Recherche le pays d'id = 1

// Fonction de niveau 'table' $dt = new Country(); $pays = $dt->find(1)->current();


La mthode find prend comme argument un identifiant (un ou plusieurs arguments selon le cas), et retourne un Rowset. La mthode current d'un Rowset retourne l'lment courant sous la forme d'un objet Row (ou NULL s'il n'existe pas. Dans notre cas c'est le premier et unique CountryRow. 16.2. Modifier un objet mtier, une ligne Modification de l'attribut 'HeadOfState' du pays d'id = 73

// Fonction de niveau 'tuple' $dt = new Country(); $rowPays = $dt->find(73)->current(); // en 2006, $rowPays->HeadOfState == 'Jacques Chirac' $rowPays->HeadOfState = "Nicolas Sarkozy"; $rowPays->save();
La mthode save fait une mise jour, dans la table Country, du tuple associ notre objet. 16.3. Cration d'un objet mtier, une ligne cration d'une ville

public function testAddAction(){ require_once 'City.php'; // Fonction de niveau 'table' et 'tuple' $ct = new City(); // les donnes sont reprsentes par un dictionnaire $data = array( 'Name' => 'Test', 'LocalName' => 'local test', // pays concern, car il en faut un. 'idCountry' => 2, 'District' => 'regionTest', 'Population' => 1 // pas grand monde ); $cityRow=$ct->createRow($data); // Zend_Debug::dump($cityRow); if ($cityRow) { $id=$cityRow->save(); // Ok, c'est fait ! // en extra, on zappe de mthode d'action, afin de visualiser le rsultat // passage en argument de l'id du pays concern par ce test $this->_setParam('id','2'); // sous-traitance de service (function villesAction de ce // contrleur, prsente un peu plus loin dans ce doc) $this->_forward('villes'); } }
La mthode createRow cre un CityRow en mmoire. La mthode save tente d'insrer le nouveau tuple dans la table City. 16.4. Suppression d'un objet mtier, une ligne suppression d'une (ou des) ville(s) portant le nom 'Test'

// Opration de niveau 'table' public function testDeleteAction(){ require_once 'City.php'; $ct = new City(); $where = $ct->getAdapter()->quoteInto('Name = ?', 'Test'); // suppression dans la table $ct->delete($where); // en extra, on zappe de mthode d'action, afin de visualiser le rsultat // passage en argument de l'id du pays concern par ce test (voir testAddAction) $this->_setParam('id','2'); $this->_forward('villes'); }
La mthode delete supprime, de la table City, le ou les tuples filtrs par la clause 'where'. 16.5. Transaction On se rfre la connexion pour grer une transaction :

$db = Zend_Registry::get('dbAdapter'); $db->beginTransaction(); try { // insert pays [...] // insert pays avec une cl primaire errone $data = array( 'id' => 1, 'Name' => 'France', ... ); $ct = new Country(); $ct->insert($data); // demande d'excution du lot des oprations $db->commit(); $this->render(); } catch (Exception $e) { // annule toutes les oprations du lot $db->rollBack(); $this->_redirect('/'); }
16.6. Quelques liens utiles le manuel en ligne, de qualit : Zend_Db des fonctions illustres d'accs aux donnes Wiki Zend_Db Une prsentation du lien entre logique du modle et table : http://zend-f ramew ork.developpez.com/ L'API Zend_Db_Table and Co

17. Introduction la navigation dans le modle


Nous nous intressons ici aux relations entre les entits du modle, entre les objets de type Table_Row. La phase prliminaire consiste dclarer les relations dans les dfinitions des Classes de niveau Table. 17.1. Dclaration des relations dans le modle En rfrence notre schma relationnel ('voir la dfinition des tables ), nous intervenons dans la dfinition des clas ses de type 'Table'

//City.php <?php //Country.php <?php class Country extends Zend_Db_Table_Abstract { protected $_name = 'Country'; protected $_primary = 'id'; protected $_rowClass = 'CountryRow'; protected $_referenceMap = array( 'Capitale' => array( 'columns' => 'Capital', 'refTableClass' => 'City', 'refColumns' => 'id' )); }

//City.php <?php class City extends Zend_Db_Table_Abstract { protected $_name = 'City'; protected $_primary = 'id'; protected $_rowClass = 'CityRow'; protected $_referenceMap = array(

'MonPays' => array( 'columns' 'refTableClass' 'refColumns' )); }

=> 'idCountry', => 'Country', => 'id'

//CountryLanguage.php <?php class CountryLanguage extends Zend_Db_Table_Abstract { protected $_name = 'CountryLanguage'; protected $_primary = array('idCountry', 'idLanguage'); protected $_referenceMap 'Langue' => array( 'columns' 'refTableClass' 'refColumns' ), 'Pays' => array( 'columns' 'refTableClass' 'refColumns' ) ); = array( => array('idLanguage'), => 'Language', => array('id')

=> array('idCountry'), => 'Country', => array('id')

} // la notation 'tableau' montre comment oprer en cas de cls multi-attributs ('id', ou array(id'))

?>
$_referenceMap est une proprit hrite, qui permet de dfinir le rle de chacune des cls trangres de la table concerne. Chaque rle est identifi par un : Un nom : par exemple 'Pays' La ou les colonnes de la cl (ventuellement compose) : par exemple array('idCountry') La classe correspondant la table rfrence : par exemple 'Country" La ou les colonnes correspondant dans la table rfrence : par exemple array('id') 17.2. Relation Un--Un (One To One) Une cas typique consiste exploiter le tuple ass oci une cl trangre. Par exemple, dans le tableau qui liste les pays, nous souhaiterions voir apparatre, non pas l'identifiant de la capitale, mais son nom. Actuellement le code de la vue est :

<table> <?php foreach ($this->countries as $country) : ?> <tr> <td> <?php echo $this->escape($country->id) ?> </td> <td> <?php echo $this->escape($country->Name) ?> </td> <td> <?php echo $this->escape($country->Continent) ?> </td> <td> <?php echo $this->escape($country->LocalName) ?> </td> <td> <?php echo $this->escape($country->Capital) ?> </td> </tr> <?php endforeach; ?> </table>
Figure 12. vue id capital

Nous souhaitons afficher le nom de la capitale, la place de son identifiant. Fidle nos principes (architecture MVC, patterns GRASP), ce n'est pas de la responsabilit de la vue de contenir le code d'extraction du nom du responsable dans le modle : C'est le pays

qui connat sa capitale , nous nous adresserons donc ce dernier.

<?php <?php foreach ($this->countries as $country) : ?> <tr> <td> <?php echo $this->escape($country->id) ?> </td> <td> <?php echo $this->escape($country->Name) ?> </td> <td> <?php echo $this->escape($country->Continent) ?> </td> <td> <?php echo $this->escape($country->LocalName) ?> </td> <td> <?php echo $this->escape($country->getCapitale()->Name) ?> <td> <a href=''> les villes </a> </td> </tr> <?php endforeach; ?> }
Nous intervenons donc au niveau de la classe des objets de type Row, afin de "drfrencer" une cl trangre. Pour cela nous utiliserons la mthode findParentRow hrite de la classe Zend_Db_Table_Row_Abstract : Figure 13. findParentRow

&bnsp; </td>

<?php class CountryRow extends Zend_Db_Table_Row_Abstract { public function getCapitale() { $city = $this->findParentRow('City', 'Capitale'); // les arguments de findParentRow : // 'City' -> c'est la table // 'Capitale' -> c'est le rle dfini dans $_referenceMap de County.php return $city; } public function getNbCities(){ // TODO return -1; } } ?>
La mthode findParentRow exploite les donnes de $_referenceMap pour slectionner la ligne (Row) de la table City lie l'instance courante (relative une ligne de la table Country). La mthode findParentRow attend deux arguments : Le premier est le nom d'une 'TableClass' et le deuxime (optionnel) est une cl de $_referenceMap, soit le nom du rle de la relation. Nous pouvons alors tester le rsultat : Figure 14. vue nom capitale

Nous venons de voir comment utiliser Zend Framework pour raliser une jointure retournant au plus une ligne. Nous nous intressons maintenant la relation Un--Plusieurs (One To Many) . 17.3. Relation Un--Plusieurs (One to Many) Je souhaite connatre l'ensensemble des villes rfrences dans la bas e de donnes pour un pays donn. Plusieurs alternatives s'offrent nous : 1. Concevoir, dans le contrleur, une requte du genre :

$db = Zend_Registry::get('dbAdapter'); $sql = "SELECT v.Name, v.District, v.Population FROM Country c, City v WHERE c.id = v.idCountry AND c.id = ?"; $sql = $db->quoteInto($sql, $idDplome); $inscrits = $db->fetchAll($sql); ...
2. Relguer cette responsabilit Country

$ct = new Country(); $pays = $ct->find($idPays)->current(); $inscrits = $pays->getVilles();


Dans le respect du pattern Expert en Inf ormation, nous choisirons la deuxime solution. Comme vous l'avez certainement constat , le lien nomm 'voir les villes' : <a href='villes/id/$pays->id'>voir</a> fait rfrence une mthode d'action du contrleur courant 'country', que nous devons concevoir. Mais intressons-nous en premier sa vue : 17.3.1. Vue de la liste des villes d'un pays Fichier : views/script/country/villes.phtml

<style> <!-table,th,td { border-style: solid; border-width: 1px; } table,caption { margin-left: auto; margin-right: auto; width: auto; } --> </style> <table> <caption>Les villes du pays <?php echo $this->nomPays; ?></caption> <thead> <tr> <th>Ville</th> <th>Rgion</th> <th>Population</th>

</tr> </thead> <tbody> <?php foreach ($this->villes as $ville) : ?> <tr><td> <?php echo $ville->Name; ?> </td> <td> <?php echo $ville->District ?> &nbsp;</td> <td> <?php echo $ville->Population ?> &nbsp; </td> </tr> <?php endforeach; ?> </tbody> </table>
Cette vue part du principe qu'elle dispose, dans son contexte d'excution, de deux attributs : $nomPays et une liste de villes $villes Voyons comment ces informations lui sont trans mises. 17.3.2. Mthode d'action du contrleur liant modle et vue La vue s'appelant villes.phtml, notre mthode action sera villesAction :

//CountryController.php public function villesAction() { $idPays=$this->_getParam('id', 1); $ct = new Country(); $paysRow = $ct->find($idPays)->current(); $villes=$paysRow->getVilles(); $this->view->villes=$villes; $this->view->nomPays=$paysRow->Name; $this->render(); }
Les diffrentes tapes 1. La mthode commence par obtenir la valeur du paramtre de la requte, avec une valeur par dfaut (pays=1) 2. Un objet de type Country est cre. 3. ... partir duquel une requte est lance, afin d'obtenir une instance de CountryRow. 4. Nous obtenons, partir d'un pays, l'ensemble des villes 5. ... et le nom du pays 6. Le tout est transmis la vue 7. ... qui prend le relai (render) 17.3.3. Mthode de liaison entre modle Sur cette base nous pouvons implmenter la mthode getVilles de la classe CountryRow. Pour cela nous utiliserons la mthode findDependanteRowset hrite de la classe Zend_Db_Table_Row_Abstract : Cette mthode permet d'obtenir l'ens emble des tuples d'une table rfrencant le tuple courant. Figure 15. findDependanteRowset

Soit :

<?php class CountryRow extends Zend_Db_Table_Row_Abstract { public function getVilles() { return $this->findDependentRowset('City'); } public function getCapitale() { $city = $this->findParentRow('City', 'Capitale'); // les arguments de findParentRow : // 'City' -> c'est la table // 'Capitale' -> c'est le rle dfini dans $_referenceMap de County.php return $city;

} public function getCapitaleBis() { $db = $this->getTable()->getAdapter(); // ou : -- voir index.php // $registry = Zend_Registry::getInstance(); // $db = $registry.get('db'); $res = $db->query("SELECT * FROM City WHERE id = ?", array($this->Capital)); require_once('CityRow.php'); $city = new CityRow(); $res->setFetchMode(PDO::FETCH_INTO, $city ); $city = $res->fetchObject(); return $city; } public function getVillesBis() { // "SELECT * FROM City WHERE id = ?", array($this->id) }

public function getNbCities(){ // TODO return -1; } }


La mthode findDependentRowset retourne un Rowset (et non pas un Row comme prcdemment, qui est l'ensemble des objets CityRow (relatifs aux lignes) lignes de la table City qui sont en relation avec this.

Le rle des relations


La mthode findDependentRowsetpeut prendre un deuxime argument, dont la valeur est un des noms de rle (cl de $_referenceMap de la classe en premier argument - voir City.php)

return

$this->findDependentRowset('City', 'MonPays');

17.4. Relation Plusieurs--Plusieurs (Many-To-Many) Cas d'une table relation porteuse ou non de proprits Une table relation, connue galement sous l'appellation table intersection, est une table dont la principale raison d'tre est de mettre en relation deux tuples (d'une mme table ou de tables diffrentes). La cl trangre d'une des deux tables est s ystmatiquement "drfrences". Zend Framework fournit une faon directe d'exploiter des tables relation. Figure 16. Many To Many

Prenons un exemple, si nous avions voulu prendre connaissance des pays o la langue franaise est parle, tout en connaissant les dtails (officielle ou non, pourcentage parl). C'est dire que nous souhaitons obtenir la listes des pays (objet CountryRow) via CountryLanguageRow :

// LanguageRow.php // obtenir tous les pays parlant cette langue public function getPaysParlantCetteLangue() { return $this->findManyToManyRowset('Country', 'CountryLanguage'); }
Nous utilisons ici la table relation CountryLanguage ct Country (prendre les informations des deux tables). La mthode findManyToManyRowset prend en argument le nom de (ou un objet de) la classe correspondant au Rowset retourner (ici se sera des objets Etudiant) et en deuxime argument, ce que la documentation zend.db.table.relationships.html nomme table intersection (connu aussi sous l'appelation 'table de relation'). La mthode reoit optionnellement d'autres arguments comme le rle dans $_referenceMap (sinon c'est le premier rle qui est lu). Voici un exemple d'utilisation : L'utilis ateur demande la liste des pays o la langue franaise est reprsente : Figure 17. http://localhost/~kpu/zf-tuto/country/pays-langue/idlangue/23

Puis l'utilisateur souhaite connatre les langues rfrences pour un pays donn : Figure 18. http://localhost/~kpu/zf-tuto/country/langues/id/44

Les actions du controleur :

public function languesAction() { $idPays=$this->_getParam('id', 1); $ct = new Country(); $paysRow = $ct->find($idPays)->current(); $langues=$paysRow->getLanguesParlees(); $this->view->langues=$langues; $this->view->nomPays=$paysRow->Name; $this->render(); } public function paysLangueAction() { $idLangue=$this->_getParam('idlangue', 1); //Zend_Debug::dump($this->getRequest()->getParams()); $lt = new Language(); $languageRow = $lt->find($idLangue)->current(); $lesPays=$languageRow->getPaysParlantCetteLangue(); $this->view->lesPays=$lesPays; $this->view->nomLangue=$languageRow->Name; $this->render(); }
Les vues associes : //pays-langue.phtml

<table> <caption>Pays ayant comme langue parle: <?php echo $this->nomLangue ?></caption> <thead> <tr> <th>Pays</th> <th>Officielle</th> <th>Pourcentage</th> </tr> </thead> <tbody> <?php foreach ($this->lesPays as $pays) : ?> <tr> <td> <?php echo $pays->Name; ?> </td> <td> <?php echo $pays->IsOfficial; ?> </td> <td> <?php echo $pays->Percentage; ?> </td> </tr> <?php endforeach; ?> </tbody> </table> //langues.phtml table> <caption>Les langues parles du pays <?php echo $this->nomPays ?></caption> <thead> <tr> <th>Langue</th> <th>Officielle</th> <th>Pourcentage</th> <th>autres pays</th> </tr> </thead> <tbody> <?php foreach ($this->langues as $langue) : ?> <tr> <td> <?php echo $langue->Name; ?> </td> <td> <?php echo $langue->IsOfficial; ?> </td> <td> <?php echo $langue->Percentage; ?> </td> <td> <a href='<?php echo $this->url(array('controller'=>'country', 'action'=>'pays-langue', 'idlangue'=> $langue->idLanguage), null, true) ?>'>voir</a> </td> </tr> <?php endforeach; ?> </tbody> </table> Les mthodes de niveau Row

//LanguageRow.php <?php class LanguageRow extends Zend_Db_Table_Row_Abstract { // obtenir tous les pays parlant cette langue public function getPaysParlantCetteLangue() { return $this->findManyToManyRowset('Country', 'CountryLanguage'); } } //CountryRow.php public function getLanguesParlees() { return $this->findManyToManyRowset('Language', 'CountryLanguage'); }
17.5. Mthodes magiques Si vous avez respect les conventions de nommage utilises dans ce support, pour pouvez alors utilis er les fonctions "magiques" de navigation dans le modle. Une mthode magique est une mthodes non dclare par le dveloppeur, mais respectant des conventions de nommage de sorte que Zend_Db_Table_Row_Abstract puisse en dduire les bons appels. Par exemple

// CountryRow.php ... public function getVilles() { //return $this->findDependentRowset('City', 'MonPays'); return $this->findCity(); } ...
sera interprt : $this->findDependentRowset('City') Il en va de mme avec les mthodes de type findTableViaTableRelation quie seront traduites avec findManyToManyRowset : Exemple

// CountryRow.php public function getLanguesParlees() { //return $this->findManyToManyRowset('Language', 'CountryLanguage'); return $this->findLanguageViaCountryLanguage(); } ...
Plus d'information avec le guide du dveloppeur et l'API

18. Formulaire et Validateurs


Comme tout framework qui se repecte, ZF propose des composants ddies aux formulaires des applications Web. ZF propose l'API Zend_Form, associ Zend_Validate, Zend_Filter pour la cration et la gestion des formulaires (prsentation, validation, retour au client).

Nous allons permettre la modification d'une donne associe Country, savoir l'attribut HeadOfState . Nous mettons en place un lien de modification dans la vue en liste. Figure 19. http://localhost/~kpu/zf-tuto/country

Le lien 'modifier' pointe sur country/edit. Nous crons donc une mthode d'action associe.

public function editAction() { $this->view->title = "Modifier pays"; $form = new PaysForm(); // ... $this->view->form = $form; }
Un formulaire est un peu plus qu'une vue, il permet l'utilisateur de transmettre de multiples informations issues de listes droulantes, cases cocher, champs d'dition de texte... De plus, les donnes d'un formulaire doivent tre filtres (pour se prmunir d'actions dangereuses) et valides (pour assurer la cohrence des donnes), ceci pouvant entraner des aller/retour entre le serveur et le client. Ce type de comportement est pr-programm par ZF, et pour en hriter, nous devons faire ... hriter nos formulaires de Zend_Form. La classe PaysForm hrite de Zend_Form. Remarque : Cette classe de base prend en compte bien d'autres aspects, voir ici Zend_Form . Le formulaire PaysForm.php est placer dans /application/forms :

<?php class PaysForm extends Zend_Form { public function __construct($options = null) { parent::__construct($options); $this->setName('modifierpays'); $headstate = new Zend_Form_Element_Text('HeadOfState'); $headstate->setLabel('HeadOfState') ->setRequired(true) ->addValidator('NotEmpty'); $headstate->addValidator(new Zend_Validate_StringLength(3)); $id = new Zend_Form_Element_Hidden('id'); $submit = new Zend_Form_Element_Submit('submit'); $submit->setLabel('Modifier'); $this->addElements(array($headstate, $id, $submit)); } }
Quelques explications : Le constructeur se charge de la s tructure du formulaire, lment par lment. Des contrles HTML de type input='text' sont crs via Zend_Form_Element_Text Les champs de saisis requis sont spcifis ( required) A chacun des contrles d'entre sont associs filtres, validateurs et vue

A titre d'indication, on trouvera ici une liste des lments standards dfinis par ZF. Exemples : Button, Checkbox, Hidde, Image, MultiCheckbox, Multis elect, Password, Radio, Reset, Select, Submit, Text, Textarea et Zend_Form_Element_Hash pour augmenter la scurit. 18.1. Qu'est-ce qu'un filtre ? Un filtre est une fonction de transformation d'une donne d'entre, soumise par un client, en une donne conforme pour le systme : simplification, ajout de code d'chappement... Il existe de nombreux filtres prts l'emploi. Un filtre est un objet, instance d'une classe qui implmente Zend_Filter_Interface. Par exemple :

$headstate = new Zend_Form_Element_Text('HeadOfState'); $headstate->setLabel('HeadOfState'); $headstate->setRequired(true); $filter = new Zend_Filter_StripTags(); $headstate->addFilter($filter);


Le filtre StripTags supprimera toute balise prsente dans prnom (afin de se prmunir d'injection de code HTML). Les filtres peuvent tre chans :

... $filter = new Zend_Filter_StripTags(); $nom->addFilter($filter); $nom->addFilter(new Zend_Filter_StringToUpper()); ...


Dans ce cas, les filtres sont appliqus par ordre d'insertion dans le composant. Ici, les balises seront supprimes avant la passage en majuscule. Plus loin avec les filtres : Zend_Filter . 18.2. Qu'est-ce qu'un validateur ? Un validateur est une fonction boolenne qui reoit une donne d'entre, et retourne vrai si son argument est conforme aux rgles de gestion prises en charge par le validateur, retourne faux sinon. De plus, le validateur peut fournir des informations sur les rgles non respectes, comme une chane vide, ou trop courte ou ne respectant pas une certaine syntaxe, etc. Il existe de nombreux validateurs prts l'emploi. Un validateur est un objet, instance d'une class e qui implmente Zend_Validate_Interface. Par exemple :

$headstate = new Zend_Form_Element_Text('HeadOfState'); $headstate->setLabel('HeadOfState'); $headstate->setRequired(true); $headstate->addValidator(new Zend_Validate_NotEmpty());


Les validateurs peuvent tre chains :

... $headstate->addValidator(new Zend_Validate_StringLength(2)); $headstate->addValidator(new Zend_Validate_Alnum()); // ou $headstate->addValidator('Alnum'); // le framework se chargeant de la correspondance ...
Dans ce cas, les validations sont appliqus par ordre d'insertion dans le composant. Ici, le test de la longueur minimale est ralis en premier, puis, quelqu'en soit le rsultat le seconde test sera lui aussi valu (caractres alphanumriques uniquement). 18.3. Exemple de code d'exploitation du formulaire

$form = new PaysForm(); // instanciation du formulaire if ($this->_request->isGet()) { $id = $this->_getParam('id', -1); if ($id == -1) // rien faire ici return $this->_helper->redirector('index', 'country'); $ct=new Country(); $rowPays=$ct->find($id)->current(); if ($rowPays) { // initialisation du formulaire // Zend_Debug::dump($rowPays); $form->getElement('HeadOfState')->setValue($rowPays->HeadOfState); $form->getElement('id')->setValue($rowPays->id); } } // reception de donnees ? else if ($this->_request->isPost()) { $formData = $this->_request->getPost(); // nous les affectons au formulaire $form->populate($formData); // qui applique les filtres $formData=$form->getValues(); $ct=new Country(); $rowPays=$ct->find($formData['id'])->current(); // activation des validateurs if ($rowPays && $form->isValid($formData)) { // ok, nous pouvons oprer $rowPays->HeadOfState = $formData['HeadOfState']; // sauvegarde dans la BD $id=$rowPays->save();

// make a "redirect after post" // @see http://en.wikipedia.org/wiki/Post/Redirect/Get return $this->_helper->redirector('index', 'country'); } } // prsentation du formulaire if ($rowPays) { $this->view->action = "Modifier le nom du chef d'tat"; $this->view->pays = $rowPays->Name; $this->view->form = $form; $this->render(); } else { //sinon, rien faire ici ! return $this->_helper->redirector('index', 'country'); } }

Comme on peut le constater, en cas de non validation ou de requte non POST, le formulaire est retourn au client. Ci-dessous un exemple de retour avec informations transmises par les diffrents validateurs. Figure 20. http://localhost/~kpu/zf-tuto/country/edit/id/73

19. Quelques modules (guide, tutoriel) explorer, dans la continuit de ce tutoriel.


19.1. Gestion des droits utilisateur rapprocher avec la notion UML d'acteur/cas d'utilisation Zend Framework est livr, entre autres, avec les modules permettant le gestion des authentifications (Zend_Auth) et des droits (Zend_Acl). Vous trouverez ici un tutoriel Rob Allen sur la question, traduit en franais. 19.2. Gestion du suivi de sessions Le module Zend_Session propose une alternative l'utilisation brute du tableau $_SESSION. En effet, Zend_Session intgre la notion d'"espace de noms" dans la zone de stockage des donnes de session, limitant ainsi des conflits de noms. Voir Guide du programmeur : Zend_Session 19.3. Structuration de blocs de prsentation Le module Zend_Layout permet d'encapsuler le contenu d'une vue dans une autre, bien que pouvant tre utilis sans MVC, le module est conu pour s'intgrer avec ce dernier, utilise Zend_View. Voir Guide du programmeur : Zend_Layout

20. Conclusion
Esprons que ce document vous ait permis d'apprcier le fort potentiel d'un framework en application Web : productivit, robustesse, extensibilit, maintenabilit, scurit, ... Mme si ces qualits n'ont t qu'effleures. Vous pouvez maintenant vous intresser au quickstart en ligne, vous y dcouvrirez une utilisation plus avance du modle, et une construction plus structure des vues car ces dernires (dans ce tuto) ce ne sont que des blocs de doc HTML. Vous trouverez ici une implmentation simple de ce tuto, avec un dbut d'intgration d'un layout : tuto.zip ALL IN ONE (5.5Mo) - la base de donnes est installer puis les valeurs database.params.xxx de application.ini. devront tre renseignes en consquence.
Le quick start de ZF, chez Zend.. Le guide du dveloppeur, de trs bonne qualit et mis jour rgulirement.

Site officiel : zend f rance