UPMC IRT c 2004-2010 by C.Queinnec 3/42 UPMC IRT c 2004-2010 by C.Queinnec 4/42 Symfony Revision: 1.1 IRT 2009-2010 Programmation côté serveur Symfony C.Queinnec Symfony est un framework, une architecture pour projet d applications web MVC : Model View Control Convention over configuration KISS : Keep It Simple, Stupid! DRY : Don t Repeat Yourself! né en 2005 en France (Fabien Potencier, Sensio) http ://www.symfony-project.org/ UPMC IRT c 2004-2010 by C.Queinnec 1/42 UPMC IRT c 2004-2010 by C.Queinnec 2/42 Rappels Programmation par objets : définir classe, héritage instancier classe, accéder/modifier champs méthode, référence à soi, à super class A extends B { private champ1 = 0; public function method1 ($v) { $this->champ1 = $v + $this->champ1; protected function method2 (type $w) { return new B($w);
UPMC IRT c 2004-2010 by C.Queinnec 7/42 UPMC IRT c 2004-2010 by C.Queinnec 8/42 Projet Applications Au début est le projet un outil en ligne de commande automatise de nombreuses tâches Un projet comporte plusieurs applications par exemple, frontend, backend... % symfony generate:app frontend % alias symfony=/lib/vendor/symfony-1.4.3/data/bin/symfony % ls -F apps/frontend/ % cd ~/public_html/ config/ i18n/ lib/ modules/ templates/ % symfony help % ls apps/frontend/config/ % symfony help generate:project app.yml frontendconfiguration.class.php % symfony generate:project pcs cache.yml routing.yml % ls -F pcs/ factories.yml security.yml apps/ config/ lib/ plugins/ test/ filters.yml view.yml cache/ data/ log/ symfony/ web/ % ls apps/frontend/templates/ layout.php UPMC IRT c 2004-2010 by C.Queinnec 5/42 UPMC IRT c 2004-2010 by C.Queinnec 6/42 Syntaxe YAML et cela fonctionne! http://localhost/pcs/web/frontend.php http://localhost/pcs/web/frontend_dev.php L application peut être tourné en mode production, développement, test... Voir la barre d outils en mode développement. Attention : aucune tabulation. Attention aux alignements. hash: cle1: valeur1 cle2: - item1 - item2 cle3: { cle3a: vala, cle3b: valb # Commentaire nom: valeur Et plein d autres choses encore...
UPMC IRT c 2004-2010 by C.Queinnec 11/42 UPMC IRT c 2004-2010 by C.Queinnec 12/42 Modèle de données Indiquer les données de connexion dans config/databases.yml ainsi que l ORM (Object Relational Mapping) à utiliser : ici Doctrine. all: doctrine: class: sfdoctrinedatabase param: dsn: mysql:host=localhost;dbname=pcs username: X # password: Y Base de données La mémoire commune, l état de l application, le lieu d échange, le principe synchronisant des applications Web! corollaire de l architecture à couches (3-tiers) Présentation Métier Présentation Présentation Présentation Métier Métier Métier BD BD +réplication UPMC IRT c 2004-2010 by C.Queinnec 9/42 Concepts de BD UPMC IRT c 2004-2010 by C.Queinnec 10/42 Usage de BD table, clés primaires/étrangères (intégrité référentielle) langage ensembliste : SQL transactions index, vue, réflexe (trigger),... Types de données : classique + temps + grandes séquences d octets ou de caractères (accès par flux) Interfaçage : ODBC, JDBC principalement via des chaînes de requêtes SQL établissement de la connexion (via un DSN data source name) à une base puis, un certain nombre de fois : établissement (implicite ou pas) d un mode transactionnel puis, un certain nombre de fois : préparation de requête appel de requête (type select) récupération des données sélectionnées (gestion de curseur) ou exécution d une requête à effet secondaire (update, delete, etc.) ou d administration (create temporary table, create index, etc.) validation ou pas de la transaction terminaison de connexion
UPMC IRT c 2004-2010 by C.Queinnec 15/42 UPMC IRT c 2004-2010 by C.Queinnec 16/42 Relationnel/Objet Première idée : une ligne de table = un objet table T int a text b 42 foo timestamp c 2008feb04 09:51 T a b c 42 StringBuffer foo Date 2008 feb 04 09 51 00 Lien entre ligne et objet La clé primaire est le lien Elle doit pouvoir être calculée à partir de l objet et de la ligne Elle permet d assurer l unicité de l objet remonté de la base table T int a text b 42 foo timestamp c 2008feb04 09:51 bd, T, CleP(a,b,c) H T a b c 42 StringBuffer foo Date 2008 feb 04 09 51 00 UPMC IRT c 2004-2010 by C.Queinnec 13/42 Modifications UPMC IRT c 2004-2010 by C.Queinnec 14/42 Traitement des références Modifier l objet doit se répercuter dans la base. Pour cela, il faut traquer les modifications en imposant l usage de modifieurs. En fin de transaction (ou à des moments choisis), collecter les objets modifiés et engendrer la série de requêtes : unt->setfield(newvalue) update T set field = newvalue,... where... cleprimaire()...; Un impératif : chargement paresseux des lignes! Traquer les lectures de clés étrangères pour en profiter pour charger la ligne référencée (mêmes techniques que précédemment) Un chargement demande un select un peu plus de doigté pour les mises à jour dans la modification de la référence (seulement sur des objets remontés (chargés ou non)) dans la génération des requêtes de mise à jour update T set field = referredobject.primarykey,... where... cleprimaire()...;
UPMC IRT c 2004-2010 by C.Queinnec 19/42 UPMC IRT c 2004-2010 by C.Queinnec 20/42 Collections table T1... ref T2 table T2... foo... text e bd, T1, clep bd, T2, foo... foo H X X T1_DB loaded bd T1 CleP... T2_DB??? bd T2 foo?????? Certaines clés étrangères mènent à un ensemble de lignes matérialisant ainsi une relation 1 :n. create table CD { id int primary key, title text not null create table Track { id int primary key, cd_id int not null references CD, title text not null Un CD a un contenu : les pistes qui le mentionnent comme CD. On veut écrire uncd.tracks() et obtenir l ensemble des objets Track qu il comporte. UPMC IRT c 2004-2010 by C.Queinnec 17/42 Opérations usuelles UPMC IRT c 2004-2010 by C.Queinnec 18/42 Convention/configuration Création fabrique A.create(...) sélection unaire A.find(clePrimaire) sélection multiple A.select(criteres) criteres définit la clause where et est souvent un objet structuré Usage lecture una.getfield() mène à valeur ou objet modification una.setfield(...) mise à jour de la base unaire una.update() générale update() Suppression unaire una.delete() multiple A.delete(criteres) Un slogan assez populaire en ce moment (Maven, Rails, etc.) est «suivre les conventions est préférable à configurer» (convention over configuration) ce qui implique qu une configuration par défaut doit exister et être raisonnable. unifier noms de classe et noms de table (caractères permis, mots clés exclus) unifier noms de champs et noms de colonnes choisir des types compatibles systématiquement passer par des méthodes Mais on peut toujours tout configurer!
UPMC IRT c 2004-2010 by C.Queinnec 23/42 UPMC IRT c 2004-2010 by C.Queinnec 24/42 Méthodes de configuration CRUD base = descripteur = code descripteur = code + base code + annotation = base (dilution descripteur en code) Le descripteur est le pivot. concept A champ type a nom SQL a nom (Java Perl PHP) a cle primaire... nom table SQL A nom classe (Java Perl PHP) A raccourci possible C c relation 1:n nom D d CRUD = Create, Retrieve, Update, Delete À partir du descripteur, on peut bâtir une webapp entière pour administrer une base (exemple phpmyadmin). On peut adjoindre des informations stylistiques. On peut aussi utiliser les seules méta-données (moins riches) de la base (ce que fait par exemple MayPole) UPMC IRT c 2004-2010 by C.Queinnec 21/42 UPMC IRT c 2004-2010 by C.Queinnec 22/42 Harmonisation Limitations mise à jour procédurale Comment marier du code engendré automatiquement avec du code métier ou de la présentation? Sachant que la base évolue, que le code métier se ramifie, que la vitesse de présentation peut devenir critique, que la vitesse de re-déploiement peut devenir dirimante,... update Person set age = age + 1 where birth = current_day; remontée conjointe de données de plusieurs tables select p.*, m.* from Person p, Mark m where p.id = m.person_id; persistance structurelle par classe/table et non par atteignabilité
UPMC IRT c 2004-2010 by C.Queinnec 27/42 UPMC IRT c 2004-2010 by C.Queinnec 28/42 Autres problèmes Manipulation de multiples bases transaction multi-bases Qualité des données le DSN identifiant la base correspond à un unique utilisateur de la base. pas de transactions longues avec le Web UPMC IRT c 2004-2010 by C.Queinnec 25/42 Mise en œuvre % symfony doctrine:build-model % find /lib/model/doctrine/ % symfony doctrine:build-sql % more data/sql/schema.sql % symfony doctrine:insert-sql % echo show tables; describe person; mysql pcs % symfony doctrine:build-forms % symfony doctrine:build-filters Définition de la base Dans config/doctrine/schema.yml Person: columns: name: { type: string(255), notnull: true comment: { type: string(255), notnull: false password: { type: string(255), notnull: true last: { type: timestamp, notnull: false relations: LoadAverage: local: last foreign: instant LoadAverage: columns: instant: { type: timestamp, notnull: true users: { type: integer, notnull: true load1: { type: float, notnull: true load5: { type: float, notnull: true load15: { type: float, notnull: true UPMC IRT c 2004-2010 by C.Queinnec 26/42 Remplissage initial base Plutôt qu écrire en Sql, cf. data/fixtures/fixtures.yml LoadAverage: genese: instant: 2010-01-01T00:00:00Z users: 0 load1: 0 load5: 0 load15: 0 Person: LouisXX: name: Louis XX comment: prochain roi de France? password: bof last: genese CneNemo: name: Capitaine Nemo comment: sous-marinier de papier password: gloup last: genese % symfony doctrine:data-load % echo select * from person; mysql pcs
Requêtes Doctrine::getTable( Person ) ->createquery() ->where("name =?", "qqch") ->execute(); // select * from Person where name = "qqch" Manipulations $person->setname($person->getname()); $person->delete(); Lire les fichiers dans lib/model/doctrine/ Doctrine::getTable( Person ) ->find(id); // select * from Person where id =? UPMC IRT c 2004-2010 by C.Queinnec 29/42 Modules Une application est structurée en modules : un regroupement de pages et d actions % symfony generate:module frontend person % symfony generate:module frontend loadaverage % ls apps/frontend/modules/person/actions/ actions.class.php % ls apps/frontend/modules/person/templates/ indexsuccess.php Voir avec http://localhost/pcs/web/frontend_dev.php/person http://localhost/pcs/web/frontend_dev.php/xxx UPMC IRT c 2004-2010 by C.Queinnec 31/42 UPMC IRT c 2004-2010 by C.Queinnec 30/42 MVC Toute requête arrive au contrôleur général de Symfony qui analyse l URL pour la router vers une action d un module selon des règles apps/a/config/routing.yml L action (incarnée par une méthode executex dans le fichier apps/a/modules/m/actions/actions.class.php) renvoie un code permettant d en déduire la vue (le patron) à utiliser ainsi que les données utiles à la génération de la vue La vue (incarnée par un fichier apps/frontend/modules/m/templates/xsuccess.php) engendre l HTML qui sera inclus dans une page HTML définie par apps/frontend/templates/layout.php UPMC IRT c 2004-2010 by C.Queinnec 32/42
UPMC IRT c 2004-2010 by C.Queinnec 35/42 UPMC IRT c 2004-2010 by C.Queinnec 36/42 Routage homepage: url: / # param: { module: default, action: index param: { module: person, action: index person_show: url: /person/:id class: sfdoctrineroute options: { model: Person, type: object param: { module: person, action: show requirements: id: \d+ sf_method: [get] default_index: url: /:module param: { action: index default: url: /:module/:action/* UPMC IRT c 2004-2010 by C.Queinnec 33/42 Vue Actions public function executeindex(sfwebrequest $request) { // $this->forward( default, module ); $this->persons = Doctrine::getTable( Person ) ->createquery() ->execute(); return sfview::success; UPMC IRT c 2004-2010 by C.Queinnec 34/42 Page <table> <thead> <tr> <th>nom</th> <th>commentaire</th> </tr> </thead> <tbody> <?php foreach ($persons as $person):?> <tr><td><a href= <?php echo url_for( person_show, $person) </head>?> > <?php echo $person->getname()?></a></td> <td><?php echo $person->getcomment()?></td> </tr> <?php endforeach;?> </tbody> </table> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " g/tr/xhtml1/dtd/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="e <head> <?php include_http_metas()?> <?php include_metas()?> <?php include_title()?> <link rel="shortcut icon" href="/favicon.ico" /> <?php include_stylesheets()?> <?php include_javascripts()?> <body> <div><a href= <?php echo url_for( @homepage )?> >Home</a> </div> <h1>bonjour Symfony</h1> <?php echo $sf_content?> </body> </html>
UPMC IRT c 2004-2010 by C.Queinnec 39/42 UPMC IRT c 2004-2010 by C.Queinnec 40/42 Fonctionnalités avancées // dans indexsuccess.php <?php echo link_to( [DELETE], person_delete, $person, array( method => delete, confirm => Vraiment? ))?> // dans routing.yml person_delete: url: /person/:id class: sfdoctrineroute options: { model: Person, type: object param: { module: person, action: delete requirements: id: \d+ sf_method: [delete] // dans actions.class.php public function executedelete(sfwebrequest $request) { $this->person = $this->getroute()->getobject(); $this->forward404unless($this->person); $this->person->delete(); $this->forward( person, index ); // module, action UPMC IRT c 2004-2010 by C.Queinnec 37/42 Pagination Configurations Configurer = paraméter statiquement afin de ne pas changer le code. config/ apps/frontend/config/ app.yml # apps/frontend/config/app.yml all: items_per_page: 10 dev: blabla: true Récupération des paramètres avec sfconfig::get( app_items_per_page ) UPMC IRT c 2004-2010 by C.Queinnec 38/42 Formulaires pour création public function executeindex(sfwebrequest $request) { $this->pager = new sfdoctrinepager( LoadAverage, sfconfig::get( app_items_per_page )); $this->pager->setquery(doctrine::gettable( LoadAverage ) ->createquery()); $this->pager->setpage($request->getparameter( page, 1)); $this->pager->init(); Les méthodes sur un sfdoctrinepager : getresults() getnbresults() havetopaginate() getpage() getpreviouspage() getnextpage() getlastpage() public function executenew(sfwebrequest $request) { // Sert le formulaire $this->form = new XyzForm(); public function executecreate(sfwebrequest $request) { // Cree l objet en base $this->form = new XyzForm(); $this->processform($request, $this->form); $this->settemplate( new ); protected function processform( sfwebrequest $request, sfform $form) { $form->bind($request->getparameter($form->getname())); if ( $form->isvalid() ) { $xyz = $form->save(); $this->redirect($this->generateurl(xyz_show, $xyz));
Formulaires pour modification public function executeedit(sfwebrequest $request) { // Sert le formulaire $this->xyz = $this->getroute()->getobject(); $this->form = new XyzForm($this->xyz); public function executeupdate(sfwebrequest $request) { // Modifie l objet en base $xyz = $this->getroute()->getobject(); $this->form = new XyzForm($xyz); $this->processform($request, $this->form); $this->template( edit ); Conclusions Un excellent cadre de développement! Livre «Symfony : Mieux développer en PHP avec Symfony 1.2 et Doctrine», Fabien Potencier, Hugo Hamon, les Cahiers du Programmeur, Eyrolles, 2009. UPMC IRT c 2004-2010 by C.Queinnec 41/42 UPMC IRT c 2004-2010 by C.Queinnec 42/42