The More with symfony book | symfony | Web - Formulaire

Transcription

The More with symfony book | symfony | Web - Formulaire
The More with symfony book | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
About
French (fr)
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
"The More with symfony book" is part of the official symfony documentation.
Introduction
Techniques Avancées de Routage
Accroître la Productivité
Les Emails
Widgets et Validateurs Personnalisés
Formulaires Avancés
Etendre la Web Debug Toolbar
Techniques Avancées avec Doctrine
Tirer Profit de l'Héritage de Table avec Doctrine
Plonger dans les Entrailles de Symfony
Windows et symfony
Développer pour Facebook
Tirer Profit de la Ligne de Commande
Manipuler le Cache de Configuration de symfony
Travailler avec la Communauté symfony
Appendix B - License
Annexe
A
Code
JavaScript
du
Widget
sfWidgetFormGMapAddress
Annexe A - Exemple de Script d'Installation
Personnalisé
A propos des Auteurs
A Propos des Traducteurs
Be trained by symfony experts
Jan 24: Paris
Support symfony!
Buy this book
or donate.
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/[31/12/2010 02:33:32]
The More with symfony book | Introduction | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Introduction
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Fabien Potencier
A l'heure où ces lignes sont écrites, le projet symfony a célébré une
étape significative : son quatrième anniversaire. En seulement quatre
ans, le framework symfony a grandi pour devenir l'un des frameworks
PHP les plus populaires dans le monde et sur lequel s'appuient des sites
à fort trafic comme Delicious, Yahoo Bookmarks et Daily Motion.
Néanmoins, c'est la sortie récente de symfony 1.4 en novembre 2009
qui marque la fin d'un cycle. Cet ouvrage est le meilleur moyen de
terminer ce cycle, et de ce fait, vous vous apprêtez à lire le tout dernier
livre officiel de la branche 1.x de symfony, publié par l'équipe du projet
symfony. Le prochain livre à paraître se focalisera quant à lui autour de
Symfony 2.0, et sera dévoilé à la fin de l'année 2010.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Pour cette raison, et bien d'autres que j'expliquerai dans cette
introduction, ce livre est un peu spécial pour nous tous.
Pourquoi avoir écrit un nouvel ouvrage ?
Nous avons récemment publié deux autres livres sur symfony 1.3 et 1.4: "Practical symfony" et
"The symfony reference guide". Le livre pratique est un excellent moyen de démarrer
l'apprentissage de symfony dans la mesure où vous apprenez toutes les bases du framework à
travers le développement d'un projet réel, étape par étape. Le dernier est un guide de référence
qui agrège la plupart des informations de configuration dont vous pourriez avoir besoin au
quotidien pour vos développements.
"Plus loin avec symfony" est un ouvrage à propos de sujets plus avancés de symfony. Cet
ouvrage n'est pas le tout premier que vous devriez lire à propos de symfony, mais il s'avèrera
être une aide ultime pour tous ceux qui ont déjà fait leurs preuves sur plusieurs petits projets
réalisés avec le framework. Si vous avez déjà voulu savoir comment symfony fonctionne à
l'intérieur, ou bien si vous souhaitez étendre le framework de diverses manières en vue de le
faire fonctionner pour des besoins spécifiques, alors ce livre est fait pour vous. Dans ce cas
précis, "More with symfony" est tout ce dont il faut savoir pour faire évoluer vos compétences
symfony au niveau supérieur.
Comme cet ouvrage est un recueil de tutoriels à propos de sujets divers, n'hésitez pas à lire les
chapitres qui vont suivre dans l'ordre que vous souhaitez, en vous basant sur ce que vous êtes
entrain d'essayer d'accomplir avec le framework.
A propos de ce livre
Cet ouvrage est un peu spécial parce qu'il s'agit d'un livre écrit par la communauté pour la
communauté. Plus d'une douzaine de personnes ont contribué à cet ouvrage : des auteurs, aux
traducteurs, en passant par les relecteurs, c'est en réalité un incroyable concentré d'efforts qui a
été mis en oeuvre pour parvenir à ce livre.
Ce livre a été publié simultanément dans pas moins de cinq langues (anglais, français, italien,
espagnol et japonais). Tout cela n'aurait été possible sans le courageux travail bénévole des
équipes de traduction.
Cet ouvrage a été rendu possible grâce à l'esprit Open-Source et c'est aussi pourquoi il est
publié sous une licence Open-Source. Ce point à lui seul change tout, car il signifie que personne
n'a été payé pour travailler sur ce recueil : tous les contributeurs ont travaillé dur pour y
parvenir et c'est surtout parce qu'ils voulaient tous y arriver.
Au cours de cette aventure, chaque contributeur a souhaité à la fois partager ses connaissances,
contribuer en retour à la communauté, participer à l'évangélisation de symfony et, bien sûr, de
prendre du plaisir et devenir célèbre.
"Plus loin avec symfony" a été écrit par dix auteurs qui utilisent symfony au quotidien en poste
de développeurs ou de chefs de projets. Ces personnes ont toutes une connaissance
http://www.symfony-project.org/more-with-symfony/1_4/fr/01-Introduction[31/12/2010 02:33:36]
Chapter Content
Pourquoi avoir écrit un nouvel
ouvrage ?
A propos de ce livre
Remerciements
Avant de commencer
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
The More with symfony book | Introduction | symfony | Web PHP Framework
particulièrement étendue du framework, et ont souhaité partager leur savoir et leur expérience
dans ces chapitres.
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
Remerciements
and more...
Lorsque j'ai commencé à réfléchir comment écrire un nouveau livre à propos de symfony en
août 2009, j'ai immédiatement eu une idée folle : pourquoi ne pas écrire un livre en deux mois
seulement et le publier simultanément en cinq langues !
Il va de soi qu'impliquer la communauté dans un projet d'une telle ampleur fut presque
obligatoire. J'ai commencé par parler de cette idée au Japon pendant la conférence PHP, et en
quelques heures seulement, l'équipe de traduction japonaise était déjà prête à travailler. C'était
incroyable ! La réponse des auteurs et des traducteurs était elle aussi très encourageante, et
peu de temps après, "Plus loin avec symfony" était né.
Je souhaite remercie toutes les personnes qui ont participé d'une manière ou d'une autre à la
création de cet ouvrage, et notamment, sans ordre particulier, les personnes ci-dessous :
Ryan Weaver, Geoffrey Bachelet, Hugo Hamon, Jonathan Wage, Thomas Rabaix, Fabrice
Bernhard, Kris Wallsmith, Stefan Koopmanschap, Laurent Bonnet, Julien Madelin, Franck Bodiot,
Javier Eguiluz, Nicolas Ricci, Fabrizio Pucci, Francesco Fullone, Massimiliano Arione, Daniel
Londero, Xavier Briand, Guillaume Bretou, Akky Akimoto, Hidenori Goto, Hideki Suzuki, Katsuhiro
Ogawa, Kousuke Ebihara, Masaki Kagaya, Masao Maeda, Shin Ohno, Tomohiro Mitsumune, et
Yoshihiro Takahara.
Avant de commencer
Ce livre a été écrit pour les versions 1.3 et 1.4 de symfony. Comme écrire un livre unique pour
deux versions différentes d'un logiciel est une tâche peu commune, cette section explique
quelles sont les principales différences entre les deux versions, et comment faire le meilleur
choix pour vos projets.
Les versions 1.3 et 1.4 de symfony ont toutes les deux été publiées à peu près au même
moment (à la fin de l'année 2009). Par conséquent, elles ont toutes les deux le même jeu de
fonctionnalités. La seule différence qui les oppose concerne le support de la rétrocompatibilité avec les versions plus anciennes de symfony.
Symfony 1.3 est la distribution que vous souhaiterez utiliser si vous avez besoin de migrer un
projet ancien qui repose sur une version antérieure de symfony (1.0, 1.1 ou 1.2). Cette version
intègre une couche de compatibilité rétrograde et toutes les fonctionnalités qui ont été rendues
obsolètes pendant le cycle de développement de la branche 1.3 restent disponibles. Par
conséquent, cela signifie aussi que mettre à jour un projet est facile, simple et sûr.
En revanche, si vous démarrez un nouveau projet aujourd'hui, vous devriez utiliser directement
symfony 1.4. Cette version a exactement le même jeu de fonctionnalités que symfony 1.3 à la
différence que les fonctionnalités obsolètes, y compris la couche entière de compatibilité
rétrograde, ont été supprimées.
Cette version est donc plus saine et aussi légèrement plus rapide que symfony 1.3. Un autre
avantage non négligeable d'utiliser symfony 1.4 est son support à plus long terme. En devenant
une version supportée à long terme (LTS - Long Time Support), symfony 1.4 sera maintenue
par l'équipe de développement pendant trois ans, jusqu'en novembre 2012.
Bien évidemment, vous pouvez migrer vos projets vers symfony 1.3 et puis mettre à jour votre
code petit à petit afin de supprimer les fonctionnalités obsolètes, et éventuellement poursuivre la
migration vers symfony 1.4 pour bénéficier de son support à long terme. Vous avez encore tout
le temps de planifier votre migration vers symfony 1.3 puisque cette dernière sera supportée
pendant un an, jusqu'en novembre 2010.
Enfin, comme cet ouvrage ne présente aucune fonctionnalité obsolète, tous les exemples que
vous découvrirez fonctionneront exactement de la même manière sur les deux versions.
Techniques Avancées de Routage »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
http://www.symfony-project.org/more-with-symfony/1_4/fr/01-Introduction[31/12/2010 02:33:36]
Search
powered by google
The More with symfony book | Introduction | symfony | Web PHP Framework
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/01-Introduction[31/12/2010 02:33:36]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Techniques Avancées de Routage
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Ryan Weaver
En son noyau, le framework de routage est en réalité une carte qui relie
chaque URL à une destination spécifique à l'intérieur d'un projet
symfony et vice versa. Cet outil permet de créer des URLs propres et
élégantes, et qui restent complètement indépendantes de la logique
applicative. Ces améliorations ont été réalisées depuis les versions
récentes de symfony, et le framework de routing continue encore d'aller
plus loin aujourd'hui.
Ce chapitre décrira comment créer une application web simple pour
laquelle chaque client utilise un sous-domaine séparé, par exemple
client1.mydomain.com et client2.mydomain.com. C'est en étendant le
framework de routage que cette tâche devient très facile à réaliser.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Ce chapitre nécessite l'usage de Doctrine en guise de couche d'ORM pour le projet symfony.
Initialisation du Projet : Un CMS pour Plusieurs Clients
Dans ce chapitre, une société fictive - Sympal Builder - désire mettre en place un CMS
permettant à ses clients de construire leur propre site web et l'héberger sur un sous-domaine de
sympalbuilder.com. L'objectif consiste ainsi à rendre le site grand public accessible à l'adresse
xxx.sympalbuilder.com pour le client XXX, et l'interface d'administration correspondante à
l'adresse xxx.sympalbuilder.com/backend.php.
Le nom Sympal est emprunté du nom de l'application Sympal développée par Jonathan Wage.
Sympal est un framework de gestion de contenu bâti sur un socle technique symfony et
Doctrine.
Ce projet dispose de deux besoins fonctionnels basiques et obligatoires :
Les utilisateurs doivent être capables de créer des pages web et spécifier pour chacune
d'entre elles un titre, un contenu et une URL correspondante.
L'application entière doit être construite à l'intérieur d'un seul projet
symfony qui gère à la fois les applications frontend et backend de tous les clients du
site. La détermination du client est basée sur le sous-domaine demandé et afin de
permettre le chargement des bonnes informations dans l'application.
Pour créer cette application, le serveur web aura besoin de rediriger toutes les requêtes des
sous-domaines *.sympalbuilder.com vers le même document racine, le répertoire web du
projet symfony.
Chapter Content
Initialisation du Projet : Un CMS
pour Plusieurs Clients
Le Modèle de Données et les Données
Le Modèle de Données et les Données
La base de données du projet se compose de deux tables Client et Page. Chaque Client est
représenté par un site (et donc un sous-domaine) qui contient plusieurs objets de type Page.
# config/doctrine/schema.yml
Client:
columns:
name:
string(255)
subdomain: string(50)
indexes:
subdomain_index:
fields:
[subdomain]
type:
unique
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
Le Routage
Mécanismes Internes du
Framework de Routage
Le Gestionnaire de Configuration du
Cache du Routage
Faire Correspondre une Requête
Entrante à une Route Spécifique
Créer une Classe de Route
Personnalisée
Ajouter de la Logique à la Route
Personnalisée
Profiter de la Route Personnalisée
Générer la Bonne Route
Les Collections de Routes
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
Page:
columns:
title:
string(255)
slug:
string(255)
content:
clob
client_id: integer
relations:
Client:
alias:
Client
foreignAlias: Pages
onDelete:
CASCADE
indexes:
slug_index:
fields:
[slug, client_id]
type:
unique
Bien que les indexes sur chaque table ne soient pas nécessaires, il est tout de même
important de les définir dans la mesure où la base de données sera fréquemment interrogée
sur ces colonnes.
Remplacer les Routes par une
Collection de Routes
Créer une Classe Personnalisée de
Collection de Routes
La Pièce Manquante : Créer une
Nouvelle Page
Personnaliser la Collection de
Routes d'Objet
Options d'une Collection de Routes
Les Routes d'Actions
Changer la Colonne Discriminante
Modifier les Méthodes du Modèle
Modifier les Paramètres par Défaut
Conclusion
Be trained by symfony experts
Jan 24: Paris
Feb 21: Paris
Pour commencer à donner naissance au projet, il est nécessaire d'avoir dès le départ quelques
jeux de données de test dans le fichier data/fixtures/fixtures.yml :
# data/fixtures/fixtures.yml
Client:
client_pete:
name:
Pete's Pet Shop
subdomain: pete
client_pub:
name:
City Pub and Grill
subdomain: citypub
Page:
page_pete_location_hours:
title:
Location and Hours | Pete's Pet Shop
content:
We're open Mon - Sat, 8 am - 7pm
slug:
location
Client:
client_pete
page_pub_menu:
title:
City Pub And Grill | Menu
content:
Our menu consists of fish, Steak, salads, and more.
slug:
menu
Client:
client_pub
Ce jeu de données de test déclare initialement deux sites web constitués chacun d'une seule
page. L'url complète vers chaque page est définie à l'aide des deux colonnes subdomain et slug
de l'objet de modèle Page.
http://pete.sympalbuilder.com/location
http://citypub.sympalbuilder.com/menu
Le Routage
Chaque page d'un site Sympal Builder correspond directement à un objet de modèle Page pour
lequel sont définis un titre et un contenu à afficher. L'étape suivante consiste à relier une URL à
un objet Page de l'application. Pour ce faire, il suffit de créer une route d'objet de type
sfDoctrineRoute qui s'appuie sur la colonne slug. Ainsi, le code suivant recherchera
automatiquement un objet Page dans la base de données, pour lequel la valeur du champ slug
correspond à celui transmis dans l'url :
# apps/frontend/config/routing.yml
page_show:
url:
/:slug
class:
sfDoctrineRoute
options:
model:
Page
type:
object
params:
module:
page
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
(Maîtrise de & Doctrine
- Français)
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
action:
show
La route ci-dessus correspondra automatiquement à l'url
http://pete.sympalbuilder.com/location ainsi qu'à son objet Page associé.
Malheureusement, cette route correspondra aussi à l'url
http://pete.sympalbuilder.com/menu, ce qui signifie que le menu du restaurant sera affiché
sur le site de Pete ! Par conséquent, la route est encore incapable de faire la différence entre les
sous-domaines des différents clients.
Pour concrétiser cette application, la route a besoin d'être plus intelligente. Elle devrait en effet
correspondre à une Page en se basant à la fois sur la valeur de la colonne slug et sur celle de
la colonne client_id. En d'autres termes, il s'agit de faire correspondre l'hôte (par exemple
pete.sympalbuilder.com) avec la colonne subdomain du modèle Client. Pour y parvenir, il
suffit d'étendre le framework de routage en créant une classe personnalisée de route.
Néanmoins, avant de démarrer, il convient de rappeler quelques principes de base au sujet du
fonctionnement du système de routage de symfony.
Mécanismes Internes du Framework de Routage
Dans symfony, une route est un objet de type sfRoute qui dispose de deux rôles importants :
Générer une URL : par exemple, si un paramètre slug est passé à une règle
page_show, alors la route doit être capable de générer une véritable URL correspondante
(/location par exemple).
Reconnaître une URL entrante : en lui fournissant l'url d'une requête entrante, chaque
route doit être capable de déterminer si l'URL correspond aux contraintes de la route.
Les informations de chaque route individuelle sont généralement déclarées à l'intérieur de
chaque répertoire config/ d'une application, et plus précisément dans le fichier
app/yourappname/config/routing.yml. Il est important de se rappeler que chaque route est
un objet de type sfRoute. Par conséquent, comment ces quelques données YAML simples
deviennent de véritables objets sfRoute ?
Le Gestionnaire de Configuration du Cache du Routage
En dépit du fait que la plupart des routes sont définies dans un fichier YAML, chaque entrée de
ce fichier est en réalité transformée en un objet au traitement de la requête.
Cette transformation est assurée à l'aide d'une classe spéciale plus communément intitulée
gestionnaire de configuration de cache. A l'issue de la conversion, il en résulte du code PHP
représentant toutes les routes de l'application. Bien que les spécificités de ce mécanisme
dépassent le périmètre de ce chapitre, il n'en demeure pas moins intéressant de tirer quelques
informations de la version compilée de la route page_show.
Le code compilé se trouve en effet dans le fichier
cache/yourappname/envname/config/config_routing.yml.php pour chaque application et
pour chaque environnement. Le listing ci-dessous est une version simplifiée de ce à quoi
ressemble la route page_show :
new sfDoctrineRoute('/:slug', array (
'module' => 'page',
'action' => 'show',
), array (
'slug' => '[^/\\.]+',
), array (
'model' => 'Page',
'type' => 'object',
));
Le nom de la classe de chaque route est défini grâce à la clé class du fichier routing.yml.
Si aucune clé class n'est spécifiée, la route par défaut sera issue de la classe sfRoute. Une
autre classe spécifique plus commune est sfRequestRoute qui offre au développeur la
possibilité de créer des routes dites RESTful. Une liste complète des classes de route et des
options disponibles existe dans le guide de référence de symfony.
Faire Correspondre une Requête Entrante à une Route Spécifique
L'un des principaux rôle du framework de routage consiste à faire correspondre chaque URL
entrante avec le bon objet route. C'est la classe sfPatternRouting qui représente le moteur du
noyau du routage et qui est dédiée à cette tâche. En dépit de son importance, un développeur
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
n'interagira que très rarement avec sfPatternRouting.
Pour faire correspondre la bonne route, la classe sfPatternRouting itère sur chaque objet
sfRoute et demande à la route si elle répond à l'url entrante. Intérieurement, cela signifie que
sfPatternRouting appelle la méthode sfMethod::matchesUrl() sur chaque objet route. Cette
méthode retourne simplement false si la route ne correspond pas à l'url entrante.
A contrario, si la route répond parfaitement à l'URL entrante, la méthode
sfRoute::matchesUrl() ne renverra pas seulement la valeur true. Bien au contraire, la route
retourne un tableau des paramètres qui sont ensuite fusionnés à l'intérieur de l'objet
représentant la requête. Par exemple, l'url http://pete.sympalbuilder.com/location répond
à la route page_show, dont la méthode matchesUrl() retourne alors le tableau suivant :
array('slug' => 'location')
Cette information est ensuite fusionnée dans l'objet de requête, et c'est pourquoi les paramètres
de la route (slug par exemple) sont accessibles depuis le fichier d'actions.
$this->slug = $request->getParameter('slug');
Comme on peut s'en douter, surcharger ou bien redéfinir complètement la méthode
sfRoute::matchesUrl() est un excellent moyen d'étendre et de personnaliser une route afin de
réaliser presque tout ce que l'on souhaite.
Créer une Classe de Route Personnalisée
L'étape suivante consiste à présent à créer une nouvelle classe de route personnalisée dans le
but d'étendre la route page_show actuelle afin qu'elle puisse se baser sur le sous-domaine des
objets Client. Pour ce faire, il suffit de créer un nouveau fichier nommé
acClientObjectRoute.class.php et de le placer dans le répertoire lib/routing du projet en
ayant pris le soin de créer ce répertoire juste avant :
// lib/routing/acClientObjectRoute.class.php
class acClientObjectRoute extends sfDoctrineRoute
{
public function matchesUrl($url, $context = array())
{
if (false === $parameters = parent::matchesUrl($url, $context))
{
return false;
}
return $parameters;
}
}
Après avoir vidé le cache de symfony, il ne reste plus qu'à indiquer à la route page_show
d'utiliser cette classe de route en modifiant la valeur de la clé class de la définition de la route
dans le fichier routing.yml :
# apps/fo/config/routing.yml
page_show:
url:
/:slug
class:
acClientObjectRoute
options:
model:
Page
type:
object
params:
module:
page
action:
show
Pour l'instant, la classe acClientObjectRoute n'apporte aucune fonctionnalité supplémentaire
mais toutes les pièces du puzzle sont désormais en place. La section suivante explique quels
sont les deux rôles spécifiques de la méthode matchesUrl() et donne la démarche à suivre pas
à pas pour redéfinir la logique de cette dernière.
Ajouter de la Logique à la Route Personnalisée
Pour ajouter la fonctionnalité requise à la classe de route personnalisée, il suffit de remplacer le
contenu du fichier acClientObjectRoute.class.php par le code ci-dessous.
class acClientObjectRoute extends sfDoctrineRoute
{
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
protected $baseHost = '.sympalbuilder.com';
public function matchesUrl($url, $context = array())
{
if (false === $parameters = parent::matchesUrl($url, $context))
{
return false;
}
// return false if the baseHost isn't found
if (strpos($context['host'], $this->baseHost) === false)
{
return false;
}
$subdomain = str_replace($this->baseHost, '', $context['host']);
$client = Doctrine_Core::getTable('Client')
->findOneBySubdomain($subdomain)
;
if (!$client)
{
return false;
}
return array_merge(array('client_id' => $client->id), $parameters);
}
}
Le premier appel à la méthode parent::matchesUrl() est important puisqu'il exécute le
processus classique d'analyse de la route. Dans cet exemple, tant que l'URL /location répond à
la route page_show, la méthode parent::matchesUrl() doit retourner un tableau contenant le
paramètre slug correspondant.
array('slug' => 'location')
En d'autres termes, tout le travail complexe d'analyse de la route a été réalisé par le
framework, ce qui permet au reste de la méthode de se concentrer sur l'analyse de la
correspondance avec le sous-domaine du Client.
public function matchesUrl($url, $context = array())
{
// ...
$subdomain = str_replace($this->baseHost, '', $context['host']);
$client = Doctrine_Core::getTable('Client')
->findOneBySubdomain($subdomain)
;
if (!$client)
{
return false;
}
return array_merge(array('client_id' => $client->id), $parameters);
}
En exécutant un simple remplacement de chaine, il est possible d'isoler la portion de l'hôte issu
du sous-domaine, afin d'interroger ensuite la base de données pour vérifier si un objet Client a
ce sous-domaine. Si aucun client ne possède ce sous-domaine, alors la méthode retourne false
afin d'indiquer que la requête entrante ne correspond pas à la route.
En revanche, si un objet Client répondant au sous-domaine courant existe dans la base de
données, alors un tableau contenant un paramètre supplémentaire client_id est fusionné avec
le tableau original retourné.
Le tableau $context passé à la méthode matchesUrl() est prérempli d'informations utiles
concernant la requête courante, incluant l'hôte (host), un booléen is_secure, le
request_uri, la méthode (method) HTTP et bien plus encore.
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
Ceci étant fait, on peut se demander ce que la nouvelle classe de route personnalisée a vraiment
accompli. La classe acClientObjectRoute réalise désormais les tâches suivantes :
La variable entrante $url correspondra à la route uniquement si l'hôte contient un sousdomaine appartenant à l'un des objets Client du modèle.
Si la route répond, alors un paramètre additionnel client_id pour l'objet de modèle
correspondant est retourné dans le tableau, qui sera ensuite fusionné dans les
paramètres de la requête.
Profiter de la Route Personnalisée
Maintenant que le bon paramètre client_id est retourné par la classe acClientObjectRoute, il
devient alors naturellement accessible via l'objet de la requête. Par exemple, l'action page/show
peut ainsi utiliser le paramètre client_id pour retrouver l'objet Page correct :
public function executeShow(sfWebRequest $request)
{
$this->page = Doctrine_Core::getTable('Page')->findOneBySlugAndClientId(
$request->getParameter('slug'),
$request->getParameter('client_id')
);
$this->forward404Unless($this->page);
}
La méthode findOneBySlugAndClientId() est un nouveau type de finders magiques introduit
dans Doctrine 1.2 qui permet d'interroger une table en s'appuyant sur plusieurs champs.
Aussi simple que cela puisse paraître, le framework de routage propose une solution encore plus
élégante. Il suffit dans un premier temps d'ajouter la méthode ci-dessous à la classe
acClientObjectRoute.
protected function getRealVariables()
{
return array_merge(array('client_id'), parent::getRealVariables());
}
Avec cette dernière pièce du puzzle, l'action peut s'appuyer entièrement sur la route pour
retourner un objet Page correspondant. L'action page/show peut ainsi être réduite à seulement
une ligne.
public function executeShow(sfWebRequest $request)
{
$this->page = $this->getRoute()->getObject();
}
Sans aucun effort additionnel, le code ci-dessus interrogera la base de données pour récupérer
un objet Page en s'appuyant sur les deux colonnes slug et client_id. De plus, comme toutes
les routes d'objet, l'action est automatiquement redirigée vers une page d'erreur 404 si aucun
objet correspondant n'a été trouvé.
Mais comment tout cela fonctionne-t-il ? Les routes d'objet comme sfDoctrineRoute, dont la
classe acClientObjectRoute hérite, interrogent automatiquement la base de données afin de
récupérer l'objet correspondant aux valeurs des variables spécifiées à la clé url de la route. Par
exemple, la route page_show, qui contient la variable slug dans son url, demande à la base de
données de lui donner l'objet Page en s'aidant de la colonne slug.
Or dans cette application, la route page_show doit également s'appuyer sur la valeur de la
colonne client_id afin de récupérer les objets correspondants. Pour ce faire, il aura seulement
fallu surcharger la méthode sfObjectRoute::getRealVariables() qui est automatiquement
appelée à l'intérieur de l'objet pour déterminer sur quelles colonnes la requête SQL doit être
exécutée. En ajoutant le champ client_id au tableau, l'objet acClientObjectRoute interrogera
la base de données en se basant sur ces deux colonnes slug et client_id.
Les routes d'objets ignorent automatiquement toutes les variables qui ne correspondent pas à
des colonnes réelles. Par exemple, si la clé url contient une variable :page tandis qu'aucune
colonne page n'existe dans la table associée, alors cette variable sera ignorée.
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
En l'état actuel des choses, la classe de route personnalisée accomplit tout ce qui est nécessaire
sans trop d'effort. Cette nouvelle route sera réutilisée dans les prochaines sections pour créer
une interface d'administration spécifique à chaque client.
Générer la Bonne Route
Un seul petit problème subsiste avec la manière dont est générée l'url. Pour comprendre, il suffit
de regarder l'exemple suivant qui décrit la création d'un lien vers une page.
<?php echo link_to('Locations', 'page_show', $page) ?>
Url générée: /location?client_id=1
Comme on peut le constater, le paramètre client_id a été automatiquement ajouté à la fin de
l'url. Cela se produit en effet parce que la route essaie d'utiliser toutes les variables pour
générer l'url. Comme la route sait qu'elle doit utiliser les deux paramètres slug et client_id,
alors elle les ajoute à l'url qu'elle génère. Pour fixer cette petite contrariété, il suffit d'ajouter la
méthode suivante à la classe acClientObjectRoute.
protected function doConvertObjectToArray($object)
{
$parameters = parent::doConvertObjectToArray($object);
unset($parameters['client_id']);
return $parameters;
}
Lorsqu'une route d'objet est générée, elle s'attend à retrouver toutes les informations
nécessaires en appelant la méthode doConvertObjectToArray(). Par défaut, le paramètre
client_id est retourné dans le tableau $parameters. Désormais, en supprimant ce paramètre
du tableau, cela permet ainsi d'éviter de le voir réapparaître dans l'url générée. Il est important
de se souvenir que la route d'objet peut s'offrir ce luxe dans la mesure où l'information du
Client est contenue dans le sous-domaine lui-même.
Le traitement de la méthode doConvertObjectToArray() peut entièrement être redéfini et
géré par les soins du développeur en ajoutant une méthode toParams() à la classe de
modèle. Cette méthode se doit de retourner un tableau des paramètres qui doivent figurer au
moment de la génération de la route.
Les Collections de Routes
Pour en finir avec l'application Sympal Builder, une interface d'administration doit être créée
dans laquelle chaque Client individuel sera capable de gérer ses propres Pages. Pour ce faire,
l'application a besoin d'un jeu d'actions pour lister, créer, éditer et supprimer des objets Page.
Comme tous ces types de modules sont sensiblement génériques, symfony est capable de les
générer automatiquement. Il suffit pour ce faire d'exécuter la tâche suivante depuis la ligne de
commande afin de générer un module pageAdmin à l'intérieur d'une application backend qui aura
été créée juste avant.
$ php symfony doctrine:generate-module backend pageAdmin Page --with-doctrine-route -with-show
La tâche ci-dessus génère un module avec un fichier d'actions et ses vues associées capables de
réaliser toutes les modifications nécessaires sur n'importe quel objet Page. De nombreuses
personnalisations de ce module CRUD généré peuvent être réalisées mais cela sort du cadre de
ce chapitre.
Alors que la commande ci-dessus prépare le module pour le développeur, il reste encore une
route à créer pour chaque action. En passant l'option --with-doctrine-route à la commande,
chaque action a été générée pour fonctionner avec une route d'objet. Cela réduit
considérablement la taille du code dans chaque action. Par exemple, l'action edit contient à
présent seulement une simple ligne.
public function executeEdit(sfWebRequest $request)
{
$this->form = new PageForm($this->getRoute()->getObject());
}
Au total, l'application d'administration de Sympal Builder contient seulement les actions index,
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
new, create, edit, update, et delete. Dans un schéma classique de développement, créer ces
routes en vue d'une utilisation RESTful aurait obligé le développeur à davantage de configuration
dans le fichier routing.yml.
pageAdmin:
url:
/pages
class:
sfDoctrineRoute
options:
{ model: Page, type: list }
params:
{ module: page, action: index
requirements:
sf_method: [get]
pageAdmin_new:
url:
/pages/new
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: new }
requirements:
sf_method: [get]
pageAdmin_create:
url:
/pages
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: create
requirements:
sf_method: [post]
pageAdmin_edit:
url:
/pages/:id/edit
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: edit }
requirements:
sf_method: [get]
pageAdmin_update:
url:
/pages/:id
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: update
requirements:
sf_method: [put]
pageAdmin_delete:
url:
/pages/:id
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: delete
requirements:
sf_method: [delete]
pageAdmin_show:
url:
/pages/:id
class:
sfDoctrineRoute
options:
{ model: Page, type: object }
params:
{ module: page, action: show }
requirements:
sf_method: [get]
}
}
}
}
Enfin, pour connaître et visualiser la configuration de toutes les routes déclarées dans une
application, il suffit d'exécuter la tâche app:routes dans la console afin d'obtenir un résumé de
la définition de chaque route.
$ php symfony app:routes backend
>> app
Current routes for application "backend"
Name
Method Pattern
pageAdmin
GET
/pages
pageAdmin_new
GET
/pages/new
pageAdmin_create POST
/pages
pageAdmin_edit
GET
/pages/:id/edit
pageAdmin_update PUT
/pages/:id
pageAdmin_delete DELETE /pages/:id
pageAdmin_show
GET
/pages/:id
Remplacer les Routes par une Collection de Routes
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
Heureusement, symfony fournit une manière bien plus simple pour spécifier toutes les routes qui
appartiennent à un CRUD traditionnel. Par conséquent, tout le contenu actuel du fichier
routing.yml se résume à une seule et unique route.
pageAdmin:
class:
options:
model:
prefix_path:
module:
sfDoctrineRouteCollection
Page
/pages
pageAdmin
Une fois encore, l'exécution de la tâche app:routes permet de visualiser toutes les routes
définies. Comme on peut le constater, les sept routes précédentes existent toujours.
$ php symfony app:routes backend
>> app
Current routes for application "backend"
Name
Method Pattern
pageAdmin
GET
/pages.:sf_format
pageAdmin_new
GET
/pages/new.:sf_format
pageAdmin_create POST
/pages.:sf_format
pageAdmin_edit
GET
/pages/:id/edit.:sf_format
pageAdmin_update PUT
/pages/:id.:sf_format
pageAdmin_delete DELETE /pages/:id.:sf_format
pageAdmin_show
GET
/pages/:id.:sf_format
Les collections de routes sont un type particulier de routes d'objet qui représentent
intérieurement plus d'une seule route. La route sfDoctrineRouteCollection, par exemple,
génère automatiquement les sept routes les plus fréquentes nécessaires à un CRUD. En
coulisses, la classe sfDoctrineRouteCollection ne fait rien de plus que créer les sept même
routes définies précédemment dans le fichier routing.yml. Les collections de route existent de
base comme raccourci pour créer un groupe de routes communes.
Créer une Classe Personnalisée de Collection de Routes
A partir de maintenant, chaque Client sera capable de modifier ses propres objets Page par
l'intermédiaire d'un CRUD interne fonctionnel et accessible depuis l'URL /pages.
Malheureusement, chaque Client peut pour le moment voir et modifier tous les objets Page de
la base de données. Par exemple, l'URL http://pete.sympalbuilder.com/backend.php/pages
génèrera une liste de deux objets Page issus des jeux de données de test : la page location de
la boutique Pete's Pet Shop et le menu de la page Menu de City Pub.
Ce problème peut être corrigé en réutilisant la classe acClientObjectRoute créée
précédemment pour le frontend. La classe sfDoctrineRouteCollection génère un groupe
d'objets sfDoctrineRoute mais il s'agit ici de générer un groupe d'objets acClientObjectRoute
à la place.
Pour y parvenir, il sera nécessaire d'avoir recours à une classe personnalisée de collection de
routes. Il suffit alors de créer le nouveau fichier acClientObjectRouteCollection.class.php
dans le répertoire lib/routing du projet avant de lui attribuer le contenu ci-dessous. On
remarque au passage que ce code est particulièrement concis.
// lib/routing/acClientObjectRouteCollection.class.php
class acClientObjectRouteCollection extends sfObjectRouteCollection
{
protected
$routeClass = 'acClientObjectRoute';
}
La propriété $routeClass définit la classe utilisée pour identifier chaque route sous-jacente.
C'est tout ! Chaque route sous-jacente est désormais un objet de type acClientObjectRoute.
Ainsi, la page http://pete.sympalbuilder.com/backend.php/pages affichera uniquement une
seule page : la page location du magasin animalier Pet's Pet Shop.
Grâce à cette nouvelle classe de route personnalisée, l'action index retourne uniquement les
objets Page liés au Client correspondant, en se basant toujours sur le sous-domaine de la
requête. Avec seulement quelques lignes de code, un module entier d'administration des objets
Page a été créé, et ce celui-ci peut désormais être utilisé en toute sécurité par les multiples
clients.
La Pièce Manquante : Créer une Nouvelle Page
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
Pour le moment, le formulaire de création ou d'édition d'une page affiche une liste déroulante de
tous les objets Client de la base de données. Pour des raisons évidentes de sécurité et
d'ergonomie, il n'est pas question de laisser l'utilisateur choisir cette donnée.
Il s'agit donc à présent de découvrir comment affecter automatiquement l'objet Client à la
page en cours en se basant toujours sur le sous-domaine de la requête. Pour ce faire, il suffit
de mettre à jour l'objet PageForm situé dans le fichier lib/form/PageForm.class.php.
public function configure()
{
$this->useFields(array(
'title',
'content',
));
}
La liste déroulante a été retirée de tous les formulaires Page comme cela était souhaité.
Néanmoins, lorsque de nouveaux objets Page sont créés, le champ client_id reste quant à lui
vide. Pour corriger ce défaut, il convient d'associer manuellement l'objet Client correspondant à
l'objet Page dans les deux actions new et create.
public function executeNew(sfWebRequest $request)
{
$page = new Page();
$page->Client = $this->getRoute()->getClient();
$this->form = new PageForm($page);
}
On remarque ici l'introduction d'une nouvelle méthode getClient() qui n'existe pas encore dans
la classe acClientObjectRoute. Le code suivant présente l'implémentation de cette nouvelle
méthode en réalisant seulement quelques modifications.
// lib/routing/acClientObjectRoute.class.php
class acClientObjectRoute extends sfDoctrineRoute
{
// ...
protected $client = null;
public function matchesUrl($url, $context = array())
{
// ...
$this->client = $client;
return array_merge(array('client_id' => $client->id), $parameters);
}
public function getClient()
{
return $this->client;
}
}
En ajoutant une propriété $client à la classe et en la définissant dans la méthode
matchesUrl(), l'objet Client correspondant peut alors facilement être rendu accessible grâce à
la route. La colonne client_id des nouveaux objets Page sera automatiquement et
correctement définie d'après le sous-domaine de l'hôte courant.
Personnaliser la Collection de Routes d'Objet
En utilisant le framework de routage, les problématiques soulevées par le cahier des charges de
l'application Sympal Builder ont toutes été résolues. A mesure que l'application grandit, le
développeur sera capable de réutiliser les routes personnalisées pour d'autres modules dans
l'interface d'administration. Par exemple, chaque Client pourra gérer ses propres galeries de
photos.
Une autre raison récurrente qui encourage la création d'une collection de routes personnalisée
est l'ajout de routes fréquemment utilisées. On peut par exemple imaginer un projet qui emploie
plusieurs modèles, et que chacun d'eux dispose d'une colonne is_active dans leur table
respective. Dans l'interface d'administration, cela permettrait ainsi de définir une manière aisée
pour activer la valeur de la colonne is_active pour n'importe quel objet. Il s'agit donc tout
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
d'abord de modifier la classe acClientObjectRouteCollection et de lui indiquer d'ajouter une
nouvelle route dans sa collection.
// lib/routing/acClientObjectRouteCollection.class.php
protected function generateRoutes()
{
parent::generateRoutes();
if (isset($this->options['with_is_active']) && $this->options['with_is_active'])
{
$routeName = $this->options['name'].'_toggleActive';
$this->routes[$routeName] = $this->getRouteForToggleActive();
}
}
La méthode sfObjectRouteCollection::generateRoutes() est appelée lorsque la classe de
collection de routes est instanciée, et est aussi responsable de la création de toutes les routes
nécessaires et de leur ajout dans le tableau de la propriété $routes. Dans ce cas, la création
actuelle de la route est déléguée à une nouvelle méthode protégée appelée
getRouteForToggleActive().
protected function getRouteForToggleActive()
{
$url = sprintf(
'%s/:%s/toggleActive.:sf_format',
$this->options['prefix_path'],
$this->options['column']
);
$params = array(
'module' => $this->options['module'],
'action' => 'toggleActive',
'sf_format' => 'html'
);
$requirements = array('sf_method' => 'put');
$options = array(
'model' => $this->options['model'],
'type' => 'object',
'method' => $this->options['model_methods']['object']
);
return new $this->routeClass(
$url,
$params,
$requirements,
$options
);
}
La dernière étape restante consiste à configurer la collection de routes dans le fichier
routing.yml. On remarque au passage que la méthode generateRoutes() cherche après une
option intitulée with_is_active avant d'ajouter la nouvelle route. Ajouter cette logique à la
classe donne davantage de contrôle au développeur dans le cas où il souhaiterait utiliser la
classe acClientObjectRouteCollection autre part dans le futur, sans pour autant avoir besoin
de la route toggleActive.
# apps/frontend/config/routing.yml
pageAdmin:
class:
acClientObjectRouteCollection
options:
model:
Page
prefix_path:
/pages
module:
pageAdmin
with_is_active: true
L'exécution de la tâche app:routes permet de vérifier que la nouvelle route toggleActive est
présente. La dernière pièce du puzzle manquante est la création de l'action qui s'occupera de
cette fonctionnalité. Par ailleurs, si cette collection de routes est amenée à être réutilisée à
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
travers d'autres modules, il suffit simplement de créer un nouveau fichier backendActions.php
dans le répertoire apps/backend/lib/actions du projet. Le répertoire actions doit être créé
manuellement.
# apps/backend/lib/action/backendActions.class.php
class backendActions extends sfActions
{
public function executeToggleActive(sfWebRequest $request)
{
$obj = $this->getRoute()->getObject();
$obj->is_active = !$obj->is_active;
$obj->save();
$this->redirect($this->getModuleName().'/index');
}
}
Enfin, il ne reste plus qu'à changer la classe de base de la classe pageAdminActions afin que
cette dernière hérite de la nouvelle classe backendActions.
class pageAdminActions extends backendActions
{
// ...
}
Quelles sont les étapes accomplies ? En ajoutant une route à la collection de routes ainsi qu'une
action associée dans une classe d'actions de base ; n'importe quel module est désormais
capable d'utiliser automatiquement cette fonctionnalité en utilisant une route
acClientObjectRouteCollection et en étendant la classe backendActions. Par conséquent,
une fonctionnalité commune peut être facilement mutualisée et capitalisée à travers plusieurs
modules.
Options d'une Collection de Routes
Les collections de routes d'objet contiennent une série d'options qui assurent une
personnalisation pointue. En de nombreuses circonstances, un développeur peut ainsi utiliser ces
options afin de configurer la collection, sans avoir besoin de créer une nouvelle classe
personnalisée de collection de routes. Une liste détaillée des options des collections de routes
est disponible dans le Guide de Référence de symfony.
Les Routes d'Actions
Chaque collection de routes d'objet accepte trois options différentes qui déterminent les routes
exactes générées dans la collection. Sans pour autant entrer profondément dans le détail, la
collection suivante génère les sept routes par défaut, auxquelles s'ajoutent une route de
collection d'objets et une route d'objet.
pageAdmin:
class:
acClientObjectRouteCollection
options:
# ...
actions:
[list, new, create, edit, update, delete, show]
collection_actions:
indexAlt:
[get]
object_actions:
toggle:
[put]
Changer la Colonne Discriminante
Par défaut, le framework de routage de symfony utilise la clé primaire d'un modèle lorsqu'il
s'agit d'interroger la base de données pour récupérer des objets. Cette information peut bien
évidemment être modifiée facilement. Par exemple, le code ci-dessous s'appuiera sur la colonne
slug pour récupérer un objet au lieu de sa clé primaire.
pageAdmin:
class:
acClientObjectRouteCollection
options:
# ...
column: slug
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
Modifier les Méthodes du Modèle
La route récupère par défaut tous les objets en relation pour une route de la collection et
interroge la base de données sur la colonne (column) spécifiée lorsqu'il s'agit des routes d'objet.
Une fois de plus, ce comportement peut être modifié et surchargé en ajoutant l'option
model_methods à la route. Dans l'exemple ci-dessous, les méthodes fetchAll() et
findForRoute() devront être définies dans la classe PageTable. Elles reçoivent toutes les deux
un tableau de paramètres de l'objet de requête en guise d'argument.
pageAdmin:
class:
acClientObjectRouteCollection
options:
# ...
model_methods:
list:
fetchAll
object:
findForRoute
Modifier les Paramètres par Défaut
Enfin, il arrive parfois qu'il faille rendre un paramètre spécifique disponible dans l'objet de
requête pour chaque route de la collection. Toutes les collections de route bénéficient d'une
option supplémentaire default_params qui permet d'y parvenir facilement.
pageAdmin:
class:
acClientObjectRouteCollection
options:
# ...
default_params:
foo:
bar
Conclusion
La principale responsabilité du framework de routage de symfony était à l'origine de faire
correspondre URLs et de générer des URLs. Cependant, il a finalement très vite évolué vers un
système entièrement personnalisable et capable de s'adapter à la plupart des besoins d'URLs
complexes dans un projet.
En prenant le contrôle sur les objets de route, la structure spéciale d'une URL peut ainsi être
abstraite en dehors de la logique métier, et conservée entièrement à l'intérieur de la route à
laquelle elle appartient. Il en résulte alors davantage de contrôle, de flexibilité et un code
beaucoup plus maniable.
« Introduction
Accroître la Productivité »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Techniques Avancées de Routage | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/02-Advanced-Routing[31/12/2010 02:33:39]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Accroître la Productivité
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Fabien Potencier
Utiliser symfony en tant que tel est, pour un développeur web, un
excellent moyen d'améliorer sa productivité. En effet, tout le monde
s'accorde à affirmer combien les exceptions détaillées de symfony ainsi
que la barre de débogage (web debug toolbar) sont d'excellents outils
qui participent à l'amélioration de la productivité.
Ce chapitre a pour objectif de présenter quelques trucs et astuces afin
de parfaire davantage la productivité en utilisant quelques unes des
fonctionnalités bien connues de symfony, ainsi que d'autres qui le sont
moins.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Automatiser le Processus de Création de Projet
Grâce à l'outil en ligne de commande, symfony fournit un moyen
simple, rapide et efficace pour initier un nouveau projet.
$ php /path/to/symfony generate-project foo --orm=Doctrine
La tâche generate:project génère la structure de base des répertoires par défaut du nouveau
projet, et crée les fichiers de configuration en leur spécifiant certaines valeurs par défaut. Le
framework symfony s'accompagne d'autres tâches pour créer des applications, installer des
plugins, configurer le modèle de données et bien plus encore.
Cependant, les toutes premières étapes de création d'un nouveau projet sont généralement
toujours les mêmes : création d'une application principale, installation de quelques plugins,
personnalisation de directives de configuration par défaut, etc.
Depuis symfony 1.3, le processus de création d'un projet peut être entièrement personnalisé et
automatisé.
Comme toutes les tâches de symfony sont des classes, il est particulièrement facile de les
personnaliser et de les étendre. En revanche, la tâche generate:project est plus
difficilement personnalisable car il n'existe pas encore de projet lorsqu'elle est exécutée.
La tâche generate:project accepte une option --installer correspondant à un chemin absolu
vers un script PHP qui sera exécuté au cours du processus de création du projet.
$ php /path/to/symfony generate:project --installer=/somewhere/my_installer.php
Le script /somewhere/my_installer.php est exécuté dans le contexte de l'instance
sfGeneratProjectTask afin de bénéficier des méthodes de la tâche en utilisant l'objet $this.
Les sections suivantes décrivent toutes les méthodes disponibles pour personnaliser le processus
de création d'un projet.
Si l'import de fichiers grâce aux URLs est configuré dans le fichier de php.ini pour la fonction
native include(), alors le chemin du script d'installation peut être remplacé par une URL.
Néanmoins, il convient de rester très prudent en utilisant cette méthode, en particulier quand
le contenu du script d'installation est peu, voire pas du tout, connu.
$ symfony generate:project
--installer=http://example.com/sf_installer.php
La Méthode installDir()
La méthode installDir() duplique une structure de répertoires, composée de sous-répertoires
et de fichiers, dans le nouveau projet créé.
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
Chapter Content
Automatiser le Processus de
Création de Projet
La Méthode installDir()
La Méthode runTask()
Les Loggers
Les Interactions avec l'Utilisateur
Les Opérations sur le Système de
Fichiers
Développer plus Vite
Choisir un Environnement de
Développement Intégré
Trouver de la Documentation
Rapidement
API en Ligne
Les Feuilles de Triche
Documentation Hors Ligne
Outils en Ligne
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
$this->installDir(dirname(__FILE__).'/skeleton');
La Méthode runTask()
La méthode runTask() exécute une tâche. Elle prend en paramètres le nom de la tâche ainsi
qu'une chaîne représentant les arguments et les options à passer à la tâche.
Déboguer Plus Vite
Tester plus Vite
Enregistrer des Tests Fonctionnels
Exécuter des Suites de Tests plus
Rapidement
$this->runTask('configure:author', "'Fabien Potencier'");
Les arguments et les options peuvent aussi être passés sous la forme de tableaux associatifs.
$this->runTask('configure:author', array('author' => 'Fabien Potencier'));
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Les noms raccourcis d'une tâche fonctionnent également :
$this->runTask('cc');
Feb 21: Paris
Mar 21: Paris
$this->runTask('plugin:install', 'sfDoctrineGuardPlugin');
Pour installer une version spécifique d'un plugin, il suffit simplement de lui fournir les options
nécessaires.
$this->runTask('plugin:install', 'sfDoctrineGuardPlugin', array('release' => '10.0.0',
'stability' => beta'));
L'exécution d'une tâche comprise dans un plugin nouvellement installé est possible à
condition de recharger la liste des tâches avant de l'utiliser.
$this->reloadTasks();
Il existe des tâches qui dépendent d'une application spécifique pour être exécuter. C'est le cas
par exemple de la tâche generate:module. Par conséquent, il est nécessaire de changer le
contexte de la configuration pour pouvoir l'utiliser.
$this->setConfiguration($this->createConfiguration('frontend', 'dev'));
Les Loggers
Afin de fournir le maximum d'informations au développeur sur l'exécution du script d'installation,
certaines informations peuvent être enregistrées en cours d'exécution.
// a simple log
$this->log('some installation message');
// log a block
$this->logBlock(array('', 'Fabien\'s Crazy Installer', ''), 'ERROR');
// log in a section
$this->logSection('install', 'install some crazy files');
Les Interactions avec l'Utilisateur
Les méthodes askConfirmation(), askAndValidate() et ask() offrent un moyen de rendre le
processus d'installation configurable et dynamique par l'intermédiaire de questions posées à
l'utilisateur dans la console.
Par exemple, si la question nécessite une confirmation en guise de réponse, alors il suffit
d'utiliser la méthode askConfirmation() comme le montre le code ci-dessous.
if (!$this->askConfirmation('Are you sure you want to run this crazy installer?'))
{
$this->logSection('install', 'You made the right choice!');
return;
}
De la même manière, il est possible de poser n'importe quelle question à l'utilisateur, et obtenir
sa saisie en guise de réponse grâce à la méthode ask(). Cette méthode retourne la réponse de
l'utilisateur sous forme d'une chaîne de caractères.
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
Cette méthode peut bien sûr être utilisée pour installer des plugins.
(Maîtrise de & Doctrine
- Français)
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
$secret = $this->ask('Give a unique string for the CSRF secret:');
Enfin, la méthode askAndValidate() est, quant à elle, responsable de la récupération de la
réponse de l'utilisateur, et de sa validation à la volée à l'aide d'un validateur.
$validator = new sfValidatorEmail(array(), array('invalid' => 'hmmm, it does not look
like an email!'));
$email = $this->askAndValidate('Please, give me your email:', $validator);
Les Opérations sur le Système de Fichiers
Le script d'installation est également capable de réaliser des modifications sur le système de
fichiers en accédant à l'objet sfFilesystem de symfony.
$this->getFilesystem()->...();
Le Processus de Création de la Sandbox
Le bac à sable de symfony est un projet préfabriqué, incluant une application prête à l'emploi et
une base de données SQLite préconfigurée. Son processus de création est réalisé à partir d'un
script d'installation que quiconque peut utiliser pour générer sa propre sandbox comme l'illustre
le code ci-dessous.
$ php symfony generate:project -installer=/path/to/symfony/data/bin/sandbox_installer.php
Etudier le contenu du fichier symfony/data/bin/sandbox_installer.php permet d'avoir un bon
exemple du fonctionnement des scripts d'installation.
Le script d'installation est un pur fichier PHP. Par conséquent, il peut réaliser à peu près tout ce
dont désire le développeur. Les scripts d'installation sont un moyen sûr et rapide de
personnaliser et d'industrialiser la création de projets symfony, tout en protégeant le
développeur des étapes manquantes. Ces fichiers peuvent également être partagés avec les
autres membres de la communauté symfony.
Développer plus Vite
Du code PHP aux tâches en ligne de commande, programmer signifie aussi taper souvent sur
son clavier. La suite de ce chapitre présente comment symfony aide le développeur à réduire
cette tâche à son plus strict minimum.
Choisir un Environnement de Développement Intégré
Utiliser un EDI (IDE pour l'acronyme anglais) aide le développeur à être toujours plus productif
dans différents domaines. Pour commencer, il faut savoir que tous les EDIs modernes
fournissent par défaut un mécanisme d'autocomplétion du code PHP qui facilite l'écriture de code
lorsque l'on ne connait pas le nom complet d'une méthode recherchée par exemple. Ainsi, il
n'est plus nécessaire de parcourir la documentation de l'API dans la mesure où l'EDI est capable
de suggérer toutes les méthodes disponibles pour l'objet courant.
Enfin, il faut savoir que certains EDIs comme PHPEdit ou bien Netbeans en savent beaucoup plus
sur symfony car ils fournissent une intégration spécifique et plus poussée des projets symfony.
Les Editeurs de Texte
Certains utilisateurs préfèrent utiliser un éditeur de texte pour développer, principalement parce
que les éditeurs de texte sont plus rapides que n'importe quel autre EDI. Cependant, les éditeurs
de texte offrent bien moins de fonctionnalités par rapport aux EDIs. La plupart des éditeurs
offrent toutefois des plugins / extensions qui peuvent être utilisés pour améliorer l'expérience
utilisateur et rendre le logiciel plus efficace avec PHP et les projets symfony.
Par exemple, de nombreux utilisateurs de Linux tendent à utiliser VIM pour toutes leurs tâches
de travail. Pour ces développeurs, une extension est disponible : vim-symfony. VIM-symfony est
un jeu de scripts qui intègrent symfony dans l'éditeur. En utilisant vim-symfony, il est ainsi
possible de créer facilement des macros et des commandes vim pour rationaliser les
développements symfony. Cet outil embarque de plus un jeu de commandes par défaut qui
mettent un certain nombre de fichiers de configuration à portée de main du développeur
(schéma, routage, etc) ainsi qu'une manière de passer simplement des actions aux vues
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
(templates).
Certains développeurs Mac OS X préfèrent utiliser TextMate et peuvent donc ainsi installer le
bundle symfony, qui offre un certain nombre de macros et de raccourcis qui aident économiser
du temps au quotidien.
Utiliser un EDI qui supporte symfony
Certains EDIs comme PHPEdit 3.4 et NetBeans 6.8 disposent d'un support natif de symfony, et
fournissent ainsi une intégration très pointue du framework. Il suffit de parcourir leur
documentation respective pour en savoir davantage à propos de leur support spécifique de
symfony et ainsi déterminer dans quelle mesure ils participent à l'amélioration de la productivité.
Aider l'EDI
L'autocomplétion de PHP dans les EDIs fonctionne uniquement pour les méthodes qui sont
explicitement déclarées dans le code PHP. En revanche, si le code fait usage de méthodes
"magiques" __call() ou __get() par exemple, les EDIs n'auront aucun moyen de deviner les
méthodes et propriétés disponibles.
Heureusement, la bonne nouvelle c'est que le développeur a la capacité d'aider l'EDI en lui
fournissant des informations sur les méthodes et propriétés du fichier à l'aide de blocs de
PHPDoc. Il suffit pour cela d'utiliser respectivement les annotations @method et @property.
Le code ci-dessous fait état d'une classe Message dans laquelle se trouvent une propriété
(message) et une méthode (getMessage()) dynamiques. Il montre comment l'EDI peut avoir
connaissance de ces propriété et méthode, bien qu'il n'y ait aucune définition explicite dans le
code PHP.
/**
* @property clob $message
*
* @method clob getMessage() Returns the current message value
*/
class Message
{
public function __get()
{
// ...
}
public function __call()
{
// ...
}
}
Même si la méthode getMessage() n'existe pas, elle sera quand même reconnue par l'EDI grâce
à l'annotation @method. Il en va de même pour la propriété message du fait de la présence de
l'annotation @property.
Cette technique est notamment utilisée par la tâche doctrine:build-model. Par exemple, une
classe Doctrine MailMessage avec deux colonnes (message et priority) ressemblera au code
ci-dessous.
/**
* BaseMailMessage
*
* This class has been auto-generated by the Doctrine ORM Framework
*
* @property clob $message
* @property integer $priority
*
* @method clob
getMessage() Returns the current record's "message" value
* @method integer
getPriority() Returns the current record's "priority" value
* @method MailMessage setMessage() Sets the current record's "message" value
* @method MailMessage setPriority() Sets the current record's "priority" value
*
* @package
##PACKAGE##
* @subpackage ##SUBPACKAGE##
* @author
##NAME## <##EMAIL##>
* @version
SVN: $Id: Builder.php 6508 2009-10-14 06:28:49Z jwage $
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
*/
abstract class BaseMailMessage extends sfDoctrineRecord
{
public function setTableDefinition()
{
$this->setTableName('mail_message');
$this->hasColumn('message', 'clob', null, array(
'type' => 'clob',
'notnull' => true,
));
$this->hasColumn('priority', 'integer', null, array(
'type' => 'integer',
));
}
public function setUp()
{
parent::setUp();
$timestampable0 = new Doctrine_Template_Timestampable();
$this->actAs($timestampable0);
}
}
Trouver de la Documentation Rapidement
Symfony est un framework riche en fonctionnalités, et de ce fait, il n'est pas toujours évident de
se rappeler toutes les possibilités de configuration, ou bien toutes les classes et les méthodes
mise à disposition. La précédente section a montré qu'utiliser un EDI est un moyen efficace pour
profiter de l'autocomplétion. Il est désormais temps de découvrir les autres outils existant qui
peuvent être employés pour trouver des réponses aussi vite que possible.
API en Ligne
La manière la plus rapide de trouver de la documentation à propos d'une classe ou d'une
méthode de symfony consiste à parcourir l'API en ligne.
Le moteur de recherche intégré à l'API en ligne est encore plus intéressant. La recherche permet
de trouver rapidement une classe ou bien une méthode avec seulement quelques frappes de
clavier. Après seulement quelques lettres saisies dans la boîte de recherche, une boîte de
résultats apparaîtra aussi vite avec davantage de suggestions très pratiques.
La recherche s'effectue en tapant le début d'un nom de classe.
Ou bien en saisissant le nom de méthode.
Ou encore en tapant le nom d'une classe suivi par :: afin de lister toutes les méthodes
disponibles.
Ou le début d'un nom de méthode afin d'affiner les possibilités.
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
Si l'on souhaite lister toutes les classes d'un paquetage, il suffit de saisir le nom du paquet, puis
de soumettre la recherche. Il est également possible d'intégrer l'API de recherche de symfony
dans le navigateur du client. De cette manière, il n'est plus nécessaire de naviguer sur le site de
symfony lorsqu'il s'agit de rechercher une information. C'est en effet possible puisque l'API en
ligne de symfony bénéficie de la recherche native OpenSearch.
Pour les utilisateurs de Firefox, le moteur de recherche de l'API en ligne de symfony apparaîtra
automatiquement dans la barre des moteurs de recherches du navigateur. Un clic sur le lien
"API OpenSearch" depuis la documentation de l'API en ligne permet de l'ajouter à la boîte de
recherche du navigateur.
Un billet a été rédigé sur le blog de symfony qui présente comment le moteur de recherche
de l'API s'intègre à Firefox à l'aide d'un screencast.
Les Feuilles de Triche
Il existe aujourd'hui une vaste collection de feuilles de triche (cheat sheets) qui permettent
d'accéder directement aux informations des majeures parties du framework :
Structure des Répertoires et CLI
La Vue
La Vue: Partials, Components, Slots et Component Slots
Tests Unitaires et Fonctionnels avec Lime
ORM
Propel
Propel Schema
Doctrine
Certaines de ces feuilles de triche n'ont pas encore été mises à jour pour >symfony 1.3.
Documentation Hors Ligne
Les réponses aux questions de configuration se trouvent principalement dans le guide de
référence de symfony. C'est le livre que tout développeur symfony se doit de garder près de lui
car il est le moyen le plus rapide de trouver n'importe quelle configuration disponible à partir
d'une table des matières détaillée, un index des mots-clés et des références croisées à l'intérieur
des chapitres, tableaux et plus encore.
Le livre est consultable gratuitement en ligne, disponible à l'achat pour une copie papier ou bien
encore de le télécharger gratuitement en version PDF.
Outils en Ligne
Le début de ce chapitre a montré que symfony fournit un large éventail d'outils pour aider le
développeur à démarrer rapidement. Arrivé au terme de son développement, un projet symfony
doit alors être déployé sur le serveur de production. Pour s'assurer qu'un projet est prêt au
déploiement, la checklist en ligne du déploiement couvre les principaux points importants à
vérifier avant de basculer l'application en production.
Déboguer Plus Vite
Lorsqu'une erreur se produit en environnement de développement, symfony affiche une page
d'exception explicite contenant de nombreuses informations utiles. Il est possible, par exemple,
d'analyser la trace de la pile d'appels des méthodes (et fonctions) et des fichiers qui ont été
exécutés.
De plus, en définissant le paramètre sf_file_link_format du fichier de configuration
settings.yml (voir ci-dessous), les noms des fichiers deviennent automatiquement cliquables
dans la trace de débogage de symfony. Ces derniers s'ouvriront ensuite dans l'éditeur ou EDI
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
configuré, et le curseur sera automatiquement positionné à la ligne où l'erreur a été générée.
C'est un excellent exemple d'une toute petite fonctionnalité qui peut faire économiser un temps
précieux aux développeurs lorsqu'ils font face à un problème.
Les panneaux dédiés à la vue et aux logs de la barre de débogage affichent eux aussi les
noms de fichiers (particulièrement lorsque l'extension xDebug est activée) qui deviennent
cliquables lorsque le paramètre sf_file_link_format est défini.
Par défaut, le paramètre sf_file_link_format n'est pas configuré et symfony utilise la valeur
de la directive de configuration xdebug.file_link_format si elle existe. Le paramètre
xdebug.file_link_format du php.ini permet à des versions récentes de xDebug d'ajouter des
liens pour tous les noms de fichiers présents dans la pile des appels.
La valeur du paramètre sf_file_link_format dépend à la fois de l'EDI et du système
d'exploitation. Par exemple, pour les utilisateurs de TextMate, il suffit d'ajouter la configuration
suivante dans le fichier settings.yml:
dev:
.settings:
file_link_format: txmt://open?url=file://%f&line=%l
Le jeton %f est remplacé par symfony par le chemin absolu vers le fichier tandis que la chaîne
%l est remplacée par le numéro de la ligne concernée. Pour les utilisateurs de VIM, la
configuration est un peu plus évoluée et décrite en ligne pour symfony et XDebug.
N'hésitez pas à utiliser votre moteur de recherche favoris afin d'apprendre comment
configurer votre EDI. Vous pouvez ainsi rechercher la configuration des paramètres
sf_file_link_format et sf_file_link_format dans la mesure où ils fonctionnent de la
même manière.
Tester plus Vite
Enregistrer des Tests Fonctionnels
Les tests fonctionnels simulent les interactions de l'utilisateur afin de tester globalement
l'intégration de toutes les parties de l'application entre elles. Ecrire des tests fonctionnels est
particulièrement simple mais aussi chronophage.
Néanmoins, comme chaque fichier de tests fonctionnels est un scénario qui simule un utilisateur
naviguant sur le site, et parce que naviguer réellement sur une application est plus rapide que
d'écrire du code PHP, pourquoi ne pas enregistrer directement le scénario en naviguant sur
l'application avant de le convertir à la volée en code PHP ?
Heureusement, symfony dispose d'un plugin pour remplir ce besoin. Il s'agit du plugin
swFunctionalTestGenerationPlugin qui permet de générer des squelettes de tests prêts à être
personnalisés en quelques minutes. Il est bien évidemment du rôle du développeur d'ajouter ses
propres appels aux différents testeurs afin de rendre le test utile et pertinent ; mais ce plugin
reste cependant un excellent moyen d'économiser du temps.
Le greffon swFunctionalTestGenerationPlugin fonctionne par l'intermédiaire d'un filtre
symfony qui intercepte toutes les requêtes qu'il convertit à la volée en code de tests
fonctionnels. Après avoir installé le plugin de manière traditionnelle, il est nécessaire de l'activer
en ajoutant les lignes suivantes après la ligne commentée du fichier filters.yml :
functional_test:
class: swFilterFunctionalTest
Ensuite, le plugin doit être activé dans la classe ProjectConfiguration du projet :
// config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
// ...
$this->enablePlugin('swFunctionalTestGenerationPlugin');
}
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Accroître la Productivité | symfony | Web PHP Framework
Comme le plugin utilise la barre de débogage en guise d'interface utilisateur principale, il faut
alors s'assurer qu'elle est bien activée (ce qui est déjà le cas par défaut en environnement de
test).
Une fois activée, un nouveau menu intitulé "Functional Test" apparaît dans la barre de
débogage. Ce nouveau panneau permet entre autres de démarrer l'enregistrement d'une session
en cliquant sur le lien "Activate" ou bien de réinitialiser la session courante en cliquant sur
"Reset".
A la fin de l'enregistrement, il ne reste plus qu'à copier et coller le code du champ multiligne
dans un fichier de tests fonctionnels, juste avant de démarrer sa personnalisation.
Exécuter des Suites de Tests plus Rapidement
Lorsque le projet contient une longue suite de tests, cette dernière peut s'avérer
particulièrement chronophage si les tests sont exécutés après chaque changement. Cela est
d'autant plus vrai quand seulement quelques tests échouent parmi un large jeu de tests.
A chaque fois que le développeur corrige un test, une nouvelle exécution de toute la suite de
tests devrait être lancée pour s'assurer que les autres tests n'ont pas été cassés. Or, tant que
les tests échoués ne sont pas tous fixés, il n'existe pas nécessairement de raison de relancer
tous les autres tests.
L'objectif consiste à accélérer ce processus. La tâche test:all inclut désormais une nouvelle
option --only-failed (raccourci -f) qui force la tâche à ne relancer que les tests qui ont
échoué à l'exécution précédente.
$ php symfony test:all --only-failed
A la première exécution, tous les tests sont exécutés tandis que les suivantes n'exécuteront que
ceux qui ont échoué au passage précédent. Une fois le code et les tests corrigés, ces derniers
passeront donc puis seront supprimés des les exécutions suivantes. Lorsque tous les tests
passent à nouveau, la suite de tests complète est exécutée...
« Techniques Avancées de Routage
Les Emails »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/03-Enhance-your-Productivity[31/12/2010 02:33:45]
The More with symfony book | Les Emails | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Les Emails
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Fabien Potencier
Envoyer des emails avec symfony devient à la fois simple et plus
efficace, grâce à l'utilisation de la bibliothèque Swift Mailer. Bien que
Swift Mailer facilite l'envoi des emails, symfony apporte quant à lui une
couche supplémentaire afin de rendre l'envoi d'emails encore plus
flexible et puissant. Ce chapitre explique comment les développeurs
peuvent tirer parti de toute cette puissance.
symfony 1.3 embarque la version 4.1 de Swift Mailer.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Introduction
La gestion des emails dans symfony est centralisée autour d'un objet de
gestion d'envoi d'email, le mailer. Comme pour la plupart des autres
objets qui composent le coeur de symfony, l'objet mailer est lui aussi une factory. De ce fait, il
est configuré par l'intermédiaire du fichier de configuration factories.yml, et toujours exposé à
travers l'instance du contexte.
$mailer = sfContext::getInstance()->getMailer();
Contrairement aux autres factories, le gestionnaire d'envoi d'emails est chargé et initialisé à
la demande. Par conséquent, s'il n'est pas utilisé, il n'y aura aucun impact sur les
performances.
Ce tutoriel explique comment est intégrée la librairie Swift Mailer dans symfony. Les lecteurs qui
souhaitent en savoir davantage sur les détails importants de Swift Mailer sont invités à se
référer à la documentation officielle en ligne.
Envoyer des Emails depuis une Action
Depuis une action, la récupération du gestionnaire d'envoi d'emails a été facilitée grâce à la
méthode raccourcie getMailer() :
$mailer = $this->getMailer();
La Méthode Rapide
Envoyer un email est alors aussi simple que d'utiliser la méthode sfAction::composeAndSend()
:
$this->getMailer()->composeAndSend(
'[email protected]',
'[email protected]',
'Subject',
'Body'
);
La méthode composeAndSend() accepte quatre arguments :
L'adresse email de l'expéditeur (from) ;
La / les adresse(s) email du / des destinataire(s) (to) ;
Le sujet du message ;
Le corps du message.
Toutes les méthodes qui accueillent une adresse e-mail en guise de paramètre peuvent en fait
accepter aussi bien une chaîne de caractères comme un tableau.
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
Chapter Content
Introduction
Envoyer des Emails depuis une
Action
La Méthode Rapide
La Méthode Flexible
La Méthode Efficace
Coupler l'Envoi d'Emails avec la Vue
de symfony
La Configuration
La Stratégie de Distribution
La Stratégie realtime
La Stratégie single_address
La Stratégie spool
La Stratégie none
Le Transport des Emails
Envoyer un Email depuis une
Tâche
The More with symfony book | Les Emails | symfony | Web PHP Framework
$address = '[email protected]';
$address = array('[email protected]' => 'Fabien Potencier');
Bien sûr, le tableau peut contenir plusieurs adresses email afin d'expédier le message à
plusieurs destinataires simultanément.
$to = array(
'[email protected]',
'[email protected]',
);
$this->getMailer()->composeAndSend('[email protected]', $to, 'Subject', 'Body');
$to = array(
'[email protected]' => 'Mr Foo',
'[email protected]' => 'Miss Bar',
);
$this->getMailer()->composeAndSend('[email protected]', $to, 'Subject', 'Body');
Le Débogage
Tester les Emails
Les Messages Electroniques sous
forme de Classes
Quelques Recettes
Envoyer des Emails avec Gmail
Personnaliser l'Objet Mailer
Utiliser des Plugins Swift Mailer~
Personnaliser le Comportement de
Spool
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
La Méthode Flexible
Bien qu'elle soit simple et rapide à mettre en place, la première méthode peut s'avérer moins
flexible. La méthode sfAction::compose() accroît la flexibilité du développeur car elle permet
de créer le message, de le personnaliser à volonté et éventuellement de l'envoyer. C'est
d'autant plus pratique lorsqu'il s'agit d'ajouter une pièce jointe au message.
Si vous avez besoin de plus de flexibilité, vous pouvez aussi utiliser la méthode
sfAction::compose() pour créer un message, le personnaliser de la manière que vous voulez,
et éventuellement l'envoyer. C'est, par exemple, très pratique lorsque vous avez besoin
d'ajouter une pièce jointe (attachment|email attachment) au message comme le montre
l'exemple ci-dessous.
// create a message object
$message = $this->getMailer()
->compose('[email protected]', '[email protected]', 'Subject', 'Body')
->attach(Swift_Attachment::fromPath('/path/to/a/file.zip'))
;
// send the message
$this->getMailer()->send($message);
La Méthode Efficace
Une autre méthode consiste à créer l'objet du message à la main directement afin de bénéficier
d'encore plus de flexibilité. Le code ci-dessous témoigne de cette flexibilité accrue.
$message = Swift_Message::newInstance()
->setFrom('[email protected]')
->setTo('[email protected]')
->setSubject('Subject')
->setBody('Body')
->attach(Swift_Attachment::fromPath('/path/to/a/file.zip'))
;
$this->getMailer()->send($message);
Les sections "Creating Messages" et "Message Headers" de la documentation officielle de Swift
Mailer décrivent tout ce dont il faut savoir à propos de la création de messages.
Coupler l'Envoi d'Emails avec la Vue de symfony
Envoyer vos emails depuis les actions permet de profiter en toute aisance de la puissance des
vues partielles et des composants pour assigner un contenu au message.
$message->setBody($this->getPartial('partial_name', $arguments));
La Configuration
Le gestionnaire d'envoi d'emails peut être configuré dans le fichier de configuration
factories.yml de la même manière que toute autre factory de symfony. La configuration par
défaut du gestionnaire est la suivante.
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Les Emails | symfony | Web PHP Framework
mailer:
class: sfMailer
param:
logging:
%SF_LOGGING_ENABLED%
charset:
%SF_CHARSET%
delivery_strategy: realtime
transport:
class: Swift_SmtpTransport
param:
host:
localhost
port:
25
encryption: ~
username:
~
password:
~
A la création d'une nouvelle application, le fichier de configuration local factories.yml
surcharge la configuration par défaut, en spécifiant quelques ajustements spécifiques aux
environnements de production (prod), de développement (dev) et de test (test).
test:
mailer:
param:
delivery_strategy: none
dev:
mailer:
param:
delivery_strategy: none
La Stratégie de Distribution
L'une des principales fonctionnalités utiles de l'intégration de Swift Mailer dans symfony est la
stratégie de distribution des emails. La stratégie de distribution permet d'indiquer à symfony de
quelle manière le framework doit envoyer les emails. Elle est configurée à partir du paramètre
de configuration delivery_strategy du fichier factories.yml.
La stratégie change la manière dont la méthode send()|sfMailer::send() se comporte. Quatre
stratégies de distribution sont disponibles par défaut, ce qui devrait convenir à la majorité des
besoins :
realtime : les messages sont envoyés en temps réel ;
single_address : les messages sont envoyés à une adresse unique ;
spool : les messages sont stockés dans une file d'attente ;
none : les messages sont simplement ignorés.
La Stratégie realtime
La stratégie realtime est la stratégie de distribution par défaut car c'est aussi la plus facile à
configurer dans la mesure où il n'y a finalement rien de spécial à faire.
Les emails sont expédiés à l'aide d'un objet de transport configuré dans la section transport du
fichier de configuration factories.yml. La prochaine section donne davantage d'informations à
propos de la configuration de l'objet de transport.
La Stratégie single_address
Avec la stratégie single_address, tous les messages sont envoyés à une unique adresse email.
Cette stratégie est configurée au paramètre de configuration delivery_strategy.
La stratégie single_address est particulièrement utile en environnement de développement
afin d'éviter d'envoyer des emails aux utilisateurs finaux réels. Le développeur garde néanmoins
une grande flexibilité dans la mesure où il peut toujours consulter le rendu du message dans un
client mail.
Le développeur peut avoir besoin de vérifier les valeurs des destinataires originaux dans les
en-têtes to, cc et bcc. Ces valeurs sont disponibles dans les entêtes respectives suivantes :
X-Swift-To, X-Swift-Cc et X-Swift-Bcc.
Les emails sont expédiés avec le même transport d'email que celui utilisé pour la stratégie de
distribution realtime.
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
La Stratégie spool
Avec la stratégie de spool, les messages sont sauvegardés dans une file d'attente. C'est sans
aucun doute la meilleure stratégie pour l'environnement de production dans la mesure où les
requêtes web n'ont pas à attendre que les emails ont bien été envoyés.
La classe de spool est configurée dans le paramètre de configuration spool_class du fichier
factories.yml, et symfony inclut trois de ces stratégies par défaut :
Swift_FileSpool : les messages sont stockés sur le système de fichiers ;
Swift_DoctrineSpool : les messages sont stockés dans un modèle Doctrine ;
Swift_PropelSpool : les messages sont stockés dans un modèle Propel.
Lorsque la classe de spool est instanciée, les valeurs définies dans le paramètre de configuration
spool_arguments sont utilisées comme arguments du constructeur. Les options de configuration
disponibles pour les classes de file d'attente natives sont listées ci-dessous :
Swift_FileSpool :
Le chemin absolu du répertoire de la file d'attente (les messages sont stockés dans
ce répertoire).
Swift_DoctrineSpool :
Le modèle Doctrine à utiliser pour sauvegarder les messages (MailMessage par
défaut).
Le nom de la colonne à utiliser pour le stockage du message (message par défaut).
La méthode à appeler pour retrouver le message à envoyer (optionnel). Elle reçoit
les options de la file d'attente comme argument.
Swift_PropelSpool :
Le modèle Propel à utiliser pour sauvegarder les messages (MailMessage par
défaut).
Le nom de la colonne à utiliser pour le stockage du message (message par défaut).
La méthode à appeler pour retrouver le message à envoyer (optionnel). Elle reçoit
les options de la file d'attente comme argument.
Le listing ci-dessous décrit une configuration typique du spool Doctrine :
# Schema configuration in schema.yml
MailMessage:
actAs: { Timestampable: ~ }
columns:
message: { type: blob, notnull: true }
# configuration in factories.yml
mailer:
class: sfMailer
param:
delivery_strategy: spool
spool_class:
Swift_DoctrineSpool
spool_arguments:
[ MailMessage, message, getSpooledMessages ]
Le code ci-après décrit la même configuration pour le spool Propel :
# Schema configuration in schema.yml
mail_message:
message:
{ type: blob, required: true }
created_at: ~
# configuration in factories.yml
dev:
mailer:
param:
delivery_strategy: spool
spool_class:
Swift_PropelSpool
spool_arguments:
[ MailMessage, message, getSpooledMessages ]
Pour envoyer un message sauvegardé dans la file d'attente, il suffit d'utiliser la tâche
project:send-emails. Il est important de noter que cette commande est complètement
indépendante de l'implémentation de la file d'attente, et des options qu'elle accepte.
$ php symfony project:send-emails
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
La tâche project:send-emails accepte aussi les options application et env.
Lorsque la tâche project:send-emails est invoquée, les emails sont envoyés à l'aide du même
objet de transport que celui défini pour la stratégie realtime.
La tâche project:send-emails est exécutable sur n'importe quelle machine, et pas
nécessairement sur la machine qui a créé le message. Cela fonctionne en effet parce que tout
est sauvegardé dans l'objet du message, y compris les fichiers attachés.
Les implémentations des files d'attente par défaut sont particulièrement triviales. Elles
envoient les emails sans aucune gestion d'erreur, comme si elles avaient été envoyées avec
la stratégie realtime. Bien sûr, les classes de files d'attente par défaut peuvent être
étendues afin d'implémenter une logique métier et une gestion des erreurs personnalisées.
Il arrive parfois qu'il faille envoyer un message immédiatement sans avoir à le sauvegarder dans
la file d'attente, bien que l'application soit configurée avec la stratégie de spool. Heureusement,
symfony fournit la méthode spéciale sendNextImmediately() de l'objet mailer pour satisfaire ce
besoin.
$this->getMailer()->sendNextImmediately()->send($message);
Dans l'exemple précédent, l'objet $message ne sera pas sauvegardé dans la file d'attente et sera
immédiatement expédié. Comme son nom l'indique, la méthode sendNextImmediately() affecte
seulement le tout prochain message à être envoyé.
La méthode sendNextImmediately() n'a aucun effet particulier lorsque la stratégie de
distribution n'est pas définie à la valeur spool.
La Stratégie none
La stratégie none est utile en environnement de développement dans la mesure où elle empêche
tout email d'être envoyé aux destinataires finaux. Néanmoins, les messages restent disponibles
dans la barre de débogage. La section suivante donne davantage d'informations au sujet du
panneau de gestion des emails de la barre d'outils.
Cette stratégie est également la meilleure pour l'environnement de test. En effet, le testeur
stTesterMailer offre la possibilité d'introspecter les messages sans avoir le besoin de les
envoyer réellement. Les tests sur les messages envoyés sont décrits dans la section suivante.
Le Transport des Emails
Les emails sont actuellement expédiés à l'aide d'un objet de transport. Le transport est configuré
dans le fichier de configuration factories.yml, et sa configuration par défaut force une
connexion au serveur SMTP de la machine locale :
transport:
class: Swift_SmtpTransport
param:
host:
localhost
port:
25
encryption: ~
username:
~
password:
~
Swift Mailer embarque nativement trois classes de transport différentes :
Swift_SmtpTransport utilise un serveur SMTP pour envoyer les messages ;
Swift_SendmailTransport utilise le binaire sendmail pour envoyer les messages ;
Swift_MailTransport utilise la fonction native mail() de PHP pour envoyer les emails.
La section "Transport Types" de la documentation officielle de Swift Mailer décrit tout ce dont
il faut savoir à propos des classes de transport natives et leurs différents paramètres.
Envoyer un Email depuis une Tâche
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
Envoyer un email depuis une tâche est pratiquement similaire à envoyer un email depuis une
action dans la mesure où le mécanisme des tâches expose une méthode getMailer().
Le système de tâches dépend de la configuration courante au moment de la création de l'objet
mailer. Par conséquent, si la tâche a besoin de la configuration d'une application spécifique,
alors la commande doit obligatoirement accepter l'option --application. Le chapitre sur les
tâches donne davantage d'explications à ce sujet.
Il est important de remarquer que la tâche utilise la même configuration que les contrôleurs. Par
conséquent, pour forcer la distribution du message, bien que ce soit la stratégie de spool qui
soit utilisée, alors il suffit d'utiliser la méthode sendNextImmediately() :
$this->getMailer()->sendNextImmediately()->send($message);
Le Débogage
Depuis toujours, le débogage des emails a toujours été un véritable cauchemar. Avec symfony,
c'est beaucoup plus simple grâce au nouveau panneau email de la web debug toolbar. Avec tout
le confort du navigateur web, il est désormais possible de savoir facilement et rapidement
combien de messages ont été expédiés dans l'action courante.
Un clic sur l'icône des emails donne accès à tous les messages envoyés, affichés sous forme
brute comme l'atteste la capture d'écran ci-dessous.
Chaque fois qu'un email est envoyé, symfony ajoute au passage un message dans le log.
Tester les Emails
Bien sûr, l'intégration des emails n'aurait pas été aussi complète sans un moyen de tester les
messages. Par défaut, symfony enregistre un nouveau testeur mailer (sfMailerTester) afin de
faciliter les tests fonctionnels sur les emails envoyés. La méthode hasSent(), par exemple, teste
le nombre de messages envoyés au cours de la requête courante.
$browser->
get('/foo')->
with('mailer')->
hasSent(1)
;
Le code précédent vérifie que l'url /foo envoie seulement un email. De plus, les spécificités de
chaque email envoyé peuvent être testées à l'aide des méthodes checkHeader() et
checkBody().
$browser->
get('/foo')->
with('mailer')->begin()->
hasSent(1)->
checkHeader('Subject', '/Subject/')->
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
checkBody('/Body/')->
end()
;
Le second argument de la méthode checkHeader() et le premier paramètre de checkBody()
peuvent être l'une des valeurs suivantes.
une chaîne pour vérifier une correspondance exacte ;
une expression régulière pour contrôler la correspondance de la valeur avec elle ;
une expression régulière négative (une expression régulière qui débute par un !) pour
vérifier que la valeur ne correspond pas.
Par défaut, les vérifications sont réalisées sur le premier message envoyé. Si plusieurs messages
ont été expédiés, la méthode withMessage() offre la possibilité de choisir sur quel message
appliquer les tests.
$browser->
get('/foo')->
with('mailer')->begin()->
hasSent(2)->
withMessage('[email protected]')->
checkHeader('Subject', '/Subject/')->
checkBody('/Body/')->
end()
;
La méthode withMessage() accepte une adresse email de destinataire en guise de premier
argument. Elle accueille également un second paramètre pour indiquer quel message tester si
plusieurs emails ont été adressés à la même personne. Enfin, la méthode debug() expose les
messages envoyés afin de déceler les problèmes lorsqu'un test échoue.
$browser->
get('/foo')->
with('mailer')->
debug()
;
Les Messages Electroniques sous forme de Classes
L'introduction de ce chapitre a montré comment envoyer des emails depuis une action. C'est
sans doute la manière la plus simple pour expédier des messages dans une application symfony,
et probablement la meilleure lorsqu'il s'agit seulement d'envoyer quelques emails simples.
Néanmoins, plus l'application a besoin de gérer un nombre important de messages, et plus le
risque d'adopter une stratégie différente augmente. Comme tous les messages sont des objets
PHP purs, la manière évidente d'organiser les messages consiste à créer une classe pour chacun
d'eux.
// lib/email/ProjectConfirmationMessage.class.php
class ProjectConfirmationMessage extends Swift_Message
{
public function __construct()
{
parent::__construct('Subject', 'Body');
$this
->setFrom(array('[email protected]' => 'My App Bot'))
->attach('...')
;
}
}
Envoyer un message depuis une action, ou bien depuis n'importe où dans ce cas est aussi
simple que d'instancier la classe du message correspondant.
$this->getMailer()->send(new ProjectConfirmationMessage());
Bien sûr, il est plus pratique d'ajouter une classe de base pour centraliser les en-têtes partagés
tels que l'en-tête From, ou bien pour inclure une signature commune.
// lib/email/ProjectConfirmationMessage.class.php
class ProjectConfirmationMessage extends ProjectBaseMessage
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
{
public function __construct()
{
parent::__construct('Subject', 'Body');
// specific headers, attachments, ...
$this->attach('...');
}
}
// lib/email/ProjectBaseMessage.class.php
class ProjectBaseMessage extends Swift_Message
{
public function __construct($subject, $body)
{
$body .= <<<EOF
-Email sent by My App Bot
EOF
;
parent::__construct($subject, $body);
// set all shared headers
$this->setFrom(array('[email protected]' => 'My App Bot'));
}
}
Si un message dépend de certains objets du modèle, ce dernier peut alors bien entendu être
transmis comme paramètre du constructeur.
// lib/email/ProjectConfirmationMessage.class.php
class ProjectConfirmationMessage extends ProjectBaseMessage
{
public function __construct($user)
{
parent::__construct('Confirmation for '.$user->getName(), 'Body');
}
}
Quelques Recettes
Envoyer des Emails avec Gmail
Les lecteurs qui ne possèdent pas de serveur SMTP mais qui disposent d'un compte Gmail
peuvent s'appuyer sur la configuration suivante afin d'utiliser les serveurs de Google comme
moyen d'expédition et d'archivage des messages.
transport:
class: Swift_SmtpTransport
param:
host:
smtp.gmail.com
port:
465
encryption: ssl
username:
your_gmail_username_goes_here
password:
your_gmail_password_goes_here
Remplacer les valeurs des paramètres username et password par celles du compte Gmail
adéquat suffisent à configurer l'objet mailer.
Personnaliser l'Objet Mailer
Si configurer le mailer par le fichier factories.yml n'est pas suffisant, l'évènement
mailer.configure peut alors être écouté afin de personnaliser davantage le mailer. Pour ce
faire, il suffit de se connecter à l'évènement depuis la classe de configuration
ProjectConfiguration comme le montre l'exemple ci-dessous.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
// ...
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
$this->dispatcher->connect(
'mailer.configure',
array($this, 'configureMailer')
);
}
public function configureMailer(sfEvent $event)
{
$mailer = $event->getSubject();
// do something with the mailer
}
}
La section suivante illustre un usage avancé et pratique de cette technique.
Utiliser des Plugins Swift Mailer~
L'utilisation des plugins de Swift Mailer s'effectue en écoutant l'évènement mailer.configure
(voir la section ci-dessus).
public function configureMailer(sfEvent $event)
{
$mailer = $event->getSubject();
$plugin = new Swift_Plugins_ThrottlerPlugin(
100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE
);
$mailer->registerPlugin($plugin);
}
La section "Plugins" de la documentation officielle de Swift Mailer décrit ce qu'il faut avoir à
propos des plugins natifs.
Personnaliser le Comportement de Spool
L'implémentation native des spools est particulièrement simple. Chaque spool récupère tous les
emails depuis une file d'attente en ordre aléatoire, avant de les envoyer un par un.
Dans cette section, il s'agit d'apprendre comment implémenter un système de priorité pour la
file d'attente afin de donner toutes les informations nécessaires à l'implémentation d'une logique
personnalisée. Tout d'abord, il convient d'ajouter une nouvelle colonne priority au modèle de
données existant.
# for Propel
mail_message:
message:
{ type: blob, required: true }
created_at: ~
priority:
{ type: integer, default: 3 }
# for Doctrine
MailMessage:
actAs: { Timestampable: ~ }
columns:
message: { type: blob, notnull: true }
priority: { type: integer }
Lorsqu'un email est envoyé, l'en-tête de priorité de celui-ci doit être fixé. La valeur 1 représente
la priorité la plus élevée.
$message = $this->getMailer()
->compose('[email protected]', '[email protected]', 'Subject', 'Body')
->setPriority(1)
;
$this->getMailer()->send($message);
Ensuite, la méthode setMessage() par défaut doit être surchargée afin de modifier la priorité de
l'objet MailMessage lui-même.
// for Propel
class MailMessage extends BaseMailMessage
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
{
public function setMessage($message)
{
$msg = unserialize($message);
$this->setPriority($msg->getPriority());
return parent::setMessage($message);
}
}
// for Doctrine
class MailMessage extends BaseMailMessage
{
public function setMessage($message)
{
$msg = unserialize($message);
$this->priority = $msg->getPriority();
return $this->_set('message', $message);
}
}
Dans cet exemple, le message est linéarisé par la file d'attente. Par conséquent, il doit d'abord
être délinéarisé afin d'être capable de récupérer la valeur de la priorité. Il ne reste maintenant
plus qu'à ajouter une méthode qui ordonne les messages par priorité.
// for Propel
class MailMessagePeer extends BaseMailMessagePeer
{
static public function getSpooledMessages()
{
$c = new Criteria();
$c->addAscendingOrderByColumn(self::PRIORITY);
return self::doSelect($c);
}
// ...
}
// for Doctrine
class MailMessageTable extends Doctrine_Table
{
public function getSpooledMessages()
{
return $this->createQuery('m')
->orderBy('m.priority')
;
}
// ...
}
La dernière étape consiste à définir la méthode de récupération des messages dans le fichier de
configuration factories.yml afin de changer la manière dont les messages sont obtenus par
défaut depuis la file d'attente.
spool_arguments: [ MailMessage, message, getSpooledMessages ]
C'est tout ce qu'il y'a à faire. Maintenant, chaque fois que la tâche project:send-emails sera
exécutée, chaque email sera expédié en fonction de sa priorité.
Personnaliser le Spool avec un Critère
L'exemple précédent utilise un en-tête standard de message : la priorité. En revanche, si l'on
souhaite utiliser n'importe quel critère ou bien ne pas altérer le message envoyé, il convient de
stocker ce critère comme un en-tête personnalisé. Il ne restera plus qu'à le retirer juste avant
d'envoyer l'email.
Il suffit tout d'abord d'ajouter un en-tête personnalisé au message à envoyer.
public function executeIndex()
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Les Emails | symfony | Web PHP Framework
{
$message = $this->getMailer()
->compose('[email protected]', '[email protected]', 'Subject', 'Body')
;
$message->getHeaders()->addTextHeader('X-Queue-Criteria', 'foo');
$this->getMailer()->send($message);
}
Enfin, il ne reste plus qu'à récupérer la valeur de cet en-tête au moment de stocker le message
dans la file d'attente et supprimer le message immédiatement.
public function setMessage($message)
{
$msg = unserialize($message);
$headers = $msg->getHeaders();
$criteria = $headers->get('X-Queue-Criteria')->getFieldBody();
$this->setCriteria($criteria);
$headers->remove('X-Queue-Criteria');
return parent::_set('message', serialize($msg));
}
« Accroître la Productivité
Widgets et Validateurs Personnalisés »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/04-Emails[31/12/2010 02:33:48]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Widgets et Validateurs Personnalisés
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Thomas Rabaix
Ce chapitre explique comment construire des widgets et validateurs
personnalisés à utiliser dans le framework de formulaires. Il présentera
les entrailles des classes sfWidgetForm et sfValidator, ainsi que la
manière de créer des widgets simples et complexes.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Au Coeur des Widgets et Validateurs
Présentation de la Classe sfWidgetForm
Un objet de la classe sfWidgetForm représente une implémentation
visuelle de la manière dont les données relatives seront éditées. Une
chaîne de caractères par exemple pourrait être éditée à partir d'un
champ texte ou bien à l'aide d'un éditeur WYSIWIG avancé. Afin d'être
entièrement configurable, la classe sfWidgetForm dispose de deux
propriétés importantes : les options et les attributs (attributes).
Support symfony!
Buy this book
or donate.
Les options sont utilisées pour configurer le widget. Une option peut servir par exemple
à recevoir une requête de base de données à utiliser afin d'alimenter une liste déroulante.
Les attributs (attributes) sont les attributs HTML ajoutés à l'élément lors du rendu.
De plus, la classe sfWidgetForm implémente deux méthodes importantes :
La méthode configure() définit les options optionnelles et celles qui sont obligatoires.
Alors que la rédéfinition du constructeur ne constitue pas une bonne pratique en soit, la
méthode configure() peut quant à elle être redéfinie en toute sécurité.
La méthode render() génère la sortie HTML du widget. Elle requiert un premier
paramètre obligatoire, le nom du widget, et un second paramètre optionnel, sa valeur.
Un objet sfWidgetForm ne sait absolument rien de son nom ou de sa valeur. Le composant
est seulement responsable du rendu du widget. Le nom et la valeur sont gérés par un objet
sfFormFieldSchema qui fait le lien entre les données et les widgets.
Présentation de la Classe sfValidatorBase
La classe sfValidatorBase est la classe de base pour tous les validateurs. Sa méthode
sfValidatorBase::clean() est la plus importante dans la mesure où elle vérifie si la valeur est
valide en fonction des options fournies.
A l'intérieur, la méthode clean() exécute plusieurs actions différentes :
supprimer les espaces blancs en début et fin de chaînes de caractères saisies, à condition
que l'option trim soit spécifiée,
vérifier si la valeur est vide,
appeler la méthode doClean() du validateur.
La méthode doClean() est celle qui implémente la logique de validation principale. Ce n'est pas
une bonne pratique de redéfinir la méthode clean(). En revanche, c'est toujours dans la
méthode doClean() que doit être réalisée la logique de validation personnalisée.
Un validateur peut aussi être utilisé comme un composant indépendant pour vérifier l'intégrité
d'une entrée. Par exemple, le validateur sfValidatorEmail contrôlera si l'email est valide ou
non.
$v = new sfValidatorEmail();
try
{
$v->clean($request->getParameter("email"));
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
Chapter Content
Au Coeur des Widgets et
Validateurs
Présentation de la Classe
sfWidgetForm
Présentation de la Classe
sfValidatorBase
L'Attribut options
Construire un Widget et un
Validateur Simples
Le Widget Google Address Map
Cas d'utilisation 1 :
Cas d'utilisation 2 :
Le Widget sfWidgetFormGMapAddress
Le Validateur
sfValidatorGMapAddress
Test du Validateur
Conclusion
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
}
catch(sfValidatorError $e)
{
$this->forward404();
}
Be trained by symfony experts
Jan 24: Paris
Lorsqu'un formulaire est associé aux valeurs transmises dans la requête, l'objet sfForm
conserve les références aux valeurs teintées originales ainsi qu'aux valeurs filtrées. Les
valeurs originales sont utilisées lorsque le formulaire est réaffiché, alors que les valeurs
nettoyées sont employées par l'application (par exemple pour hydrater et sauvegarder
l'objet).
Feb 21: Paris
Les deux objets sfWidgetForm et sfValidatorBase ont une variété d'options dont certaines
sont optionnelles et d'autres obligatoires. Ces options sont définies à l'intérieur de chaque
méthode configure() de chaque classe grâce aux méthodes suivantes.
addOption($name, $value) définit une option avec un nom et une valeur par défaut,
addRequiredOption($name) définit une option obligatoire.
Ces deux méthodes sont très utiles dans la mesure où elles s'assurent que les valeurs
respectives sont correctement transmises au validateur ou au widget.
Construire un Widget et un Validateur Simples
Cette section décrit comment construire un widget simple. Ce widget particulier sera intitulé
widget "Trilean", et affichera une liste déroulante, de type select, composée de trois choix
possibles : No, Yes et Null.
class sfWidgetFormTrilean extends sfWidgetForm
{
public function configure($options = array(), $attributes = array())
{
$this->addOption('choices', array(
0 => 'No',
1 => 'Yes',
'null' => 'Null'
));
}
public function render($name, $value = null, $attributes = array(), $errors =
array())
{
$value = $value === null ? 'null' : $value;
$options = array();
foreach ($this->getOption('choices') as $key => $option)
{
$attributes = array('value' => self::escapeOnce($key));
if ($key == $value)
{
$attributes['selected'] = 'selected';
}
$options[] = $this->renderContentTag(
'option',
self::escapeOnce($option),
$attributes
);
}
return $this->renderContentTag(
'select',
"\n".implode("\n", $options)."\n",
array_merge(array('name' => $name), $attributes
));
}
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
L'Attribut options
(Maîtrise de & Doctrine
- Français)
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
La méthode configure() définit la liste des valeurs des options grâce à l'option choices. Ce
tableau peut être redéfini afin de modifier le label associé de chaque valeur par exemple. Il n'y a
pas de limite au nombre d'options qu'un widget peut déclarer. Néanmoins, la classe de base des
widgets déclare quelques options standards qui sont donc des options réservées de-facto.
id_format : le format des identifiants, %s par défaut ;
is_hidden : valeur booléenne qui définit si le widget est un champ caché, utilisée
notamment par la méthode sfForm::renderHiddenFields() pour générer tous les
champs cachés en une seule fois ;
needs_multipart: valeur booléenne qui détermine si la balise du formulaire doit inclure
l'option multipart. Cette option est nécessaire lorsqu'il s'agit de réaliser des envois de
fichiers ;
default: la valeur par défaut qui doit être utilisée pour rendre le widget si aucune valeur
n'a été fournie ;
label: le label par défaut du widget.
La méthode render() génère le HTML correspondant pour une liste déroulante select. Elle
appelle la méthode interne renderContentTag() qui facilite la génération de balises HTML. Le
widget est désormais complet et son validateur associé peut quant à lui être défini dès à
présent.
class sfValidatorTrilean extends sfValidatorBase
{
protected function configure($options = array(), $messages = array())
{
$this->addOption('true_values', array('true', 't', 'yes', 'y', 'on', '1'));
$this->addOption('false_values', array('false', 'f', 'no', 'n', 'off', '0'));
$this->addOption('null_values', array('null', null));
}
protected function doClean($value)
{
if (in_array($value, $this->getOption('true_values')))
{
return true;
}
if (in_array($value, $this->getOption('false_values')))
{
return false;
}
if (in_array($value, $this->getOption('null_values')))
{
return null;
}
throw new sfValidatorError($this, 'invalid', array('value' => $value));
}
public function isEmpty($value)
{
return false;
}
}
Le validateur sfValidatorTrilean définit trois options dans sa méthode configure(). Chaque
option est un jeu de valeurs possibles. Comme elles sont définies comme des options, le
développeur a la possibilité de personnaliser les valeurs en fonction des spécifications
techniques.
La méthode doClean() vérifie si la valeur fournie correspond à une valeur valide, puis retourne
la bonne valeur filtrée parmi les trois. Si aucune valeur ne correspond alors la méthode lance
une exception sfValidatorError qui correspond à la classe standard pour toute les exceptions
de validation.
La dernière méthode, isEmpty(), est écrasée car le comportement par défaut est de retourner
true si null est fourni en entrée. Comme le widget en cours autorise la valeur null, cette
méthode doit toujours retourner false dans ce cas.
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
Si isEmpty() retourne true, la méthode doClean() du validateur n'est jamais appelée.
Ce premier widget était très simple à réaliser, cependant, il a introduit certaines bases
importantes pour la suite.
Le Widget Google Address Map
Dans cette partie, il s'agit d'expliquer comment créer un widget beaucoup plus complexe avec
plusieurs champs et avec des interactions JavaScript. Le widget s'appellera "GMAW" pour
"Google Map Address Widget".
Quel est le but de ce composant ? Le widget doit fournir une méthode simple pour que
l'utilisateur final puisse ajouter une adresse en utilisant un champ texte et une carte du service
"Google Map".
Cas d'utilisation 1 :
L'utilisateur saisit une adresse.
L'utilisateur clique sur le bouton "rechercher".
Les champs cachés longitude et latitude sont mis à jour et un marqueur est ajouté sur
la carte. Le marqueur est positionné sur la localisation de l'adresse. Si le service de géopositionnement ne peut trouver cette adresse alors un message d'erreur doit apparaître.
Cas d'utilisation 2 :
L'utilisateur clique sur la carte
La latitude et la longitude sont mises à jour.
Une demande d'adresse est envoyée au service de géo-positionnement.
Les champs suivants ont besoin d'être envoyés et gérés par le formulaire :
latitude : nombre à virgule flottante, entre 90 et -90 ;
longitude : nombre à virgule flottante, entre 180 et -180 ;
address : chaîne de caractères, texte seulement.
Les spécifications fonctionnelles sont maintenant définies, et voici la liste des éléments
techniques utilisés dans la suite de ce chapitre :
Services Google Maps et Geocoding : affichent la carte et récupèrent les informations
d'une adresse,
jQuery : gère les interactions javascript entre le formulaire et la carte,
sfForm : rend la carte et les champs textes.
Le Widget sfWidgetFormGMapAddress
Comme indiqué précédemment, un widget est seulement la représentation visuelle des données
à éditer, la méthode configure() de ce widget doit avoir toutes les options nécessaires afin de
configurer la carte Google ou bien pour modifier les styles de chaque élément.
L'option la plus importante est ici l'option template.html qui définit comment les éléments sont
ordonnés. Lors de la création d'un widget il est très important de prévoir de la souplesse afin de
réutiliser le composant sur d'autres pages.
Un autre point important à noter est la définition des médias externes utilisés par le widget. Là
encore, le framework de formulaires permet d'implémenter deux méthodes :
getJavascripts() : retourne un tableau de fichiers JavaScript,
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
getStylesheets() : retourne un tableau de feuilles de style où la clé du tableau est le
chemin du fichier et la valeur le type de media.
Pour fonctionner correctement, le widget a besoin de code JavaScript dans le but de gérer les
interactions entre la carte Google et les champs du widget. Le widget doit donc seulement
implémenter la méthode getJavascript().
Le widget n'est pas responsable du chargement des services Google. Il est en effet de la
responsabilité du développeur d'insérer les bonnes informations relatives à l'API Google. Le
widget n'est pas forcément le seul élément sur une page à utiliser ces services, il faut donc
découpler cette fonction du widget.
class sfWidgetFormGMapAddress extends sfWidgetForm
{
public function configure($options = array(), $attributes = array())
{
$this->addOption('address.options', array('style' => 'width:400px'));
$this->setOption('default', array(
'address' => '',
'longitude' => '2.294359',
'latitude' => '48.858205'
));
$this->addOption('div.class', 'sf-gmap-widget');
$this->addOption('map.height', '300px');
$this->addOption('map.width', '500px');
$this->addOption('map.style', "");
$this->addOption('lookup.name', "Lookup");
$this->addOption('template.html', '
<div id="{div.id}" class="{div.class}">
{input.search} <input type="submit" value="{input.lookup.name}"
id="{input.lookup.id}" /> <br />
{input.longitude}
{input.latitude}
<div id="{map.id}"
style="width:{map.width};height:{map.height};{map.style}"></div>
</div>
');
$this->addOption('template.javascript', '
<script type="text/javascript">
jQuery(window).bind("load", function() {
new sfGmapWidgetWidget({
longitude: "{input.longitude.id}",
latitude: "{input.latitude.id}",
address: "{input.address.id}",
lookup: "{input.lookup.id}",
map: "{map.id}"
});
})
</script>
');
}
public function getJavascripts()
{
return array(
'/sfFormExtraPlugin/js/sf_widget_gmap_address.js'
);
}
public function render($name, $value = null, $attributes = array(), $errors =
array())
{
// define main template variables
$template_vars = array(
'{div.id}'
=> $this->generateId($name),
'{div.class}'
=> $this->getOption('div.class'),
'{map.id}'
=> $this->generateId($name.'[map]'),
'{map.style}'
=> $this->getOption('map.style'),
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
'{map.height}'
'{map.width}'
'{input.lookup.id}'
'{input.lookup.name}'
'{input.address.id}'
'{input.latitude.id}'
'{input.longitude.id}'
=>
=>
=>
=>
=>
=>
=>
$this->getOption('map.height'),
$this->getOption('map.width'),
$this->generateId($name.'[lookup]'),
$this->getOption('lookup.name'),
$this->generateId($name.'[address]'),
$this->generateId($name.'[latitude]'),
$this->generateId($name.'[longitude]'),
);
// vérifie si la valeur est valide
$value = !is_array($value) ? array() : $value;
$value['address']
= isset($value['address'])
? $value['address'] : '';
$value['longitude'] = isset($value['longitude']) ? $value['longitude'] : '';
$value['latitude'] = isset($value['latitude']) ? $value['latitude'] : '';
// définit le widget pour le champ adresse
$address = new sfWidgetFormInputText(array(), $this>getOption('address.options'));
$template_vars['{input.search}'] = $address->render($name.'[address]',
$value['address']);
// définit les widgets pour les champs : longitude et latitude
$hidden = new sfWidgetFormInputHidden;
$template_vars['{input.longitude}'] = $hidden->render($name.'[longitude]',
$value['longitude']);
$template_vars['{input.latitude}'] = $hidden->render($name.'[latitude]',
$value['latitude']);
// assemble le modèle avec les valeurs
return strtr(
$this->getOption('template.html').$this->getOption('template.javascript'),
$template_vars
);
}
}
Le widget fait appel à la méthode generateId() pour générer le champ id de chaque élément.
La variable $name est fournie par la classe sfFormFieldSchema. Par conséquent, le nom du
widget est composé du nom du formulaire, des schémas de widgets imbriqués et finalement du
nom du champ défini par la méthode configure().
Par exemple, si le nom du formulaire est user, le widget schéma imbriqué (via un formulaire
imbriqué) est location, et le nom du champ est address. Alors le nom final du champ sera
user[location][address] et l'id sera user_location_address. Pour résumer, $this>generateId($name.'[latitude]') générera un id valide et unique pour le champ
latitude.
Les différentes valeurs des identifiants sont importantes car elles sont passées au bloc
JavaScript par l'intermédiaire de la variable template.js afin que le Javascript puisse gérer les
différents éléments du widget.
La méthode render() initialise deux widgets : sfWidgetFormInputText pour le champ texte de
l'adresse et sfWidgetFormInputHidden pour les champs cachés longitude et latitude. Le widget
peut ainsi être rapidement testé à l'aide du code ci-dessous.
$widget = new sfWidgetFormGMapAddress();
echo $widget->render('user[location][address]', array(
'address' => '151 Rue montmartre, 75002 Paris',
'longitude' => '2.294359',
'latitude' => '48.858205'
));
Le résultat obtenu est le suivant :
<div id="user_location_address" class="sf-gmap-widget">
<input style="width:400px" type="text" name="user[location][address][address]"
value="151 Rue montmartre, 75002 Paris" id="user_location_address_address" />
<input type="submit" value="Lookup" id="user_location_address_lookup" /> <br />
<input type="hidden" name="user[location][address][longitude]" value="2.294359"
id="user_location_address_longitude" />
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
<input type="hidden" name="user[location][address][latitude]" value="48.858205"
id="user_location_address_latitude" />
<div id="user_location_address_map" style="width:500px;height:300px;"></div>
</div>
<script type="text/javascript">
jQuery(window).bind("load", function() {
new sfGmapWidgetWidget({
longitude: "user_location_address_longitude",
latitude: "user_location_address_latitude",
address: "user_location_address_address",
lookup: "user_location_address_lookup",
map: "user_location_address_map"
});
})
</script>
La partie JavaScript du widget utilise les différents attributs id et les attache avec jQuery à des
écouteurs, listeners, qui seront activés à l'occasion de différentes interactions utilisateur. Le
JavaScript met à jour les différents champs tels que les valeurs de la longitude et de la latitude
ainsi que l'adresse si l'utilisateur clique dessus. L'objet JavaScript dispose de trois méthodes
intéressantes :
init() initialise les variables et les événements ;
lookupCallback(), méthode statique utilisée pour trouver la longitude et la latitude en
fonction de l'adresse ;
reverseLookupCallback(), méthode statique utilisée pour retrouver l'adresse à partir de
la longitude et de la latitude.
Le code JavaScript se trouve dans l'Annexe A. Toutes les informations complémentaires sur le
fonctionnement des services Google peuvent être trouvées sur le site officiel de Google Maps
API.
Le Validateur sfValidatorGMapAddress
Le validateur doit vérifier plusieurs points importants. Pour rappel, il est impossible de faire
confiance aux valeurs fournies par un utilisateur. C'est pourquoi le validateur doit s'assurer que
la valeur du champ est correcte. Cette dernière doit être un tableau associatif contenant une
longitude, une latitude et une adresse. C'est pour cette raison que le validateur principal
instancie d'autres validateurs auxquels il délègue la validation de chaque élément du tableau.
class sfValidatorGMapAddress extends sfValidatorBase
{
protected function doClean($value)
{
if (!is_array($value))
{
throw new sfValidatorError($this, 'invalid');
}
try
{
$latitude = new sfValidatorNumber(array( 'min' => -90, 'max' => 90, 'required'
=> true ));
$value['latitude'] = $latitude->clean(isset($value['latitude']) ?
$value['latitude'] : null);
$longitude = new sfValidatorNumber(array( 'min' => -180, 'max' => 180,
'required' => true ));
$value['longitude'] = $longitude->clean(isset($value['longitude']) ?
$value['longitude'] : null);
$address = new sfValidatorString(array( 'min_length' => 10, 'max_length' => 255,
'required' => true ));
$value['address'] = $address->clean(isset($value['address']) ? $value['address']
: null);
}
catch(sfValidatorError $e)
{
throw new sfValidatorError($this, 'invalid');
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
return $value;
}
}
En cas d'erreur, un validateur lance toujours une exception de type sfValidatorError. C'est
pour cette raison que le code de validation de chaque valeur du tableau est encapsulé dans
un bloc try/catch. Une exception globale de type invalid est ensuite retournée en cas
d'erreur sur l'un des champs.
Test du Validateur
Pourquoi tester ? Le validateur est le lien entre les valeurs saisies par l'utilisateur et
l'application. Si le validateur n'est pas fiable, alors l'application est vulnérable. Heureusement,
symfony est livré avec une librairie de test très facile à utiliser.
Comment peut-on tester un validateur ? Comme expliqué plus haut, un validateur jette une
exception en cas d'erreur. Le test doit donc injecter des valeurs correctes et invalides au
validateur et vérifier la présence de l'exception.
$t = new lime_test(7, new lime_output_color());
$tests = array(
array(false, '', 'empty value'),
array(false, 'string value', 'string value'),
array(false, array(), 'empty array'),
array(false, array('address' => 'my awesome address'), 'incomplete address'),
array(false, array('address' => 'my awesome address', 'latitude' => 'String',
'longitude' => 23), 'invalid values'),
array(false, array('address' => 'my awesome address', 'latitude' => 200,
'longitude' => 23), 'invalid values'),
array(true, array('address' => 'my awesome address', 'latitude' => '2.294359',
'longitude' => '48.858205'), 'valid value')
);
$v = new sfValidatorGMapAddress();
$t->diag("Testing sfValidatorGMapAddress");
foreach($tests as $test)
{
list($validity, $value, $message) = $test;
try
{
$v->clean($value);
$catched = false;
}
catch(sfValidatorError $e)
{
$catched = true;
}
$t->ok($validity != $catched, '::clean() '.$message);
}
Quand la méthode sfForm::bind() est appelée, la méthode clean() de chaque validateur est
alors invoquée. Par conséquent, il est facile de reproduire ce comportement en instanciant
directement le validateur sfValidatorGMapAddress.
Conclusion
L'erreur la plus courante lors de la création d'un widget réside dans le fait d'être trop concentré
sur la manière dont sont stockées les valeurs en base de données.
Cependant le framework de formulaires se comporte à la fois comme un conteneur et un
validateur de données. Par conséquent, un widget doit seulement gérer ces informations. Si les
données sont valides alors les différentes valeurs peuvent être utilisées dans un modèle de
données ou dans le contrôleur.
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Widgets et Validateurs Personnalisés | symfony | Web PHP Framework
« Les Emails
Formulaires Avancés »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/05-Custom-Widgets-and-Validators[31/12/2010 02:33:52]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Formulaires Avancés
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Ryan Weaver, Fabien Potencier
Le framework de formulaires de symfony équipe le développeur des
outils nécessaires à l'affichage et à la validation des données dans un
problème orienté objet. Grâce aux classes sfFormDoctrine et
sfFormPropel proposées par chaque ORM, le framework de formulaires
peut facilement afficher et sauvegarder des formulaires liés au modèle
de données.
Toutefois, des situations courantes demandent au développeur de
personnaliser et d'étendre des formulaires. Ce chapitre présentera et
résoudra quelques uns des problèmes complexes récurrents liés aux
formulaires. L'objet sfForm sera quant à lui disséqué afin de lever une
partie du mystère.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Mini-Projet : Produits et Photos
Le premier problème concerne l'édition d'un produit individuel et d'un nombre de photos illimité
pour ce produit. L'utilisateur doit pouvoir modifier le produit et ses photos associées sur le
même formulaire. Il s'agit de permettre à l'utilisateur d'envoyer jusqu'à deux photos du produit
à la fois. Le modèle de données ci-dessous présente une implémentation potentielle pour
résoudre ce problème.
Product:
columns:
name:
price:
ProductPhoto:
columns:
product_id:
filename:
caption:
relations:
Product:
alias:
foreignType:
foreignAlias:
onDelete:
{ type: string(255), notnull: true }
{ type: decimal, notnull: true }
{ type: integer }
{ type: string(255) }
{ type: string(255), notnull: true }
Product
many
Photos
cascade
Lorsqu'il sera terminé, le formulaire ressemblera à la capture d'écran ci-après.
Chapter Content
Mini-Projet : Produits et Photos
Apprendre par l'Exemple
Configuration de Base des
Formulaires
Imbriquer les Formulaires
Remaniement
Dissection de l'Objet sfForm
Un Formulaire est un Tableau
Dissection de la Classe ProductForm
La Méthode sfForm::embedForm() en
Coulisses
Afficher des Formulaires Imbriqués
dans la Vue
Afficher chaque Champ du Formulaire
avec sfFormField
Apprendre par l'Exemple
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
Les Méthodes de Rendu de
sfFormField
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
Le meilleur moyen d'apprendre les techniques avancées d'usage des formulaires est bien
entendu de suivre le déroulement de ce chapitre, et de tester les exemples présentés étape par
étape.
Grâce à la fonctionnalité --installer de symfony, le framework offre la possibilité de créer un
projet fonctionnel accompagné d'une base de données SQLite prête à l'emploi. Ce projet intègre
un modèle de base de données Doctrine, quelques données de test, une application frontend et
un module product pour travailler. Le script d'installation est disponible en téléchargement et
s'exécute à l'aide de la commande suivante afin de générer la base du projet symfony.
$ php symfony generate:project advanced_form -installer=/path/to/advanced_form_installer.php
Cette commande crée un projet complet et fonctionnel à partir du schéma de base de données
étudié dans la section précédente.
Rendu d'un Nouveau ProductForm
Sauvegarder des Formulaires
d'Objets
Le Processus de Sauvegarde du
Formulaire
Ignorer les Formulaires Imbriqués
Création d'un Validateur Personnalisé
Imbriquer Facilement des
Formulaires Doctrine
Les Evénements de Formulaire
Enregistrement d'Erreurs
Personnalisées via
form.validation_error
Styles Personnalisés des Erreurs
d'un Champ de Formulaire
Conclusion
Dans ce chapitre, les chemins des fichiers correspondent à un projet symfony utilisant
Doctrine dans la mesure où il a été généré par la commande précédente.
Configuration de Base des Formulaires
Puisque les besoins entraînent des changements sur deux modèles différents, Product et
ProductPhoto, la solution oblige à contenir deux formulaires symfony (ProductForm et
ProductPhotoForm). Heureusement, le framework de formulaires peut facilement combiner
plusieurs formulaires en un seul via la méthode sfForm::embedForm(). Il s'agit tout d'abord de
configurer la classe ProductPhotoForm. Dans cet exemple, c'est le champ filename qui est
utilisé comme champ d'envoi de fichiers.
// lib/form/doctrine/ProductPhotoForm.class.php
public function configure()
{
$this->useFields(array('filename', 'caption'));
$this->setWidget('filename', new sfWidgetFormInputFile());
$this->setValidator('filename', new sfValidatorFile(array(
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir').'/products',
)));
}
Pour ce formulaire, les deux champs caption et filename sont requis par défaut, mais pour des
raisons différentes. Le champ caption est obligatoire car la colonne relative en base de données
a été définie avec la propriété notnull à true. Le champ filename est quant à lui obligatoire
par défaut car un objet validateur a toujours l'option required à true par défaut.
sfForm::useFields() est une nouvelle méthode de symfony 1.3 qui permet au développeur
de spécifier exactement les champs que le formulaire devra utiliser et l'ordre dans lequel ils
seront affichés. Tous les autres champs non affichés seront retirés du formulaire.
Jusqu'à présent, rien de particulier n'a été réalisé si ce n'est une configuration ordinaire du
formulaire. Il s'agit maintenant de combiner les formulaires en un seul.
Imbriquer les Formulaires
En invoquant la méthode sfForm::embedForm(), les formulaires indépendants ProductForm et
ProductPhotoForms peuvent être combinés très facilement. Le travail est effectué dans le
formulaire principal, ProductForm dans cet exemple. Les besoins fonctionnels spécifient que
l'utilisateur final doit être capable d'envoyer jusqu'à deux photos d'un même produit à la fois.
Pour ce faire, deux objets ProductPhotoForm seront embarqués dans l'objet ProductForm.
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
$subForm = new sfForm();
for ($i = 0; $i < 2; $i++)
{
$productPhoto = new ProductPhoto();
$productPhoto->Product = $this->getObject();
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
$form = new ProductPhotoForm($productPhoto);
$subForm->embedForm($i, $form);
}
$this->embedForm('newPhotos', $subForm);
}
En accédant directement au module product depuis un navigateur, l'utilisateur a désormais la
possibilité de soumettre deux objets ProductPhoto mais également de modifier l'objet Product
lui-même. Symfony sauvegarde automatiquement les nouveaux objets ProductPhoto et les relie
à l'objet Product correspondant. L'envoi de fichiers défini dans la classe ProductPhotoForm
fonctionne lui aussi normalement.
A ce stade, il s'agit de vérifier que les enregistrements ont été correctement sauvegardés en
base de données.
$ php symfony doctrine:dql --table "FROM Product"
$ php symfony doctrine:dql --table "FROM ProductPhoto"
Il est intéressant de remarquer les noms des photos dans la table ProductPhoto. Tout
fonctionne comme prévu à condition de trouver des fichiers avec les mêmes noms que ceux de
la base de données dans le répertoire web/uploads/products/.
Etant donnés que les champs filename et caption sont requis dans ProductPhotoForm, la
validation du formulaire principal échouera tout le temps tant que l'utilisateur n'enverra pas
deux nouvelles photos. La suite de ce chapitre explique comment résoudre ce problème.
Remaniement
Bien que le formulaire précédent se comporte comme prévu, il serait néanmoins plus judicieux
de factoriser le code afin de faciliter l'écriture de tests. De plus, cette pratique permet de
réutiliser le code plus aisément.
Tout d'abord, il s'agit de créer un nouveau formulaire qui représente une collection d'objets
ProductPhotoForm en s'appuyant sur le code écrit jusqu'à maintenant.
// lib/form/doctrine/ProductPhotoCollectionForm.class.php
class ProductPhotoCollectionForm extends sfForm
{
public function configure()
{
if (!$product = $this->getOption('product'))
{
throw new InvalidArgumentException('You must provide a product object.');
}
for ($i = 0; $i < $this->getOption('size', 2); $i++)
{
$productPhoto = new ProductPhoto();
$productPhoto->Product = $product;
$form = new ProductPhotoForm($productPhoto);
$this->embedForm($i, $form);
}
}
}
Ce formulaire nécessite deux options :
product : le produit pour lequel la collection d'objets ProductPhotoForm doit être créée ;
size: le nombre d'objets ProductPhotoForm à créer, deux par défaut.
La méthode configure() de la classe ProductForm peut être alors être modifiée comme suit.
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
$form = new ProductPhotoCollectionForm(null, array(
'product' => $this->getObject(),
'size'
=> 2,
));
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
$this->embedForm('newPhotos', $form);
}
Dissection de l'Objet sfForm
En réalité, un formulaire web est une collection de champs qui sont affichés et envoyés au
serveur. Dans le même esprit, l'objet sfForm est essentiellement un tableau de champs de
formulaire. Alors que sfForm s'occupe du processus, les champs individuels sont responsables
de définir comment chacun doit s'afficher et être validé.
Dans symfony, chaque champ de formulaire est défini à l'aide de deux objets distincts :
Un widget qui affiche le code XHTML du champ ;
Un validateur qui nettoie et valide les données envoyées.
Dans symfony, un widget est défini comme n'importe quel objet dont la seule finalité est
d'afficher du code XHTML. Bien qu'ils soient couramment utilisés dans les formulaires, les
widgets peuvent être créés pour afficher n'importe quelle balise.
Un Formulaire est un Tableau
Pour rappel, un objet sfForm est essentiellement un "tableau de champs de formulaires". Pour
être plus précis, l'objet sfForm abrite un tableau de widgets et un tableau de validateurs pour
tous les champs du formulaire. Ces deux tableaux, appelés widgetSchema etvalidatorSchema,
sont des propriétés de la classe sfForm.
Pour ajouter un champ au formulaire, il suffit d'ajouter simplement le widget du champ dans le
tableau widgetSchema et le validateur du champ dans le tableau validatorSchema. Par
exemple, le code suivant déclare un champ email dans le formulaire.
public function configure()
{
$this->widgetSchema['email'] = new sfWidgetFormInputText();
$this->validatorSchema['email'] = new sfValidatorEmail();
}
Les tableaux widgetSchema et validatorSchema sont en réalité des classes spéciales
appelées sfWidgetFormSchema et sfValidatorSchema qui implémentent l'interface
ArrayAccess.
Dissection de la Classe ProductForm
Comme la classe ProductForm étend fatalement la classe sfForm, elle abrite tous les widgets et
validateurs dans les tableaux widgetSchema et validatorSchema. Le listing ci-dessous décrit
l'organisation générale de chaque tableau dans un objet ProductForm entièrement élaboré.
widgetSchema
=> array
(
[id]
=> sfWidgetFormInputHidden,
[name]
=> sfWidgetFormInputText,
[price]
=> sfWidgetFormInputText,
[newPhotos]
=> array(
[0]
=> array(
[id]
=> sfWidgetFormInputHidden,
[filename]
=> sfWidgetFormInputFile,
[caption]
=> sfWidgetFormInputText,
),
[1]
=> array(
[id]
=> sfWidgetFormInputHidden,
[filename]
=> sfWidgetFormInputFile,
[caption]
=> sfWidgetFormInputText,
),
),
)
validatorSchema => array
(
[id]
=> sfValidatorDoctrineChoice,
[name]
=> sfValidatorString,
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
[price]
=> sfValidatorNumber,
[newPhotos]
=> array(
[0]
=> array(
[id]
=> sfValidatorDoctrineChoice,
[filename]
=> sfValidatorFile,
[caption]
=> sfValidatorString,
),
[1]
=> array(
[id]
=> sfValidatorDoctrineChoice,
[filename]
=> sfValidatorFile,
[caption]
=> sfValidatorString,
),
),
)
Comme les objets widgetSchema et validatorSchema se comportent tels des tableaux, les
tableaux ci-dessus définis par les clés newPhotos, 0 et 1 sont aussi des objets
sfWidgetSchema et sfValidatorSchema.
Comme prévu, les champs basiques (id, name et price) sont représentés au premier niveau de
chaque tableau. Dans un formulaire sans formulaire imbriqué, les tableaux widgetSchema et
validatorSchema ont un seul niveau qui représente les champs de base du formulaire. Les
widgets et validateurs de n'importe quel formulaire embarqué sont représentés comme des
sous-tableaux dans widgetSchema et validatorSchema comme cela a été démontré
précédemment. La méthode qui s'occupe de ce processus est expliquée après.
La Méthode sfForm::embedForm() en Coulisses
Il convient de garder à l'esprit qu'un formulaire est composé d'un tableau de widgets et d'un
tableau de validateurs. Embarquer un formulaire dans un autre signifie essentiellement que les
tableaux des widgets et des validateurs d'un formulaire seront ajoutés dans les tableaux des
widgets et des validateurs du formulaire principal. Cette tâche est entièrement effectuée par la
méthode sfForm::embedForm(). Le résultat est toujours une addition multidimensionnelle des
tableaux widgetSchema et validatorSchema.
Maintenant, c'est au tour de la configuration de l'objet ProductPhotoCollectionForm d'être
étudiée car c'est elle qui lie les objets ProductPhotoForm. Ce formulaire du milieu agit comme
un formulaire d'adaptation et aide à son organisation. Il convient alors de commencer par
l'étude du code suivant de la méthode ProductPhotoCollectionForm::configure().
$form = new ProductPhotoForm($productPhoto);
$this->embedForm($i, $form);
Le formulaire ProductPhotoCollectionForm commence lui-même comme un nouvel objet
sfForm. En tant que tels, les tableaux widgetSchema et validatorSchema sont vides.
widgetSchema
=> array()
validatorSchema => array()
Cependant, l'objet ProductPhotoForm est déjà préparé avec trois champs (id, filename et
caption), et trois entrées correspondantes dans ses tableaux widgetSchema et
validatorSchema.
widgetSchema
=> array
(
[id]
=> sfWidgetFormInputHidden,
[filename]
=> sfWidgetFormInputFile,
[caption]
=> sfWidgetFormInputText,
)
validatorSchema => array
(
[id]
=> sfValidatorDoctrineChoice,
[filename]
=> sfValidatorFile,
[caption]
=> sfValidatorString,
)
La méthode sfForm::embedForm() ajoute simplement les tableaux widgetSchema et
validatorSchema de chaque ProductPhotoForm aux tableaux widgetSchema et
validatorSchema de l'objet ProductPhotoCollectionForm vide.
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
Une fois terminés, les tableaux widgetSchema et validatorSchema du formulaire d'adaptation
(ProductPhotoCollectionForm) deviennent des tableaux multi-dimensionnels contenant les
widgets et les validateurs des deux objets ProductPhotoForm.
widgetSchema
=> array
(
[0]
=> array
(
[id]
=> sfWidgetFormInputHidden,
[filename]
=> sfWidgetFormInputFile,
[caption]
=> sfWidgetFormInputText,
),
[1]
=> array
(
[id]
=> sfWidgetFormInputHidden,
[filename]
=> sfWidgetFormInputFile,
[caption]
=> sfWidgetFormInputText,
),
)
validatorSchema => array
(
[0]
=> array
(
[id]
=> sfValidatorDoctrineChoice,
[filename]
=> sfValidatorFile,
[caption]
=> sfValidatorString,
),
[1]
=> array
(
[id]
=> sfValidatorDoctrineChoice,
[filename]
=> sfValidatorFile,
[caption]
=> sfValidatorString,
),
)
Dans la dernière étape du processus, le formulaire d'adaptation résultant,
ProductPhotoCollectionForm est embarqué directement dans l'objet ProductForm. Cela se
produit dans la méthode ProductForm::configure() qui tire profit de tout le travail réalisé
dans l'objet ProductPhotoCollectionForm.
$form = new ProductPhotoCollectionForm(null, array(
'product' => $this->getObject(),
'size'
=> 2,
));
$this->embedForm('newPhotos', $form);
Ceci établit la dernière structure des tableaux widgetSchema et validatorSchema vus ci-dessus.
A noter que la méthode embedForm() est très proche du simple fait de la combinaison manuelle
des tableaux widgetSchema et validatorSchema.
$this->widgetSchema['newPhotos'] = $form->getWidgetSchema();
$this->validatorSchema['newPhotos'] = $form->getValidatorSchema();
Afficher des Formulaires Imbriqués dans la Vue
Le template actuel _form.php du module product ressemble sensiblement au code ci-dessous :
// apps/frontend/module/product/templates/_form.php
<!-- ... -->
<tbody>
<?php echo $form ?>
</tbody>
<!-- ... -->
La ligne <?php echo $form ?> est à la fois la façon la plus simple d'afficher un formulaire, et la
plus compliquée. Elle est d'une grande utilité lorsqu'il s'agit de réaliser un prototype. Or, dès
qu'un changement de l'agencement est nécessaire, elle doit être remplacée par un code
spécifique à l'affichage désiré. Elle peut alors être supprimée dans la mesure où elle sera de
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
toute manière modifiée dans cette section.
La chose la plus importante à comprendre lorsqu'il s'agit d'afficher un formulaire imbriqué dans
la vue est l'organisation du tableau multidimensionnel widgetSchema expliquée dans la section
précédente. Pour cet exemple, l'objectif consiste à commencer par afficher les champs de base
name et price de l'objet ProductForm dans la vue.
// apps/frontend/module/product/templates/_form.php
<?php echo $form['name']->renderRow() ?>
<?php echo $form['price']->renderRow() ?>
<?php echo $form->renderHiddenFields() ?>
Comme son nom l'indique, la méthode renderHiddenFields() génère tous les champs cachés
du formulaire.
Le code des actions n'a pas été exposé volontairement car il ne nécessite pas d'attention
particulière. Il suffit de regarder le fichier d'actions
apps/frontend/modules/product/actions/actions.class.php pour s'en persuader. Il
ressemble en effet à n'importe quel CRUD et peut être généré automatiquement à l'aide de la
tâche doctrine:generate-module.
La classe sfForm abrite désormais les tableaux widgetSchema et validatorSchema qui
définissent les champs. De plus, la classe sfForm implémente la classe native ArrayAccess de
PHP 5, ce qui signifie que les champs du formulaire sont directement accessibles par
l'intermédiaire de la syntaxe des clés de tableaux vue précédemment.
L'affichage des champs un par un nécessite d'accéder à un champ de manière unique en
appelant sa méthode renderRow(). Mais quel est le type de l'objet $form['name'] ? Alors que
la réponse se pourrait d'être le widget sfWidgetFormInputText pour le champ name, elle est en
réalité sensiblement différente.
Afficher chaque Champ du Formulaire avec sfFormField
En utilisant les tableaux widgetSchema et validatorSchema définis dans chaque classe de
formulaire, sfForm génère automatiquement un troisième tableau appelé sfFormFieldSchema.
Ce tableau contient un objet spécial pour chaque champ qui agit comme une classe helper
responsable de l'affichage du champ. L'objet, de type sfFormField, est une combinaison d'un
widget et d'un validateur pour chaque champ, et est créé automatiquement.
<?php echo $form['name']->renderRow() ?>
Dans le morceau de code précédent, $form['name'] est un objet de type sfFormField qui
abrite la méthode renderRow() avec plusieurs autres fonctions de rendu utiles.
Les Méthodes de Rendu de sfFormField
Chaque objet de type sfFormField peut être utilisé pour générer le rendu de tous les aspects
du champ qu'il représente. Par exemple, le champ lui même, le label, les messages d'erreurs
etc. Voici quelques méthodes utiles de l'objet sfFormField. Les autres peuvent être trouvées
via l'API en ligne de symfony 1.3.
sfFormField->render() génère le champ du formulaire (par exemple input, select)
avec les bonnes valeurs en utilisant l'objet widget du champ ;
sfFormField->renderError() génère toutes les erreurs de validation sur le champ en
utilisant l'objet validateur du champ ;
sfFormField->renderRow() est une méthode englobante qui affiche le label, le champ
du formulaire, l'erreur et le message d'aide.
En réalité, chaque méthode d'affichage de la classe sfFormField utilise également des
informations de la propriété widgetSchema du formulaire. C'est le cas par exemple de l'objet
sfWidgetFormSchema qui abrite tous les widgets du formulaire. Cette classe aide à la
génération des attributs name et id de chaque champ, garde une trace du label pour chaque
champ et définit la balise XHTML utilisée avec renderRow().
Il est important de noter que le tableau formFieldSchema reflète toujours la structure des
tableaux widgetSchema et validatorSchema du formulaire. Par exemple, le tableau
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
formFieldSchema d'un objet ProductForm complet aura la structure suivante, qui est la clé du
rendu de chaque champ dans la vue.
formFieldSchema
=> array
(
[id]
=> sfFormField
[name]
=> sfFormField,
[price]
=> sfFormField,
[newPhotos]
=> array(
[0]
=> array(
[id]
=> sfFormField,
[filename]
=> sfFormField,
[caption]
=> sfFormField,
),
[1]
=> array(
[id]
=> sfFormField,
[filename]
=> sfFormField,
[caption]
=> sfFormField,
),
),
)
Rendu d'un Nouveau ProductForm
En utilisant le tableau ci-dessus comme carte, il est facile d'afficher les champs du formulaire
embarqué ProductPhotoForm dans la vue en localisant et en affichant l'objet sfFormField.
// apps/frontend/module/product/templates/_form.php
<?php foreach ($form['newPhotos'] as $photo): ?>
<?php echo $photo['caption']->renderRow() ?>
<?php echo $photo['filename']->renderRow() ?>
<?php endforeach; ?>
Le bloc de code ci-dessus itère à deux reprises : une fois pour le tableau à l'index 0 et une
seconde fois pour le tableau à l'index 1. Comme l'illustrait le diagramme ci-dessus, les objets
sous-jacents de chaque tableau sont de type sfFormField, qui peuvent donc être affichés
comme n'importe quel autre champ.
Sauvegarder des Formulaires d'Objets
Dans la plupart des cas, un formulaire repose directement sur une ou plusieurs tables de la
base de données, et entraîne des changements sur les données dans ces tables en fonction des
valeurs envoyées. Symfony génère automatiquement un objet de formulaire pour chaque
modèle du schéma, qui étend soit sfFormDoctrine ou sfFormPropel en fonction de l'ORM
choisi. Chaque classe de formulaire est similaire et permet finalement aux valeurs transmises de
rester en base de données.
sfFormObject est une nouvelle classe ajoutée dans symfony 1.3 pour gérer toutes les tâches
communes de sfFormDoctrine et sfFormPropel. Chaque classe étend sfFormObject, qui
s'occupe maintenant du processus de sauvegarde du formulaire décrit ci-dessous.
Le Processus de Sauvegarde du Formulaire
Dans cet exemple, symfony sauvegarde automatiquement les informations de l'objet Product et
des nouveaux objets ProductPhoto sans autre intervention du développeur. C'est la méthode
sfFormObject::save() qui exécute une multitude de méthodes en arrière plan. La
compréhension de ce processus est la clé pour étendre ce traitement à des cas plus complexes.
Le processus de sauvegarde du formulaire est une suite de méthodes exécutées en interne, qui
se déclenchent après l'appel de la méthode sfFormObject::save(). La majorité du travail est
située dans la méthode sfFormObject::updateObject() qui est appelée récursivement sur tous
les formulaires imbriqués.
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
La majorité du processus de sauvegarde intervient dans la méthode
sfFormObject::doSave(), qui est appelée par sfFormObject::save() et entourée par une
transaction. Si le processus de sauvegarde lui-même doit être surchargé, c'est alors dans la
méthode sfFormObject::doSave() que ce travail doit être réalisé.
Ignorer les Formulaires Imbriqués
L'implémentation actuelle de ProductForm a un inconvénient majeur. Etant donnés que les
champs filename et caption sont nécessaires dans ProductPhotoForm, la validation du
formulaire principal échouera à chaque fois tant que l'utilisateur n'enverra pas deux nouvelles
photos. En d'autres termes, l'utilisateur ne peut alors changer le prix du produit sans envoyer
deux nouvelles photos.
Les champs obligatoires du formulaire doivent être redéfinis afin d'inclure les suivants. Si
l'utilisateur laisse vides tous les champs du formulaire ProductPhotoForm, ce formulaire sera
alors complètement ignoré. Cependant, si au moins un champ possède des données (par
exemple caption ou filename), le formulaire devra être validé et sauvegardé normalement.
Pour ce faire, le formulaire a besoin d'une technique avancée nécessitant l'utilisation d'un post
validateur personnalisé.
La première étape consiste à modifier le formulaire ProductPhotoForm afin de rendre les
champs caption et filename optionnels.
// lib/form/doctrine/ProductPhotoForm.class.php
public function configure()
{
$this->setValidator('filename', new sfValidatorFile(array(
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir').'/products',
'required' => false,
)));
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
$this->validatorSchema['caption']->setOption('required', false);
}
Dans le code ci-dessus, la valeur de l'option required a été modifiée à false, en surchargeant
la valeur par défaut du validateur pour le champ filename. De plus, la valeur de l'option
required du champ caption a été explicitement configurée à false.
Le code ci-dessous se charge ensuite d'ajouter un post validateur à l'objet
ProductPhotoCollectionForm.
// lib/form/doctrine/ProductPhotoCollectionForm.class.php
public function configure()
{
// ...
$this->mergePostValidator(new ProductPhotoValidatorSchema());
}
Un post validateur est un type de validateur particulier qui exécute une validation sur toutes les
données soumises après le processus de validation classique. Il s'oppose à la validation valeur
par valeur de chaque champ. L'un des post validateurs les plus courants est
sfValidatorSchemaCompare qui vérifie, par exemple, que la valeur d'un certain champ est
inférieure à celle d'un autre.
Création d'un Validateur Personnalisé
Heureusement, la création d'un validateur personnalisé est en fait simple. Il suffit de créer un
nouveau fichier ProductPhotoValidatorSchema.class.php et de le placer dans le répertoire
lib/validator. La création de ce répertoire est à la charge du lecteur.
// lib/validator/ProductPhotoValidatorSchema.class.php
class ProductPhotoValidatorSchema extends sfValidatorSchema
{
protected function configure($options = array(), $messages = array())
{
$this->addMessage('caption', 'The caption is required.');
$this->addMessage('filename', 'The filename is required.');
}
protected function doClean($values)
{
$errorSchema = new sfValidatorErrorSchema($this);
foreach($values as $key => $value)
{
$errorSchemaLocal = new sfValidatorErrorSchema($this);
// filename is filled but no caption
if ($value['filename'] && !$value['caption'])
{
$errorSchemaLocal->addError(new sfValidatorError($this, 'required'),
'caption');
}
// caption is filled but no filename
if ($value['caption'] && !$value['filename'])
{
$errorSchemaLocal->addError(new sfValidatorError($this, 'required'),
'filename');
}
// no caption and no filename, remove the empty values
if (!$value['filename'] && !$value['caption'])
{
unset($values[$key]);
}
// some error for this embedded-form
if (count($errorSchemaLocal))
{
$errorSchema->addError($errorSchemaLocal, (string) $key);
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
}
}
// throws the error for the main form
if (count($errorSchema))
{
throw new sfValidatorErrorSchema($this, $errorSchema);
}
return $values;
}
}
Tous les validateurs étendent la classe abstraite sfValidatorBase qui les oblige à
implémenter la méthode doClean(), déclarée abstraite. La méthode configure() peut
également être utilisée pour ajouter des options ou messages d'erreur au validateur. Dans
l'exemple précédent, deux messages ont été ajoutés au validateur. D'autres options peuvent
également être définies à l'aide de la méthode addOption().
La méthode doClean() est responsable du nettoyage et de la validation des valeurs envoyées.
La logique du validateur est quant à elle triviale.
Si une photo est envoyée uniquement avec un fichier ou une légende, alors une erreur
est générée (sfValidatorErrorSchema) avec le message approprié ;
Si une photo est soumise sans fichier et sans légende, alors les valeurs sont supprimées
afin d'éviter de sauvegarder une photo vide ;
Si aucune erreur de validation n'a été produite, la méthode retourne un tableau de
valeurs nettoyées.
Dans cette situation, étant donné que le validateur personnalisé doit être utilisé comme un
validateur global, la méthode doClean() attend un tableau des valeurs soumises et retourne
un tableau des valeurs nettoyées. Cependant les validateurs personnalisés peuvent être créés
pour des champs individuels. Dans ce cas, la méthode doClean() n'attendra qu'une seule
valeur (la valeur du champ) et ne retournera qu'une seule valeur nettoyée.
La dernière étape consiste à surcharger la méthode saveEmbeddedForms() de la classe
ProductForm afin de supprimer les formulaires de photos vides, et ainsi éviter de sauvegarder
une photo vide en base de données. Une exception serait en effet levée car le champ caption
est requis.
public function saveEmbeddedForms($con = null, $forms = null)
{
if (null === $forms)
{
$photos = $this->getValue('newPhotos');
$forms = $this->embeddedForms;
foreach ($this->embeddedForms['newPhotos'] as $name => $form)
{
if (!isset($photos[$name]))
{
unset($forms['newPhotos'][$name]);
}
}
}
return parent::saveEmbeddedForms($con, $forms);
}
Imbriquer Facilement des Formulaires Doctrine
Une nouveauté de symfony 1.3 est la méthode sfFormDoctrine::embedRelation() qui offre au
développeur la possibilité d'imbriquer automatiquement des relations n-à-plusieurs dans un
formulaire. Dans l'exemple de ce chapitre, il serait alors intéressant de permettre à l'utilisateur
de pouvoir à la fois télécharger deux nouvelles photos, mais aussi de le rendre capable de
modifier les objets ProductPhoto existants rattachés à l'objet Product.
Pour ce faire, il suffit d'utiliser la méthode embedRelation() afin d'ajouter un objet
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
ProductPhotoForm additionnel pour chaque objet ProductPhoto existant.
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
// ...
$this->embedRelation('Photos');
}
En interne, sfFormDoctrine::embedRelation() fait quasiment la même chose que le processus
décrit plus tôt pour imbriquer deux nouveaux objets ProductPhotoForm. Si deux relations
ProductPhoto existent déjà, alors les objets widgetSchema et validatorSchema résultants
seront de la forme suivante.
widgetSchema
(
[id]
[name]
[price]
[newPhotos]
[Photos]
[0]
[id]
[caption]
),
[1]
[id]
[caption]
),
),
)
validatorSchema
(
[id]
[name]
[price]
[newPhotos]
[Photos]
[0]
[id]
[caption]
),
[1]
[id]
[caption]
),
),
)
=> array
=>
=>
=>
=>
=>
sfWidgetFormInputHidden,
sfWidgetFormInputText,
sfWidgetFormInputText,
array(...)
array(
=> array(
=> sfWidgetFormInputHidden,
=> sfWidgetFormInputText,
=> array(
=> sfWidgetFormInputHidden,
=> sfWidgetFormInputText,
=> array
=>
=>
=>
=>
=>
sfValidatorDoctrineChoice,
sfValidatorString,
sfValidatorNumber,
array(...)
array(
=> array(
=> sfValidatorDoctrineChoice,
=> sfValidatorString,
=> array(
=> sfValidatorDoctrineChoice,
=> sfValidatorString,
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
L'étape qui suit consiste à ajouter du code dans la vue pour afficher les formulaires Photo
imbriqués.
// apps/frontend/module/product/templates/_form.php
<?php foreach ($form['Photos'] as $photo): ?>
<?php echo $photo['caption']->renderRow() ?>
<?php echo $photo['filename']->renderRow(array('width' => 100)) ?>
<?php endforeach; ?>
Ce morceau de code est exactement le même que celui qui a été utilisé plus tôt pour embarquer
les nouveaux formulaires de photos. Enfin, la dernière étape consiste à modifier le champ
d'envoi de fichier par un widget qui permet à l'utilisateur de visualiser la photo courante et de la
remplacer par une nouvelle (sfWidgetFormInputFileEditable).
public function configure()
{
$this->useFields(array('filename', 'caption'));
$this->setValidator('filename', new sfValidatorFile(array(
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir').'/products',
'required' => false,
)));
$this->setWidget('filename', new sfWidgetFormInputFileEditable(array(
'file_src'
=> '/uploads/products/'.$this->getObject()->filename,
'edit_mode'
=> !$this->isNew(),
'is_image'
=> true,
'with_delete' => false,
)));
$this->validatorSchema['caption']->setOption('required', false);
}
Les Evénements de Formulaire
Une autre nouveauté de symfony 1.3 sont les évènements de formulaires qui peuvent être
utilisés pour étendre n'importe quel objet de formulaire de n'importe où dans le code. Symfony
propose les quatre évènements de formulaire suivants par défaut.
form.post_configure est notifié après chaque configuration de formulaire ;
form.filter_values filtre les paramètres fusionnés teintés et les tableaux de fichiers
juste avant l'association avec le formulaire ;
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
form.validation_error est notifié dès que la validation du formulaire échoue ;
form.method_not_found est notifié dès qu'une méthode inconnue est appelée.
Enregistrement d'Erreurs Personnalisées via form.validation_error
En utilisant les évènements de formulaires, il est possible d'ajouter des logs personnalisés pour
les erreurs de validation sur tous les formulaires du projet. Ces outils peuvent s'avérer utiles
pour identifier les champs des formulaires qui entraînent des conflits pour les utilisateurs.
Pour ce faire, il convient d'enregistrer un nouvel écouteur à partir de l'expéditeur d'événements,
event dispatcher, pour l'événement form.validation_error en ajoutant le code suivant à la
méthode setup() de la classe ProjectConfiguration. Cette dernière se trouve à l'intérieur du
répertoire config/ du projet.
public function setup()
{
// ...
$this->getEventDispatcher()->connect(
'form.validation_error',
array('BaseForm', 'listenToValidationError')
);
}
La classe BaseForm, qui se trouve dans le répertoire lib/form, est une classe spéciale de
formulaires dont toutes les autres classes de formulaire héritent. BaseForm est essentiellement
une classe utilitaire servant à partager du code et de la logique métier communs à tous les
objets de formulaire du projet. Pour activer le log des erreurs de validation, il suffit simplement
d'ajouter le code suivant à la classe BaseForm.
public static function listenToValidationError($event)
{
foreach ($event['error'] as $key => $error)
{
self::getEventDispatcher()->notify(new sfEvent(
$event->getSubject(),
'application.log',
array (
'priority' => sfLogger::NOTICE,
sprintf('Validation Error: %s: %s', $key, (string) $error)
)
));
}
}
Styles Personnalisés des Erreurs d'un Champ de Formulaire
En guise de dernier exercice, il s'agit d'aborder un sujet légèrement plus sobre concernant la
personnalisation des éléments du formulaire. C'est tout à fait le cas, par exemple, lorsqu'il s'agit
d'appliquer un style spécial au design de la page Product pour tous les champs du formulaire
dont la validation a échoué.
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
Si l'on admet que le designer a déjà implémenté les feuilles de styles qui permettent d'appliquer
un style d'erreur personnalisé à n'importe quel champ input dans une div avec la classe
form_error_row. Comment ajouter simplement la classe form_row_error aux champs erronés ?
La réponse se trouve dans un objet spécial appelé form schema formatter. Chaque formulaire
symfony utilise un form schema formatter pour déterminer le code HTML adéquat à utiliser lors
de l'affichage des éléments du formulaire. Par défaut, symfony utilise un formateur de
formulaire qui s'appuie sur les balises HTML <table>.
Tout d'abord, il s'agit de créer une nouvelle classe de formatage de formulaire qui utilise juste
quelques balises pour l'affichage du formulaire. Pour ce faire, il convient de créer un nouveau
fichier sfWidgetFormSchemaFormatterAc2009.class.php dans le répertoire lib/widget/. La
création de ce dernier est à la charge du lecteur.
class sfWidgetFormSchemaFormatterAc2009 extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat
= "<div class="form_row">
%label% \n %error% <br/> %field%
%help% %hidden_fields%\n</div>\n",
$errorRowFormat = "<div>%errors%</div>",
$helpFormat
= '<div class="form_help">%help%</div>',
$decoratorFormat = "<div>\n %content%</div>";
}
Bien que le format de cette classe paraisse étrange, l'idée générale est que la méthode
renderRow() fasse usage de la variable $rowFormat afin de procéder à l'affichage. Une classe
de formatage de formulaire offre d'autres options de formatage qui ne sont pas détaillées ici.
Pour plus d'informations à ce sujet, l'API de symfony 1.3 est disponible.
Ajouter le code suivant à la classe ProjectConfiguration suffit à utiliser le nouveau formateur
de formulaires dans tous les objets de formulaire du projet.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
// ...
sfWidgetFormSchema::setDefaultFormFormatterName('ac2009');
}
}
L'objectif est ici d'attribuer une classe form_row_error à l'élément div form_row seulement si
un champ échoue à la validation. Pour ce faire, il suffit d'inclure un jeton %row_class% à la
propriété $rowFormat, puis de surcharger la méthode
sfWidgetFormSchemaFormatter::formatRow() comme suit.
class sfWidgetFormSchemaFormatterAc2009 extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat
= "<div class="form_row%row_class%">
%label% \n %error% <br/> %field%
%help% %hidden_fields%\n</div>\n",
// ...
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Formulaires Avancés | symfony | Web PHP Framework
public function formatRow($label, $field, $errors = array(), $help = '',
$hiddenFields = null)
{
$row = parent::formatRow(
$label,
$field,
$errors,
$help,
$hiddenFields
);
return strtr($row, array(
'%row_class%' => (count($errors) > 0) ? ' form_row_error' : '',
));
}
}
Avec ce code, chaque élément affiché via la méthode renderRow() sera automatiquement
décoré d'une balise div avec une classe form_row_error si la validation du champ échoue.
Conclusion
Le framework de formulaires est à la fois le composant le plus puissant et le plus complexe de
symfony. Le compromis pour une validation minutieuse, une protection CSRF, et les objets de
formulaire peut très vite s'avérer être une tâche redoutable lorsqu'il s'agit d'étendre le
framework.
En revanche, la connaissance en profondeur du système de formulaires est la clé pour révéler
tout son potentiel. Les développements futurs du framework de formulaires se focaliseront sur la
conservation de la puissance de cet outil, et sur la réduction de la complexité en offrant plus de
flexibilité au développeur. Le framework de formulaires n'en est finalement qu'à ses débuts...
« Widgets et Validateurs Personnalisés
Etendre la Web Debug Toolbar »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms[31/12/2010 02:33:56]
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Etendre la Web Debug Toolbar
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Ryan Weaver
La barre de débogage web, web debug toolbar (WDT), de symfony
regroupe des outils qui permettent de déboguer et d'améliorer les
performances d'une application. Elle est constituée d'outils appelés
panneaux de débogage web, web debug panels, chacun donnant des
informations relatives au cache, à la configuration, aux fichiers de logs,
à la consommation mémoire, au temps d'exécution ou bien encore
concernant la version de symfony. Symfony 1.3 introduit deux
nouveaux panneaux, un pour la vue et l'autre pour la gestion des
emails.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Depuis symfony 1.2 il est possible de créer et d'ajouter ses propres panneaux de débogage web.
Tout au long de ce chapitre, il s'agira de créer un nouvel onglet de débogage en étudiant par la
même occasion les outils et les options qui permettent de le personnaliser. Il est de plus
possible de se référer au plugin ac2009WebDebugPlugin qui contient de nombreux panneaux
supplémentaires s'appuyant sur les techniques expliquées dans la suite de ce chapitre.
Créer un Nouveau Panneau de Débogage Web
Les composants de la barre de débogage web sont appelés web debug panels, ou bien panneaux
de débogage web pour les puristes francophones. Ils étendent la classe sfWebDebugPanel.
Ajouter un nouveau panel est particulièrement simple puisqu'il s'agit tout d'abord de créer un
nouveau fichier sfWebDebugPanelDocumentation.class.php dans le dossier lib/debug/ du
projet. Le répertoire lib/debug doit être créé manuellement.
// lib/debug/sfWebDebugPanelDocumentation.class.php
class acWebDebugPanelDocumentation extends sfWebDebugPanel
{
public function getTitle()
{
return '<img src="/images/documentation.png" alt="Documentation Shortcuts"
height="16" width="16" /> docs';
}
public function getPanelTitle()
{
return 'Documentation';
}
public function getPanelContent()
{
$content = 'Placeholder Panel Content';
return $content;
}
}
Tous les panneaux doivent implémenter au minimum les méthodes getTitle(),
getPanelTitle() et getPanelContent().
sfWebDebugPanel::getTitle() définit l'apparence du panneau dans la barre de
débogage. Il s'agit généralement d'un nom court accompagné d'une icône.
sfWebDebugPanel::getPanelTitle() définit le nom du panneau qui est affiché dans le
tag h1 de l'onglet ouvert. Il sert aussi d'attribut title au lien de la barre de débogage.
Cette méthode ne doit pas retourner de code HTML.
sfWebDebugPanel::getPanelContent() génère le code HTML affiché lorsque l'onglet est
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
Chapter Content
Créer un Nouveau Panneau de
Débogage Web
Les Trois Types de Panneaux de
Débogage Web
Le Type Icon-Only
Le Type Link
Le Type Content
Personnaliser le Contenu d'un
Onglet
La Méthode
sfWebDebugPanel::setStatus()
La Méthode
sfWebDebugPanel::getToggler()
La Méthode
sfWebDebugPanel::getToggleableDebugStack()
La Méthode
sfWebDebugPanel::formatFileLink()
Autres Informations à Connaître
au Sujet de la WDT
Enlever les Onglets par Défaut
Accéder aux Paramètres de la
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
ouvert.
Pour finir cette implémentation, il ne reste plus qu'à informer l'application que l'on souhaite
inclure ce nouvel onglet à la barre de débogage. Pour ce faire, il est nécessaire d'ajouter un
nouvel écouteur sur l'événement debug.web.load_panels. Cet événement est notifié lorsque la
barre de débogage recherche ses panneaux. Il suffit donc de modifier le contenu du fichier
config/ProjectConfiguration.class.php afin de lui faire implémenter cet écouteur.
// config/ProjectConfiguration.class.php
public function setup()
{
// ...
$this->dispatcher->connect('debug.web.load_panels', array(
'acWebDebugPanelDocumentation',
'listenToLoadDebugWebPanelEvent'
));
}
L'étape suivante consiste à ajouter la méthode listenToLoadDebugWebPanelEvent() à la classe
acWebDebugPanelDocumentation.class.php afin d'ajouter le nouvel onglet dans la base
d'outils. Cette méthode sera appelée lorsque l'évènement debug.web.load_panels sera notifié.
// lib/debug/sfWebDebugPanelDocumentation.class.php
public static function listenToLoadDebugWebPanelEvent(sfEvent $event)
{
$event->getSubject()->setPanel(
'documentation',
new self($event->getSubject())
);
}
Voilà ! Il ne reste plus qu'à rafraîchir le navigateur et apprécier le résultat.
Depuis symfony 1.3, un paramètre sfWebDebugPanel peut être ajouté à l'URL d'une page
pour charger automatiquement un panneau de débogage. Si l'on ajoute par exemple la
chaîne ?sfWebDebugPanel=documentation à la fin de l'URL, le nouveau panneau sera ajouté
à la page. C'est particulièrement utile lorsqu'il s'agit de développer des panneaux
personnalisés.
Les Trois Types de Panneaux de Débogage Web
En réalité, il existe trois différents types de panneaux de débogage web. Les lignes qui suivent
les décrivent les uns après les autres afin de mieux comprendre les intérêts de chacun.
Le Type Icon-Only
Ce type de panneau se contente d'afficher une icône et du texte dans la barre d'outil. C'est
typiquement le cas du panneau memory qui affiche la mémoire consommée sans aucun lien
supplémentaire. Pour créer un panel de type icon-only, la méthode getPanelContent() doit
retourner une chaine vide. La seule sortie de l'onglet provient en réalité de la méthode
getTitle().
public function getTitle()
{
$totalMemory = sprintf('%.1f', (memory_get_peak_usage(true) / 1024));
return '<img src="'.$this->webDebug->getOption('image_root_path').'/memory.png"
alt="Memory" /> '.$totalMemory.' KB';
}
public function getPanelContent()
{
return;
}
Le Type Link
Au même titre que le panneau de type Icon-Only, l'onglet de type link n'a pas de contenu, mais
dispose cependant d'un lien supplémentaire. L'URL de ce lien est défini par la méthode
getTitleUrl().
Pour créer un panneau de type link, la méthode getPanelContent() doit retourner une chaine
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
Requête depuis un Onglet
Masquer le Panneau sous Certaines
Conditions
Conclusion
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
vide tandis que la méthode getTitleUrl() doit être ajoutée à la classe comme le montre
l'exemple ci-dessous.
public function getTitleUrl()
{
// link to an external uri
return 'http://www.symfony-project.org/api/1_3/';
// or link to a route in your application
return url_for('homepage');
}
public function getPanelContent()
{
return;
}
Le Type Content
Enfin, le panneau de type content est le plus courant de tous puisqu'il s'agit d'un onglet qui
affiche un bloc de code HTML lorsque l'on clique dessus dans la barre de débogage web.
Pour créer ce type de panneau de contenu, la méthode getPanelContent() doit retourner autre
chose qu'une chaine vide.
Personnaliser le Contenu d'un Onglet
Maintenant que le nouveau panneau de contrôle a été ajouté à la barre de débogage de
symfony, il convient de lui attribuer du contenu à l'aide de la méthode getPannelContent().
Symfony fournit plusieurs méthodes capables de rendre ce contenu à la fois riche et
ergonomique.
La Méthode sfWebDebugPanel::setStatus()
La couleur de fond des onglets est grise par défaut. Elle peut néanmoins être modifiée par de
l'orange ou bien par du rouge lorsqu'il s'agit d'attirer l'attention du développeur sur un élément
précis du panneau.
Pour changer la couleur de fond du panel, la solution consiste à utiliser la méthode
setStatus(). Cette méthode accepte toutes les constantes priority de la classe sfLogger.
Il existe en particulier trois niveaux qui correspondent respectivement aux trois couleurs de fond
que peut prendre le panel (gris, orange et rouge). La méthode setStatus() est généralement
appelée depuis la méthode getPanelContent() si certains événements nécessitent d'attirer
l'attention du développeur.
public function getPanelContent()
{
// ...
// set the background to gray (the default)
$this->setStatus(sfLogger::INFO);
// set the background to orange
$this->setStatus(sfLogger::WARNING);
// set the background to red
$this->setStatus(sfLogger::ERR);
}
La Méthode sfWebDebugPanel::getToggler()
Il existe une fonction fréquemment rencontrée dans les panneaux de contrôle. Il s'agit du
toggler. C'est une croix qui affiche ou cache alternativement du contenu lorsque l'on clique
dessus. On pourrait généraliser son comportement à celui d'un interrupteur domestique qui
allume ou éteint une ampoule par exemple.
Le toggler peut être utilisé par le panneau de contrôle en invoquant la méthode getToggler().
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
Le code ci-dessous explique comment appliquer le toggler sur une liste d'éléments contenus
dans l'onglet de contrôle.
public function getPanelContent()
{
$listContent = '<ul id="debug_documentation_list" style="display: none;">
<li>List Item 1</li>
<li>List Item 2</li>
</ul>';
$toggler = $this->getToggler('debug_documentation_list', 'Toggle list');
return sprintf('<h3>List Items %s</h3>%s', $toggler, $listContent);
}
Cette méthode getToggler() accepte deux arguments : l'identifiant DOM, id, de l'élément sur
lequel doit être appliqué le toggler, et une chaîne pour l'attribut title du lien du toggler.
La Méthode sfWebDebugPanel::getToggleableDebugStack()
Au même titre que la méthode getToggler(), la méthode getToggleableDebugStack() crée
une flèche cliquable qui affiche ou masque un élément du contenu. Cette méthode génère le
contenu HTML d'une trace de débogage d'une pile d'appels de fonctions.
Cette fonction est particulièrement utile lorsqu'il s'agit d'afficher les logs d'une des classes du
projet. Par exemple, si une classe myCustomClass a besoin d'enregistrer des informations dans
les logs, alors ces derniers seraient créés de la manière suivante depuis l'intérieur de cette
classe.
class myCustomClass
{
public function doSomething()
{
$dispatcher = sfApplicationConfiguration::getActive()
->getEventDispatcher();
$dispatcher->notify(new sfEvent($this, 'application.log', array(
'priority' => sfLogger::INFO,
'Beginning execution of myCustomClass::doSomething()',
)));
}
}
Pour l'exemple de ce chapitre, il s'agit d'afficher une liste des logs de la classe myCustomClass
en accompagnant chacun d'eux par sa propre trace de débogage de la pile d'appels.
public function getPanelContent()
{
// retrieves all of the log messages for the current request
$logs = $this->webDebug->getLogger()->getLogs();
$logList = '';
foreach ($logs as $log)
{
if ($log['type'] == 'myCustomClass')
{
$logList .= sprintf('<li>%s %s</li>',
$log['message'],
$this->getToggleableDebugStack($log['debug_backtrace'])
);
}
}
return sprintf('<ul>%s</ul>', $logList);
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
Les logs de la classe myCustomClass se trouvent toujours dans le panneau Logs. Utiliser un
onglet personnalisé permet ainsi de les isoler et de les présenter autrement.
La Méthode sfWebDebugPanel::formatFileLink()
Depuis symfony 1.3, il est possible d'ouvrir un fichier dans son éditeur favoris depuis la barre de
débogage web. Davantage d'informations concernant cette fonctionnalité sont disponibles sur
Internet, il suffit pour cela de se référer à l'article "What's new" de symfony 1.3.
Pour bénéficier de cette fonctionnalité, il faut impérativement faire appel à la méthode
formatFileLink(). En plus du nom du fichier à ouvrir, cette méthode peut aussi recevoir
comme second paramètre le numéro de la ligne incriminée à atteindre. L'exemple suivant
montre comment atteindre la ligne 15 du fichier config/ProjectConfiguration.class.php:
public function getPanelContent()
{
$content = '';
// ...
$path = sfConfig::get('sf_config_dir') . '/ProjectConfiguration.class.php';
$content .= $this->formatFileLink($path, 15, 'Project Configuration');
return $content;
}
Les deuxième et troisième arguments, sont respectivement le numéro de la ligne et le nom du
lien, et sont également optionnels. Si le nom du lien n'est pas renseigné, c'est le chemin du
fichier ciblé qui sera utilisé à la place.
Avant de tester, il convient de s'assurer de bien avoir configuré la nouvelle fonctionnalité de
lien de fichier. Cela peut être réalisé à l'aide de l'option sf_file_link_format dans le fichier
settings.yml ou bien avec l'option file_link_format de xdebug. Cette dernière méthode
permet aussi à votre projet de ne pas dépendre d'un IDE.
Autres Informations à Connaître au Sujet de la WDT
L'intérêt de la barre de débogage réside dans les informations qu'elle affiche. Il s'agit
maintenant d'étudier les autres possibilités offertes par la barre de débogage web de symfony.
Enlever les Onglets par Défaut
La barre de débogage charge par défaut plusieurs panneaux qui peuvent être enlevés en
utilisant l'événement debug.web.load_panels. Il s'agit ici de faire appel à la même méthode
écouteur déclarée plus haut, en remplaçant son contenu par la méthode removePanel(). Le
code suivant supprime l'onglet memory de la barre de débogage web.
static public function listenToLoadDebugWebPanelEvent(sfEvent $event)
{
$event->getSubject()->removePanel('memory');
}
Accéder aux Paramètres de la Requête depuis un Onglet
Les paramètres de la requête sont souvent nécessaires dans les onglets. Il convient donc
maintenant d'afficher des informations concernant un objet Event provenant de la base de
données à partir d'un paramètre de requête event_id.
$parameters = $this->webDebug->getOption('request_parameters');
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
The More with symfony book | Etendre la Web Debug Toolbar | symfony | Web PHP Framework
if (isset($parameters['event_id']))
{
$event = Doctrine::getTable('Event')->find($parameters['event_id']);
}
Masquer le Panneau sous Certaines Conditions
Parfois, l'onglet ne dispose pas d'informations utiles à afficher. Dans ce cas, il est préférable de
le masquer. Pour mieux comprendre comment mettre cela en place, le code suivant s'appuiera
sur l'exemple précédent. Il s'agit de masquer le panneau de contrôle si aucun paramètre
event_id n'a été transmis dans la requête. Pour ce faire, la méthode getTitle() doit retourner
une chaine vide.
public function getTitle()
{
$parameters = $this->webDebug->getOption('request_parameters');
if (!isset($parameters['event_id']))
{
return;
}
return '<img src="/acWebDebugPlugin/images/documentation.png" alt="Documentation
Shortcuts" height="16" width="16" /> docs';
}
Conclusion
La barre de débogage de symfony est là pour faciliter la vie du développeur. Elle est aussi bien
plus qu'un simple panneau d'informations passif puisqu'en lui ajoutant des onglets personnalisés,
les fonctionnalités de la barre d'outils ne seront limitées que par l'imagination du développeur.
Le plugin ac2009WebDebugPlugin ne contient qu'un aperçu des panneaux pouvant être créés,
alors n'hésitez pas à ajouter les vôtres également.
« Formulaires Avancés
Techniques Avancées avec Doctrine »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/07-Extending-the-Web-Debug-Toolbar[31/12/2010 02:34:02]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Techniques Avancées avec Doctrine
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Jonathan H. Wage
Ecrire un Comportement Doctrine
L'objectif de ce chapitre est de découvrir comment écrire un
comportement (behavior) pour Doctrine 1.2. Il s'agira de créer un
exemple simple qui permet de maintenir à jour un compteur de
relations en cache dans un champ d'une table. Cette fonctionnalité
permettra ainsi d'éviter d'avoir à demander le résultat d'un
dénombrement à chaque appel d'une méthode en réalisant des requêtes
supplémentaires.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Ce type de fonctionnalité est relativement simple à mettre en oeuvre.
Pour chaque relation dont on souhaite conserver à jour le résultat d'un
compteur, le comportement se chargera d'ajouter une colonne
supplémentaire au modèle afin de stocker la valeur courante du
dénombrement.
Le Schéma de Données
Le listing ci-dessous décrit le modèle de données utilisé pour commencer. Puis, il sera modifié
au fil du chapitre afin d'enregistrer le comportement développé dans la section actAs du
modèle.
# config/doctrine/schema.yml
Thread:
columns:
title:
type: string(255)
notnull: true
Post:
columns:
thread_id:
type: integer
notnull: true
body:
type: clob
notnull: true
relations:
Thread:
onDelete: CASCADE
foreignAlias: Posts
Le schéma de données établi, il ne reste plus qu'à construire tout le modèle de données associé
à l'aide de la tâche doctrine:build.
$ php symfony doctrine:build --all
Le Template Doctrine
La première étape consiste à écrire une classe basique qui étend la classe Doctrine_Template.
Cette classe sera responsable de l'ajout de la colonne qui stocke la valeur courante du compteur
dans la classe de modèle associée. Pour ce faire, il suffit de créer le fichier
CountCache.class.php dans l'un des répertoires lib/ du projet afin que symfony puisse le
charger automatiquement.
// lib/count_cache/CountCache.class.php
class CountCache extends Doctrine_Template
{
public function setTableDefinition()
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
Chapter Content
Ecrire un Comportement Doctrine
Le Schéma de Données
Le Template Doctrine
Ecouter les Evénements
Tester le Comportement
Utiliser le Cache des Résultats
Doctrine
Le Modèle de Données
Configurer le Cache de Résultats
Exemples de Requêtes
Supprimer le Cache
Supprimer des Entrées du Cache à
l'Aide des Evénements
Développer un Hydrator Doctrine
Le Modèle de Données et les Données
de Test
Développer l'Hydrator
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
{
}
Utiliser l'Hydrator
public function setUp()
{
}
}
A présent, il est temps de modifier la définition du modèle Post afin que l'objet implémente
(actAs) le comportement CountCache.
# config/doctrine/schema.yml
Post:
actAs:
CountCache: ~
# ...
Le modèle Post est désormais prêt à utiliser le comportement CountCache, bien que quelques
explications complémentaires à son sujet soient les bienvenues. Dès lors que la définition du
modèle est instanciée, n'importe quel comportement attaché à ce dernier voit ses méthodes
setTableDefinition() et setUp() invoquées comme celles qui se trouvent dans la classe
BasePost du fichier lib/model/doctrine/base/BasePost.class.php. Ce mécanisme permet
d'ajouter des choses supplémentaires à n'importe quel modèle à la manière "plug and play". Ces
dernières peuvent être aussi bien des colonnes, des relations, des écouteurs d'évènements, etc.
Le fonctionnement général des comportements a été éclairci. Par conséquent, il convient de faire
en sorte que le comportement CountCache satisfasse réellement un besoin fonctionnel.
class CountCache extends Doctrine_Template
{
protected $_options = array(
'relations' => array()
);
public function setTableDefinition()
{
foreach ($this->_options['relations'] as $relation => $options)
{
// Build column name if one is not given
if (!isset($options['columnName']))
{
$this->_options['relations'][$relation]['columnName'] =
'num_'.Doctrine_Inflector::tableize($relation);
}
// Add the column to the related model
$columnName = $this->_options['relations'][$relation]['columnName'];
$relatedTable = $this->_table->getRelation($relation)->getTable();
$this->_options['relations'][$relation]['className'] = $relatedTable>getOption('name');
$relatedTable->setColumn($columnName, 'integer', null, array('default' => 0));
}
}
}
Le code ci-dessus ajoute désormais des colonnes pour maintenir à jour le compteur du modèle
associé. Ainsi, dans l'étude de cas courante, le comportement est attaché au modèle Post sur la
relation Thread associée. L'objectif est de maintenir le nombre de messages (posts) quelle que
soit l'instance de la classe Thread dans une colonne nommée num_posts. Le modèle de données
YAML peut alors être modifié comme ci-après afin de définir l'option complémentaire du
comportement.
# ...
Post:
actAs:
CountCache:
relations:
Thread:
columnName: num_posts
foreignAlias: Posts
# ...
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
Désormais, le modèle Thread contient une colonne num_posts dont la valeur sera conservée à
jour avec le nombre de messages que chaque sujet de discussion possède.
Ecouter les Evénements
L'étape suivante de la construction du comportement consiste à écrire un écouteur d'événements
pour l'enregistrement en cours. Cet écouteur est responsable de la bonne conservation de la
valeur du compteur lorsque de nouveaux enregistrements sont insérés en base de données, ou
bien lorsqu'un (ou plusieurs) enregistrement(s) est (sont) supprimé(s en DQL).
class CountCache extends Doctrine_Template
{
// ...
public function setTableDefinition()
{
// ...
$this->addListener(new CountCacheListener($this->_options));
}
}
Avant d'aller plus loin, la classe CountCacheListener doit être définie et étendre
Doctrine_Record_Listener. Cette classe accepte un tableau d'options transmis simplement du
template à l'écouteur.
// lib/model/count_cache/CountCacheListener.class.php
class CountCacheListener extends Doctrine_Record_Listener
{
protected $_options;
public function __construct(array $options)
{
$this->_options = $options;
}
}
A présent, les évènements suivants doivent être initialisés dans le but de garder les compteurs
à jour en permanence.
postInsert() incrémente le compteur lorsqu'un nouvel objet est inséré ;
postDelete() décrémente le compteur lorsqu'un objet est supprimé ;
preDqlDelete() décrémente les compteurs lorsque les enregistrements sont supprimés à
partir d'un ordre DQL DELETE.
Le listing ci-dessous définit tout d'abord la méthode postInsert() :
class CountCacheListener extends Doctrine_Record_Listener
{
// ...
public function postInsert(Doctrine_Event $event)
{
$invoker = $event->getInvoker();
foreach ($this->_options['relations'] as $relation => $options)
{
$table = Doctrine::getTable($options['className']);
$relation = $table->getRelation($options['foreignAlias']);
$table
->createQuery()
->update()
->set($options['columnName'], $options['columnName'].' + 1')
->where($relation['local'].' = ?', $invoker->$relation['foreign'])
->execute();
}
}
}
Le code ci-dessus incrémente les compteurs d'une unité pour toutes les relations configurées à
l'aide d'une requête DQL UPDATE à chaque fois qu'un objet similaire à celui ci-dessous est
inséré.
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
$post = new Post();
$post->thread_id = 1;
$post->body = 'body of the post';
$post->save();
Le Thread ayant 1 pour id verra sa colonne num_posts augmentée de 1. Les compteurs sont à
présent bien incrémentés lorsque de nouveaux objets sont insérés. Il convient maintenant de
gérer la décrémentation des compteurs lorsque les objets sont supprimés en implémentant la
méthode postDelete() suivante.
class CountCacheListener extends Doctrine_Record_Listener
{
// ...
public function postDelete(Doctrine_Event $event)
{
$invoker = $event->getInvoker();
foreach ($this->_options['relations'] as $relation => $options)
{
$table = Doctrine::getTable($options['className']);
$relation = $table->getRelation($options['foreignAlias']);
$table
->createQuery()
->update()
->set($options['columnName'], $options['columnName'].' - 1')
->where($relation['local'].' = ?', $invoker->$relation['foreign'])
->execute();
}
}
}
La méthode postDelete() ci-dessus est presque identique à la méthode postInsert() puisque
la seule différence qui les oppose est la décrémentation de 1 de la colonne num_posts au lieu de
l'incrémentation. Cela se traduit par le code ci-dessous si l'enregistrement $post sauvegardé
précédemment est supprimé.
$post->delete();
La dernière pièce du puzzle consiste à gérer la mise à jour des compteurs à l'aide d'une requête
DQL DELETE lorsque plusieurs objets sont supprimés d'un coup. Pour résoudre ce problème, il
suffit d'implémenter la méthode preDqlDelete().
class CountCacheListener extends Doctrine_Record_Listener
{
// ...
public function preDqlDelete(Doctrine_Event $event)
{
foreach ($this->_options['relations'] as $relation => $options)
{
$table = Doctrine::getTable($options['className']);
$relation = $table->getRelation($options['foreignAlias']);
$q = clone $event->getQuery();
$q->select($relation['foreign']);
$ids = $q->execute(array(), Doctrine::HYDRATE_NONE);
foreach ($ids as $id)
{
$id = $id[0];
$table
->createQuery()
->update()
->set($options['columnName'], $options['columnName'].' - 1')
->where($relation['local'].' = ?', $id)
->execute();
}
}
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
}
Le code ci-dessus clone la requête DQL DELETE et la transforme en un SELECT qui permet de
retrouver la liste des IDs qui seront supprimés. Par conséquent, les compteurs peuvent être mis
à jour en fonction des enregistrements supprimés.
Le scénario suivant est à présent pris en charge et les compteurs seront décrémentés si le code
suivant était exécuté.
Doctrine::getTable('Post')
->createQuery()
->delete()
->where('id = ?', 1)
->execute();
Ou bien si plusieurs enregistrements devaient être supprimés, les compteurs seraient eux aussi
correctement décrémentés.
Doctrine::getTable('Post')
->createQuery()
->delete()
->where('body LIKE ?', '%cool%')
->execute();
L'invocation de la méthode preDqlDelete() est soumise à l'activation d'un attribut. Les DQL
de rappel (DQL callbacks) sont désactivés par défaut car ils ont un coût supplémentaire sur
les performances. Par conséquent, il doivent être explicitement activés afin de pouvoir les
utiliser.
$manager->setAttribute(Doctrine_Core::ATTR_USE_DQL_CALLBACKS, true);
C'est tout ! Le comportement Doctrine est terminé, mais la dernière chose qui reste à faire
consiste à le tester un peu.
Tester le Comportement
Le code est désormais implémenté et peut donc être testé avec quelques jeux de données de
test.
# data/fixtures/data.yml
Thread:
thread1:
title: Test Thread
Posts:
post1:
body: This is the body of my test thread
post2:
body: This is really cool
post3:
body: Ya it is pretty cool
Il ne reste plus qu'à tout reconstruire et charger les données de test.
$ php symfony doctrine:build --all --and-load
Maintenant que l'ensemble est reconstruit et que les données de test sont chargées, un test peut
être exécuté afin de s'assurer que les compteurs ont bien été mis à jour:
$ php symfony doctrine:dql "FROM Thread t, t.Posts p"
doctrine - executing: "FROM Thread t, t.Posts p" ()
doctrine id: '1'
doctrine title: 'Test Thread'
doctrine num_posts: '3'
doctrine Posts:
doctrine doctrine id: '1'
doctrine thread_id: '1'
doctrine body: 'This is the body of my test thread'
doctrine doctrine id: '2'
doctrine thread_id: '1'
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
doctrine
doctrine
doctrine
doctrine
doctrine
-
body: 'This is really cool'
id: '3'
thread_id: '1'
body: 'Ya it is pretty cool'
La colonne num_posts de la classe de modèle Thread dispose bien de la valeur trois. Si l'un des
posts est amené à être supprimé avec le code suivant, alors il décrémentera automatiquement
le compteur associé de l'objet Thread.
$post = Doctrine_Core::getTable('Post')->find(1);
$post->delete();
Le listing ci-dessous prouve que l'enregistrement est bien supprimé et que le compteur a bien
été mis à jour.
$ php symfony doctrine:dql "FROM Thread t, t.Posts p"
doctrine - executing: "FROM Thread t, t.Posts p" ()
doctrine id: '1'
doctrine title: 'Test Thread'
doctrine num_posts: '2'
doctrine Posts:
doctrine doctrine id: '2'
doctrine thread_id: '1'
doctrine body: 'This is really cool'
doctrine doctrine id: '3'
doctrine thread_id: '1'
doctrine body: 'Ya it is pretty cool'
Cela fonctionne de la même manière si les deux enregistrements restants sont supprimés en
même temps à l'aide d'une requête DQL.
Doctrine_Core::getTable('Post')
->createQuery()
->delete()
->where('body LIKE ?', '%cool%')
->execute();
A présent, tous les posts associés au sujet de discussion ont été supprimés et la colonne
num_posts devrait ainsi conserver la valeur zéro.
$ php symfony doctrine:dql "FROM Thread t, t.Posts p"
doctrine - executing: "FROM Thread t, t.Posts p" ()
doctrine id: '1'
doctrine title: 'Test Thread'
doctrine num_posts: '0'
doctrine Posts: { }
C'est tout! J'espère que cet article vous a été utile dans le sens où vous avez appris quelque
chose de nouveau au sujet des comportements. De plus, j'espère que ce comportement vous
sera également utile.
Utiliser le Cache des Résultats Doctrine
Dans la plupart des applications web à fort trafic, il est commun de cacher de l'information afin
d'économiser des ressources CPU. Avec la dernière version de Doctrine 1.2, de nombreuses
améliorations ont été réalisées au niveau du cache des jeux de résultats afin d'offrir au
développeur davantage de contrôle. En effet, le développeur a désormais plus de contrôle
lorsqu'il s'agit de supprimer des entrées du cache depuis les gestionnaires de cache.
Autrefois, il était impossible de spécifier la clé de cache utilisée pour stocker une entrée dans le
cache. Par conséquent, l'entrée cachée ne pouvait être véritablement identifiée en vue de la
supprimer.
Cette section présentera, à partir d'un exemple simple, comment utiliser le cache de jeux de
résultats afin de mettre en cache toutes les requêtes relatives à l'utilisateur courant. Cette mise
en cache sera réalisée de la même manière qu'en utilisant des événements afin de s'assurer
qu'elles sont correctement nettoyées lorsque des données évoluent.
Le Modèle de Données
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
Pour cet exemple, le schéma suivant est utilisé.
# config/doctrine/schema.yml
User:
columns:
username:
type: string(255)
notnull: true
unique: true
password:
type: string(255)
notnull: true
Une fois le schéma recopié, l'ensemble du projet peut alors être reconstruit à l'aide de la
commande suivante.
$ php symfony doctrine:build --all
Ceci étant fait, la classe User ci-après devrait avoir été générée par Doctrine.
// lib/model/doctrine/User.class.php
/**
* User
*
* This class has been auto-generated by the Doctrine ORM Framework
*
* @package
##PACKAGE##
* @subpackage ##SUBPACKAGE##
* @author
##NAME## <##EMAIL##>
* @version
SVN: $Id: Builder.php 6508 2009-10-14 06:28:49Z jwage $
*/
class User extends BaseUser
{
}
Il est important de noter que cette classe accueillera du code supplémentaire plus loin dans cet
article.
Configurer le Cache de Résultats
Afin de pouvoir mettre en oeuvre le cache de résultats, un gestionnaire de cache doit d'abord
être configuré pour les requêtes utilisées. Cette étape se réalise très simplement en configurant
l'attribut ATTR_RESULT_CACHE.
Dans cet article, c'est le gestionnaire de cache APC qui a été retenu dans la mesure où c'est le
meilleur choix pour l'environnement de production. Si APC n'est pas disponible sur le serveur de
développement, alors celui-ci pourra aussi bien se contenter des pilotes Doctrine_Cache_Db ou
bien Doctrine_Cache_Array pour des besoins de test.
Cet attribut est configurable dans la classe de configuration du projet, ProjectConfiguration.
Il suffit pour cela de déclarer une méthode configureDoctrine() comme expliqué ci-dessous.
// config/ProjectConfiguration.class.php
// ...
class ProjectConfiguration extends sfProjectConfiguration
{
// ...
public function configureDoctrine(Doctrine_Manager $manager)
{
$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, new
Doctrine_Cache_Apc());
}
}
Maintenant que le gestionnaire de cache des résultats est configuré, il peut désormais être testé
pour cacher les jeux de résultats des requêtes exécutées.
Exemples de Requêtes
Supposons que l'application dispose d'un certain nombre de requêtes relatives à l'utilisateur
courant, et qu'elles doivent être nettoyées à chaque fois que les données de l'utilisateur sont
modifiées. Le code ci-dessous présente une requête simple qui pourrait servir à rendre la liste
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
des utilisateurs triés par ordre alphabétique.
$q = Doctrine_Core::getTable('User')
->createQuery('u')
->orderBy('u.username ASC');
A présent, le cache peut être activé pour cette requête en utilisant la méthode useResultCache.
$q->useResultCache(true, 3600, 'users_index');
Notez le troisième argument. Il s'agit de la clé qui sera utilisée pour stocker l'entrée de cache
des résultats dans le gestionnaire de cache. Cela permet ainsi d'identifier clairement cette
requête afin de la supprimer du gestionnaire de cache.
Désormais, lorsque la requête est exécutée, elle interroge tout d'abord la base de données pour
obtenir les résultats. Puis, elle stocke ces derniers dans le gestionnaire de cache à la clé
users_index, et ainsi, toutes les requêtes ultérieures iront chercher l'information dans le
gestionnaire au lieu d'attaquer directement la base de données:
$users = $q->execute();
Non seulement ce système fait économiser du traitement au serveur de base de données, il
contourne également le processus complet d'hydratation puisque Doctrine sauvegarde les
données déjà hydratées. Cela signifie que le serveur web en sera d'autant plus soulagé.
A présent, si l'on contrôle le gestionnaire de cache, on découvrira une entrée nommée
users_index.
if ($cacheDriver->contains('users_index'))
{
echo 'cache exists';
}
else
{
echo 'cache does not exist';
}
Supprimer le Cache
A ce stade, la requête est cachée, et il est temps d'en apprendre un peu plus au sujet de la
suppression du cache. Le cache peut être supprimé manuellement en utilisant l'API du
gestionnaire de cache ou bien en invoquant quelques événements pour nettoyer l'entrée de
cache automatiquement lorsqu'un utilisateur est ajouté ou modifié.
L'API du Gestionnaire de Cache
Tout d'abord, il s'agit de présenter l'API brute du gestionnaire de cache avant de lui faire
implémenter un nouvel événement.
Pour avoir accès à l'instance du gestionnaire de cache des résultats, il suffit de faire appel à
l'instance de la classe Doctrine_Manager.
$cacheDriver = $manager->getAttribute(Doctrine_Core::ATTR_RESULT_CACHE);
Si l'accès à la variable $manager est impossible, l'instance reste disponible à l'aide du code
suivant:
$manager = Doctrine_Manager::getInstance();
Il ne reste alors plus qu'à utiliser l'API du gestionnaire de cache pour supprimer des entrées du
cache.
$cacheDriver->delete('users_index');
Cependant, il est probable qu'il y ait plus d'une requête préfixée par users_, c'est pourquoi il
convient de supprimer le cache de résultats pour toutes ces requêtes. Dans cet exemple, la
méthode delete() actuelle ne fonctionnera pas. Par résoudre ce problème, Doctrine fournit une
méthode nommée deleteByPrefix() qui supprime n'importe quelle entrée du cache qui
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
contient le préfixe passé en paramètre comme le montre l'exemple suivant.
$cacheDriver->deleteByPrefix('users_');
Il existe d'autres méthodes très utiles facilitant la suppression des entrées du cache si la
méthode deleteByPrefix() ne suffit pas à elle-même.
deleteBySuffix($suffix) supprime les entrées du cache enregistrées avec le suffixe
passé en paramètre ;
deleteByRegex($regex) supprime les entrées du cache qui correspondent à l'expression
régulière passée en paramètre ;
deleteAll() supprime toutes les entrées du cache.
Supprimer des Entrées du Cache à l'Aide des Evénements
La méthode idéale pour nettoyer le cache serait de le faire à chaque fois que les données de
l'utilisateur sont modifiées. Pour y parvenir, il suffit d'implémenter un événement postSave()
dans la classe de définition du modèle User.
Souvenez-vous de la classe User déclarée au tout début de ce chapitre. L'étape suivante
consiste à lui implémenter la méthode postSave() ci-dessous afin de régénérer le cache des
résultats à chaque fois que l'objet est modifié.
// lib/model/doctrine/User.class.php
class User extends BaseUser
{
// ...
public function postSave($event)
{
$cacheDriver = $this->getTable()->getAttribute(Doctrine_Core::ATTR_RESULT_CACHE);
$cacheDriver->deleteByPrefix('users_');
}
}
Grâce à ces quelques lignes, le cache des requêtes spécifiques à l'utilisateur sera nettoyé à
chaque fois que ce dernier sera mis à jour ou ajouté dans la base de données.
$user = new User();
$user->username = 'jwage';
$user->password = 'changeme';
$user->save();
La prochaine fois que les requêtes seront exécutées, Doctrine se chargera de récupérer les
données à jour en provenance de la base de données dans la mesure où le cache n'existe pas
encore. Ce n'est qu'après la récupération des enregistrements que ces derniers seront mis en
cache pour toutes les requêtes ultérieures.
Développer un Hydrator Doctrine
L'une des principales fonctionnalités de Doctrine est sa capacité à transformer un objet
Doctrine_Query en différents types de jeux de résultats. Cette transformation est assurée par
les hydrators Doctrine.
Jusqu'à Doctrine 1.2, les hydrators étaient codés en dur et figés, ce qui empêchait les
développeurs d'écrire et d'utiliser les leurs. Heureusement, cette contrainte n'est plus et il
désormais possible d'écrire des hydrators personnalisés. Par conséquent, n'importe quelle
structure de données peut être créée afin de formater les résultats de la base de données
lorsque une instance de la classe Doctrine_Query est exécutée.
L'exemple présenté plus loin explique comment développer un hydrator à la fois simple et facile
à comprendre, mais s'avère néanmoins très utile. Cet objet permettra de sélectionner deux
valeurs et d'hydrater les données dans un tableau associatif dont la première colonne sera la clé
et la seconde la valeur.
Le Modèle de Données et les Données de Test
Avant de débuter, il est nécessaire d'avoir un modèle de données épuré avec lequel seront
exécutés quelques tests. Pour y parvenir, un simple modèle User suffit comme le présente le
listing ci-dessous.
# config/doctrine/schema.yml
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
User:
columns:
username: string(255)
is_active: string(255)
Afin de pouvoir tester le fonctionnement de l'hydrator, le modèle User doit disposer de quelques
jeux de tests sommaires. Le listing ci-dessous fait état de deux objets User.
# data/fixtures/data.yml
User:
user1:
username: jwage
password: changeme
is_active: 1
user2:
username: jonwage
password: changeme
is_active: 0
Ces données de tests peuvent désormais être chargées dans la base de données à l'aide de la
commande suivante.
$ php symfony doctrine:build --all --and-load
Développer l'Hydrator
L'écriture d'un hydrator personnalisé nécessite de déclarer une nouvelle classe dérivée de la
classe abstraite Doctrine_Hydrator_Abstract, puis d'implémenter une méthode
hydrateResultSet($stmt). Cette méthode reçoit en argument une instance de la classe
PDOStatement utilisée pour exécuter la requête SQL. Par conséquent, cet objet peut être utilisé
pour obtenir les résultats bruts de la requête grâce à PDO, puis de les transformer en une
structure personnalisée.
Pour y parvenir, il suffit de créer une nouvelle classe KeyValuePairHydrator dans le répertoire
lib du projet afin que symfony puisse la charger automatiquement.
// lib/KeyValuePairHydrator.class.php
class KeyValuePairHydrator extends Doctrine_Hydrator_Abstract
{
public function hydrateResultSet($stmt)
{
return $stmt->fetchAll(Doctrine_Core::FETCH_NUM);
}
}
En l'état, le code ci-dessus retourne les données brutes grâce à PDO, ce qui ne correspond pas
vraiment aux spécifications techniques. Il s'agit donc de transformer ces données en une
structure personnalisée de paires clé => valeur. Un modification mineure de la méthode
hydrateResultSet() permet d'y parvenir.
// lib/KeyValuePairHydrator.class.php
class KeyValuePairHydrator extends Doctrine_Hydrator_Abstract
{
public function hydrateResultSet($stmt)
{
$results = $stmt->fetchAll(Doctrine_Core::FETCH_NUM);
$array = array();
foreach ($results as $result)
{
$array[$result[0]] = $result[1];
}
return $array;
}
}
Ce fut facile, n'est-ce pas ? Le code de l'objet hydrator est désormais terminé et il répond
parfaitement aux besoins. Il ne reste donc plus qu'à le tester pour s'assurer qu'il fonctionne
correctement.
Utiliser l'Hydrator
Pour utiliser et tester l'hydrator, il est impératif de l'enregistrer afin que Doctrine ait
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Techniques Avancées avec Doctrine | symfony | Web PHP Framework
connaissance de la classe d'hydrator précédemment écrite lorsque les requêtes sont exécutées.
Pour y parvenir, elle doit être enregistrée grâce à l'instance Doctrine_Manager depuis la classe
ProjectConfiguration.
// config/ProjectConfiguration.class.php
// ...
class ProjectConfiguration extends sfProjectConfiguration
{
// ...
public function configureDoctrine(Doctrine_Manager $manager)
{
$manager->registerHydrator('key_value_pair', 'KeyValuePairHydrator');
}
}
L'hydrator est à présent enregistré et peut être utilisé avec des instances de la classe
Doctrine_Query comme le montre l'exemple ci-dessous.
$q = Doctrine_Core::getTable('User')
->createQuery('u')
->select('u.username, u.is_active');
$results = $q->execute(array(), 'key_value_pair');
print_r($results);
L'exécution de ce code avec les jeux de données de tests définis plus haut provoque le résultat
suivant.
Array
(
[jwage] => 1
[jonwage] => 0
)
Il n'en faut pas plus pour réaliser un hydrator aussi simplement. J'espère donc qu'il vous sera
utile et que la communauté n'hésitera pas à contribuer en retour en développant de nouveaux
hydrators pour Doctrine.
« Etendre la Web Debug Toolbar
Tirer Profit de l'Héritage de Table avec Doctrine »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/08-Advanced-Doctrine-Usage[31/12/2010 02:34:05]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Tirer Profit de l'Héritage de Table avec Doctrine
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Hugo Hamon
Doctrine est officiellement devenu la bibliothèque d'ORM par défaut à
partir de symfony 1.3 alors que le développement de Propel avait ralenti
depuis quelques mois. Le projet Propel reste toutefois supporté dans
symfony et continue de s'améliorer grâce notamment aux efforts des
membres de la communauté symfony.
Le projet Doctrine 1.2 est devenu le nouvel ORM par défaut de symfony
pour deux raisons principales. En effet, Doctrine est beaucoup plus
simple à utiliser que Propel et parce qu'il fournit de nombreuses
fonctionnalités intéressantes telles que les comportements (behaviors),
les requêtes DQL simplifiées, les migrations ou bien l'héritage de table.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Ce chapitre explique ce qu'est l'héritage de table et comment cette
fonctionnalité est désormais parfaitement intégrée dans symfony 1.3.
C'est à l'aide d'exemples concrets réels que ce chapitre illustrera comment tirer parti de
l'héritage de table Doctrine afin de rendre le code plus flexible et mieux organisé.
L'Héritage de Table Doctrine
Bien qu'il ne soit pas encore très connu des développeurs et peu utilisé, l'héritage de table est
probablement l'une des fonctionnalités les plus intéressantes de Doctrine. L'héritage de table
permet au développeur de créer des tables, dans une base de données, qui héritent d'autres
plus génériques de la même manière que des classes en étendent d'autres dans un langage de
programmation orienté objet. En effet, cette fonctionnalité offre une manière simple et efficace
pour partager des données entre deux ou plusieurs tables dans une même table maîtresse plus
générique. Le diagramme ci-dessous explique ce principe de l'héritage de table.
Chapter Content
L'Héritage de Table Doctrine
La Stratégie de l'Héritage Simple
La Stratégie de l'Héritage de Table
par Agrégation de Colonnes
Doctrine intègre trois stratégies différentes pour gérer les héritages de table selon les besoins
de l'applications en terme de performance, d'atomicité, d'efficacité ou bien encore de simplicité,
etc. Ces trois stratégies natives sont l'héritage simple, l'héritage par agrégation de colonnes et
l'héritage concret. Bien que que toutes ces stratégies soient présentées dans le livre Doctrine,
des informations complémentaires aideront à mieux comprendre chacune de leurs options et
dans quelles circonstances elles sont particulièrement utiles.
La Stratégie de l'Héritage Simple
La stratégie de l'héritage simple est, comme son nom l'indique, la plus simple de toutes dans la
mesure où elle stocke toutes les colonnes, y compris celles des tables filles, dans la table
maîtresse. Par exemple, si le schéma descriptif du modèle de données ressemble au code YAML
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
La Stratégie de l'Héritage de Table
Concret
Intégration de l'Héritage de Table
avec Symfony
Introduction aux Etudes de Cas
Concrètes
Héritage de Table au Niveau du
Modèle
Héritage de Table au Niveau des
Formulaires
L'Héritage de Table au Niveau des
Filtres
L'Héritage de Table avec l'Admin
Generator
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
ci-après, alors Doctrine génèrera une seule table Person, dans laquelle seront fusionnées les
colonnes des tables Professor et Student.
Person:
columns:
first_name:
type:
notnull:
last_name:
type:
notnull:
Professor:
inheritance:
type:
extends:
columns:
specialty:
type:
notnull:
Student:
inheritance:
type:
extends:
columns:
graduation:
type:
notnull:
promotion:
type:
notnull:
Conclusion
Be trained by symfony experts
string(50)
true
Jan 24: Paris
string(50)
true
- Français)
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
Mar 21: Paris
(Maîtrise de & Doctrine
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
simple
Person
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
string(50)
true
Search
powered by google
simple
Person
string(20)
true
integer(4)
true
Avec l'héritage de table simple, toutes les colonnes supplémentaires (specialty, graduation et
promotion) sont automatiquement remontées au niveau supérieur dans le modèle Person, bien
que Doctrine génère une classe de de modèle pour chacune des tables Student et Person.
Cette stratégie a un inconvénient majeur dans la mesure où la table maîtresse Person ne fournit
aucune colonne pour identifier le type de chaque enregistrement. En d'autres termes, il n'y a
absolument aucun moyen de retrouver des objets de type Professor ou Student distincts. Par
conséquent, l'exécution du code Doctrine ci-dessous retourne un objet Doctrine_Collection
qui contient tous les enregistrements de la table (Student et Professor ensemble).
$professors = Doctrine_Core::getTable('Professor')->findAll();
Il en résulte que la stratégie par héritage de table simple ne s'avère pas pratique pour des cas
concrets d'application. En effet, la plupart des applications requièrent de sélectionner et
d'hydrater des objets de type spécifiques. Par conséquent, cette stratégie est d'ores et déjà
abandonnée pour la suite de ce chapitre.
La Stratégie de l'Héritage de Table par Agrégation de Colonnes
La stratégie de l'héritage par agrégation de colonnes est similaire à la stratégie d'héritage
simple à la différence qu'elle inclut automatiquement une colonne type pour identifier les
différents types d'enregistrements. Par conséquent, lorsqu'un un enregistrement est persisté en
base de données, une valeur est affectée à cette colonne afin de déterminer à quelle classe il
appartient.
Person:
columns:
first_name:
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
type:
notnull:
last_name:
type:
notnull:
Professor:
inheritance:
type:
extends:
keyField:
keyValue:
columns:
specialty:
type:
notnull:
Student:
inheritance:
type:
extends:
keyField:
keyValue:
columns:
graduation:
type:
notnull:
promotion:
type:
notnull:
string(50)
true
string(50)
true
column_aggregation
Person
type
1
string(50)
true
column_aggregation
Person
type
2
string(20)
true
integer(4)
true
Dans le schéma de données ci-dessus, la stratégie d'héritage a été changée en
column_aggregation et deux nouveaux attributs ont aussi fait leur apparition. Le premier,
keyField, indique le nom de la colonne qui doit être créée pour stocker l'information concernant
le type de l'enregistrement. L'attribut keyField est un entier obligatoire nommé type et est
aussi le nom de la colonne par défaut s'il n'est pas explicitement spécifié dans le schéma de
données. Le second attribut définit quant à lui la valeur à affecter pour chaque enregistrement
qui appartient aux classes Professor et Student.
La stratégie de l'agrégation de colonnes est une bonne méthode d'héritage de table dans la
mesure où elle ne crée finalement qu'une seule table (Person) contenant tous les champs
fusionnés, auxquels s'ajoute le champ type. De cette manière, il n'y a pas besoin de créer
plusieurs tables et de les joindre par des requêtes SQL lorsqu'il s'agit de récupérer des données.
Le listing ci-dessous montre quelques exemples d'interrogation des tables, ainsi que les types de
résultats retournés.
// Returns a Doctrine_Collection of Professor objects
$professors = Doctrine_Core::getTable('Professor')->findAll();
// Returns a Doctrine_Collection of Student objects
$students = Doctrine_Core::getTable('Student')->findAll();
// Returns a Professor object
$professor = Doctrine_Core::getTable('Professor')->findOneBySpeciality('physics');
// Returns a Student object
$student = Doctrine_Core::getTable('Student')->find(42);
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
// Returns a Student object
$student = Doctrine_Core::getTable('Person')->findOneByIdAndType(array(42, 2));
Lorsqu'il s'agit de récupérer des données depuis une classe fille (Professor ou Student),
Doctrine se charge d'ajouter automatiquement la clause WHERE à la requête avec la bonne valeur
de filtre pour la colonne type.
Toutefois, il existe quelques inconvénients quant à l'usage de la stratégie par agrégation de
colonnes dans certains cas. Tout d'abord, l'agrégation de colonnes empêche tous les champs de
chaque table fille d'être définis comme obligatoires. Par conséquent, selon le nombre de champs
définis, la table Person pourra contenir des enregistrements pour lesquels plusieurs champs
resteront vides.
Le second inconvénient réside quant à lui dans le nombre de tables et de champs dérivés. Si le
schéma de données déclare un nombre important de tables filles, qui elles mêmes définissent
beaucoup de champs, alors il en résultera que la table maîtresse sera un large jeu de colonnes.
Par conséquent, cette dernière risque de devenir moins performante et plus difficile à maintenir.
La Stratégie de l'Héritage de Table Concret
La stratégie de l'héritage de table concret est un bon compromis entre les avantages de la
stratégie par agrégation de colonnes, les performances et la maintenabilité. En effet, cette
stratégie crée des tables indépendantes pour chaque table fille, et réplique toutes les colonnes
(colonnes partagées et colonnes spécifiques) à l'intérieur de celles-ci.
Person:
columns:
first_name:
type:
notnull:
last_name:
type:
notnull:
Professor:
inheritance:
type:
extends:
columns:
specialty:
type:
notnull:
Student:
inheritance:
type:
extends:
columns:
graduation:
type:
notnull:
promotion:
type:
notnull:
string(50)
true
string(50)
true
concrete
Person
string(50)
true
concrete
Person
string(20)
true
integer(4)
true
Ainsi, pour le schéma de données précédent, la table Professor générée contiendra l'ensemble
des champs suivants : id, first_name, last_name et specialty.
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Cette approche a de nombreux avantages par rapport aux stratégies précédentes. La première,
c'est que toutes les tables sont désormais isolées et demeurent indépendantes les unes par
rapport aux autres. De plus, cela a pour effet immédiat d'éliminer tous les champs vides ainsi
que la colonne additionnelle type. De ce fait, chaque table est désormais plus légère et isolée
des autres.
Le fait que les champs partagés soient dupliqués dans les tables dérivées est un gain en
termes de performance et de scalabilité. En effet, Doctrine n'a plus besoin de créer des
jointures automatiques vers la table maîtresse lorsqu'il s'agit de récupérer des informations
partagées par les enregistrements des tables filles.
Les deux seuls inconvénients notables de la stratégie d'héritage concret sont la duplication des
champs (bien que la réplication est généralement la clé vers les performances) et le fait que la
table maîtresse générée demeurera toujours vide. En effet, Doctrine a généré une table Person
alors qu'elle ne sera jamais remplie ni référencée par aucune requête SQL. Aucune requête ne
sera exécutée sur cette table dans la mesure où toute l'information est sauvegardée dans les
tables dérivées.
Cette première partie du chapitre a permis de prendre le temps nécessaire pour introduire les
trois types de stratégies d'héritage de table avec Doctrine. Cependant, il n'a pas encore été
question de les mettre en pratique avec symfony dans des cas concrets issus de problématiques
réelles. La section suivante du chapitre explique et présente comment profiter de la puissance de
l'héritage de table Doctrine dans symfony 1.3, particulièrement au niveau du modèle et du
framework de formulaires.
Intégration de l'Héritage de Table avec Symfony
Avant symfony 1.3, l'héritage de table Doctrine n'était pas complètement supporté par le
framework dans la mesure où les classes de formulaires et de filtres n'héritaient pas de leur
classe mère respective. Par conséquent, les développeurs qui avaient besoin d'utiliser l'héritage
de table dans leurs projets, étaient contraints de réajuster les formulaires et les filtres. Ils
étaient également forcés de redéfinir de nombreuses méthodes pour parvenir à reproduire le
comportement de l'héritage, au détriment d'un temps perdu non négligeable...
Heureusement, grâce aux retours d'utilisation de la communauté, l'équipe de développement de
symfony a pu améliorer les classes de formulaires et de filtres dans le but de supporter
facilement et entièrement l'héritage de table dans symfony 1.3.
Le reste de ce chapitre explique comment utiliser l'héritage de table de Doctrine et comment en
profiter dans plusieurs situations à travers le modèle, les formulaires, les filtres et le générateur
d'administration. Pour ce faire, des exemples issus de problématiques réelles aideront à mieux
comprendre comment l'héritage fonctionne avec symfony, afin de pouvoir facilement l'utiliser
pour vos propres besoins.
Introduction aux Etudes de Cas Concrètes
Tout au long de ce chapitre, plusieurs cas concrets seront étudiés afin de dévoiler les principaux
avantages de l'héritage de table Doctrine à plusieurs niveaux : modèle, formulaires, filtres et
générateur d'administration.
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Le premier exemple est tiré d'une application réelle développée chez Sensio pour un client grand
compte français. Il explique en quoi l'héritage de table Doctrine est une excellente solution pour
gérer une douzaine de jeux de référentiels de données identiques qui partagent des propriétés
et méthodes similaires. L'objectif consistera ici à éviter la duplication du code partagé.
Le deuxième exemple explique comment tirer profit de la stratégie de l'héritage de table concret
au niveau des formulaires en créant un modèle de données simple destiné à la gestion de
fichiers numériques.
Enfin, le troisième et dernier exemple montrera comment tirer parti de l'héritage de table avec
l'Admin Generator et comment le rendre plus flexible.
Héritage de Table au Niveau du Modèle
Comme avec la programmation orientée objet, l'héritage de table favorise le partage de
données. En effet, l'héritage de table permet de partager des propriétés et des méthodes
lorsqu'il s'agit de travailler avec des modèles générés. L'héritage de table de Doctrine est un bon
moyen de partager et de redéfinir des actions propres aux objets hérités. La section suivante
explique ce concept à l'aide d'un exemple pratique concret.
La Problématique
De nombreuses applications web fonctionnent à l'aide de jeux de données particuliers, appelés
"référentiels". Un référentiel est généralement un jeu de données représenté par une simple
table contenant au moins deux champs : id et label. Cependant, dans certains cas, le
référentiel contient davantage d'informations comme par exemple des drapeaux is_active ou
is_default. Ce fut exactement le cas chez Sensio pour un projet client.
Le client souhaitait pouvoir gérer de multiples jeux de données servant à alimenter les
formulaires et les vues majeures de l'application. Pour ce projet, toutes les tables de référentiels
ont été construites selon le même modèle de base et incluent toutes par extension les colonnes
suivantes : id, label, position et is_default.
Le champ position sert ici à ordonner les enregistrements les uns par rapport aux autres grâce
à un comportement Ajax de glisser / déposer (drag and drop). Le champ is_default quant à lui
représente un marqueur qui indique si oui ou non l'enregistrement doit être sélectionné par
défaut lorsqu'il alimente une liste déroulante HTML.
La Solution
Gérer de manière équivalente plus de deux tables similaires est l'une des meilleures
problématiques solutionnables grâce à l'héritage de table. Pour résoudre la problématique
expliquée précédemment, c'est la stratégie de l'héritage de table concret qui a été retenue afin
de satisfaire les besoins fonctionnels de l'application, et partager les méthodes communes des
objets dans la même classe de base. Le code YAML ci-dessous décrit un modèle de données
simplifié pour illustrer la problématique.
sfReferential:
columns:
id:
type:
integer(2)
notnull:
true
label:
type:
string(45)
notnull:
true
position:
type:
integer(2)
notnull:
true
is_default:
type:
boolean
notnull:
true
default:
false
sfReferentialContractType:
inheritance:
type:
concrete
extends:
sfReferential
sfReferentialProductType:
inheritance:
type:
concrete
extends:
sfReferential
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
L'héritage de table concret fonctionne parfaitement dans ce cas dans la mesure où il génère des
tables séparées et isolées les unes des autres. C'est d'autant plus important ici du fait du
l'existence du champ position qui doit impérativement être géré pour des enregistrements de
même type.
Dès lors que le modèle de données est établi, l'étape suivante consiste à générer les classes de
modèle correspondantes avant de les étudier. Pour ce schéma, Doctrine et symfony génèrent
trois tables SQL distinctes et six classes de modèle dans le répertoire lib/model/doctrine:
sfReferential gère les enregistrements de la table sf_referential ;
sfReferentialTable gère la table sf_referential ;
sfReferentialContractType
gère
sf_referential_contract_type ;
les
enregistrements
de
la
table
sfReferentialContractTypeTable gère la table sf_referential_contract_type ;
sfReferentialProductType
gère
sf_referential_product_type ;
les
enregistrements
de
la
table
sfReferentialProductTypeTable gère la table sf_referential_product_type.
Etudier les classes générées et leur héritage révèle que les deux classes de base des modèles
sfReferentialContractType et sfReferentialProductType héritent de la classe
sfReferential. Ainsi, toutes les méthodes publiques et protégées (y compris les propriétés)
définies dans la classe sfReferential seront partagées jusque dans les classes les plus basses,
et pourront être redéfinies si nécessaire.
C'est exactement le but recherché et la classe sfReferential peut désormais encapsuler des
méthodes pour gérer toutes les données des référentiels. Par exemple :
// lib/model/doctrine/sfReferential.class.php
class sfReferential extends BasesfReferential
{
public function promote()
{
// move up the record in the list
}
public function demote()
{
// move down the record in the list
}
public function moveToFirstPosition()
{
// move the record to the first position
}
public function moveToLastPosition()
{
// move the record to the last position
}
public function moveToPosition($position)
{
// move the record to a given position
}
public function makeDefault($forceSave = true, $conn = null)
{
$this->setIsDefault(true);
if ($forceSave)
{
$this->save($conn);
}
}
}
Grâce à l'héritage de table concret de Doctrine, tout le code est partagé à la même place. Le
code devient ainsi plus facile à déboguer, à maintenir, à faire évoluer et à tester unitairement.
C'est le principal réel avantage de l'héritage de table. De plus, grâce à cette approche, les objets
de modèle peuvent être utilisés pour centraliser le code des actions dans une classe spécifique.
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
La classe sfBaseReferentialActions ci-dessous est une classe générique d'actions, dérivée par
chaque contrôleur de chaque module de gestion des référentiels.
// lib/actions/sfBaseReferentialActions.class.php
class sfBaseReferentialActions extends sfActions
{
/**
* Ajax action that saves the new position as a result of the user
* using a drag and drop in the list view.
*
* This action is linked thanks to an ~sfDoctrineRoute~ that
* eases single referential object retrieval.
*
* @param sfWebRequest $request
*/
public function executeMoveToPosition(sfWebRequest $request)
{
$this->forward404Unless($request->isXmlHttpRequest());
$referential = $this->getRoute()->getObject();
$referential->moveToPosition($request->getParameter('position', 1));
return sfView::NONE;
}
}
Que se passerait-il si le schéma de données n'utilisait pas de l'héritage de table ? Le code aurait
alors besoin d'être dupliqué dans chaque classe de modèle de référentiel. Cette approche ne
serait pas non plus très "DRY" (Don't Repeat Yourself), particulièrement lorsqu'il s'agit de
travailler avec une douzaine de tables de référentiels identiques.
Héritage de Table au Niveau des Formulaires
Le visite guidée des avantages de l'héritage de table Doctrine continue. La section précédente a
montré combien cette fonctionnalité pouvait s'avérer utile pour partager des méthodes et des
propriétés communes entre plusieurs modèles. La suite de ce chapitre explique comment
l'héritage de table se comporte avec les formulaires générés par symfony.
Le Modèle de Données Etudié
Le schéma de données YAML ci-dessous décrit un modèle destiné à la gestion de documents
numériques. L'objectif de cette étude de cas consiste à sauvegarder des informations génériques
dans le modèle File et les données spécifiques dans des tables filles telles que Vidéo et PDF.
File:
columns:
filename:
type:
notnull:
mime_type:
type:
notnull:
description:
type:
notnull:
size:
type:
notnull:
default:
Video:
inheritance:
type:
extends:
columns:
format:
type:
notnull:
duration:
type:
notnull:
default:
string(50)
true
string(50)
true
clob
true
integer(8)
true
0
concrete
File
string(30)
true
integer(8)
true
0
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
encoding:
type:
PDF:
tableName:
inheritance:
type:
extends:
columns:
pages:
type:
notnull:
default:
paper_size:
type:
orientation:
type:
default:
values:
is_encrypted:
type:
default:
notnull:
string(50)
pdf
concrete
File
integer(8)
true
0
string(30)
enum
portrait
[portrait, landscape]
boolean
false
true
Les deux tables PDF et Video partagent la même table File, qui contient les informations
générales des documents numériques téléchargés. Le modèle Vidéo encapsule les données
relatives aux fichiers vidéos telles que le format (4/3, 16/9...) ou la durée (duration), tandis
que le modèle PDF contient le nombre de pages ou l'orientation du document. La commande
doctrine:build ci-dessous construit le modèle de données et les formulaires correspondants.
$ php symfony doctrine:build --all
La section suivante décrit comment tirer profit de l'héritage de table dans les classes de
formulaires grâce à la nouvelle méthode setupInheritance().
A la Découverte de la Méthode setupInheritance()
Comme attendu, Doctrine a généré six classes de formulaires dans les répertoires
lib/form/doctrine/base et lib/form/doctrine.
BaseFileForm
BaseVideoForm
BasePDFForm
FileForm
VideoForm
PDFForm
Il suffit d'ouvrir les trois classes préfixées par Base pour découvrir quelque chose de nouveau
dans la méthode setup(). Une nouvelle méthode setupInheritance(), vide par défaut, a été
ajoutée depuis symfony 1.3.
Il est très important de remarquer que l'héritage du formulaire est conservé dans la mesure où
les deux classes BaseVideoForm et BasePDFForm héritent des classes FileForm et
BaseFileForm. Par conséquent, chacune d'entre elles dérivent la classe File et peut également
partager les mêmes méthodes de base.
Le listing suivant redéfinit la méthode setupInheritance() et configure la classe FileForm afin
qu'elle puisse être réutilisée plus efficacement dans un autre sous-formulaire.
// lib/form/doctrine/FileForm.class.php
class FileForm extends BaseFileForm
{
protected function setupInheritance()
{
parent::setupInheritance();
$this->useFields(array('filename', 'description'));
$this->widgetSchema['filename']
= new sfWidgetFormInputFile();
$this->validatorSchema['filename'] = new sfValidatorFile(array(
'path' => sfConfig::get('sf_upload_dir')
));
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
}
}
La méthode setupInheritance(), appelée par les deux sous-classes VideoForm et PDFForm,
supprime tous les champs à l'exception de filename et description. Le widget du champ
filename a été redéfini en un widget de téléchargement de fichier et son validateur
correspondant a été remplacé par un objet sfValidatorFile. De cette manière, l'utilisateur
sera capable de transmettre un fichier et de le sauvegarder sur le serveur.
Définir le Type Mime et la Taille du Fichier
Tous les formulaires sont désormais prêts et personnalisés. Il reste néanmoins une toute
dernière chose à configurer avant de pouvoir les utiliser. Comme les widgets mime_type et size
ont été retirés de l'objet FileForm, ils doivent être définis par programmation. Le meilleur
endroit pour y parvenir est de passer par une nouvelle méthode generateFilenameFilename
dans la classe File.
// lib/model/doctrine/File.class.php
class File extends BaseFile
{
/**
* Generates a filename for the current file object.
*
* @param sfValidatedFile $file
* @return string
*/
public function generateFilenameFilename(sfValidatedFile $file)
{
$this->setMimeType($file->getType());
$this->setSize($file->getSize());
return $file->generateFilename();
}
}
Cette nouvelle méthode a pour rôle de générer un nom de fichier personnalisé pour le fichier
avant de le sauvegarder sur le système de fichiers. Bien que la méthode
generateFilenameFilename() retourne un nom de fichier auto-généré par défaut, elle sert
aussi à définir les propriétés mime_type et size à la volée en s'appuyant sur l'objet
sfValidatedFile passé en premier argument.
Depuis que symfony 1.3 supporte entièrement l'héritage de table Doctrine, les formulaires sont
maintenant capables de sauver un objet ainsi que ses valeurs héritées. Le support natif de
l'héritage permet ainsi de créer des formulaires puissants et fonctionnels avec seulement
quelques lignes de code personnalisé.
L'exemple ci-dessous aurait pu être largement et facilement amélioré grâce à l'héritage de
classe. Par exemple, les deux classes VideoForm et PDFForm peuvent redéfinir le validateur du
champ filename par un validateur plus spécifique tel que sfValidatorVideo ou bien
sfValidatorPDF. Il suffit pour cela de créer les deux classes, de leur faire étendre
sfValidatorFile et de spécialiser leur option mime_type respective.
L'Héritage de Table au Niveau des Filtres
Parce que les filtres sont aussi des formulaires, ils héritent eux aussi des propriétés et des
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
méthodes des formulaires de filtres parents. Par conséquent, les objets VideoFormFilter et
PDFFormFilter étendent la classe FileFormFilter et peuvent ainsi être personnalisés en
utilisant la méthode setupInheritance().
De la même manière, les deux classes VideoFormFilter et PDFFormFilter peuvent partager les
mêmes méthodes personnalisées de la classe FileFormFilter.
L'Héritage de Table avec l'Admin Generator
Il est maintenant l'heure de découvrir comment tirer profit de l'héritage de table Doctrine dans
l'Admin Generator grâce à l'une de ses nouvelles fonctionnalités : la définition d'une classe
d'actions de base commune. Il faut savoir que l'Admin Generator est l'une des fonctionnalités
les plus appréciées des développeurs depuis symfony 1.0.
En novembre 2008, le framework symfony 1.2 s'est doté d'un tout nouveau système de
génération de modules d'administration. Cet outil est livré par défaut avec de nombreuses
fonctionnalités prêtes à l'emploi telles les opérations CRUD de base, le filtrage et la pagination
de la liste de résultats, la suppression massive et bien plus encore... L'Admin Generator est un
outil puissant qui facilite et accélère considérablement la génération et la personnalisation
d'interfaces d'administration pour tout développeur.
Introduction à l'Exemple Pratique
L'objectif de la dernière partie de ce chapitre consiste à illustrer comment tirer parti de l'héritage
de table Doctrine dans l'Admin Generator. Pour y parvenir, une petite interface d'administration
sera bâtie. Cette dernière contiendra deux modules de gestion de tables qui contiennent toutes
les deux des données qui peuvent être ordonnées et priorisées les unes par rapport aux autres.
Comme la ligne de conduite de symfony est de ne pas réinventer la roue à chaque fois, le
modèle de données Doctrine s'appuiera sur le plugin csDoctrineActAsSortablePlugin qui
fournit toute l'API nécessaire à l'ordonnancement d'objets. Ce plugin est développé et maintenu
par CentreSource, l'une des sociétés les plus actives dans l'écosystème de symfony.
Le modèle de données est particulièrement simple pour cet exemple. Il consiste en trois classes
de modèle, sfItem, sfTodoItem et sfShoppingItem, qui servent à gérer une liste de choses à
faire et une liste de courses. Chaque enregistrement de ces deux listes est ordonnable afin de
permettre aux objets d'être priorisés les uns par rapport aux autres.
sfItem:
actAs:
columns:
name:
type:
notnull:
sfTodoItem:
actAs:
inheritance:
type:
extends:
columns:
priority:
type:
notnull:
default:
assigned_to:
type:
notnull:
default:
sfShoppingItem:
actAs:
inheritance:
type:
extends:
columns:
quantity:
type:
notnull:
default:
[Timestampable]
string(50)
true
[Sortable]
concrete
sfItem
string(20)
true
minor
string(30)
true
me
[Sortable]
concrete
sfItem
integer(3)
true
1
Le schéma ci-dessus décrit le modèle de données séparé en trois classes de modèle. Les deux
classes filles, sfTodoItem et sfShoppingItem, utilisent toutes les deux les comportements
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Sortable et Timestampable par héritage pour ce dernier. Le comportement Sortable est fourni
par le plugin csDoctrineActAsSortableBehaviorPlugin qui ajoute une colonne supplémentaire
position de type entier pour chaque table. Les deux classes étendent la classe de base sfItem,
qui elle-même contient les colonnes id et name.
L'étape suivante consiste à ajouter quelques données de test sur lesquelles l'interface
d'administration pourra agir. Les jeux de données de test sont, comme toujours, situés dans le
fichier data/fixtures.yml du projet symfony.
sfTodoItem:
sfTodoItem_1:
name:
priority:
assigned_to:
sfTodoItem_2:
name:
priority:
assigned_to:
sfTodoItem_3:
name:
priority:
assigned_to:
sfTodoItem_4:
name:
priority:
assigned_to:
sfShoppingItem:
sfShoppingItem_1:
name:
quantity:
sfShoppingItem_2:
name:
quantity:
sfShoppingItem_3:
name:
quantity:
sfShoppingItem_4:
name:
quantity:
"Write a new symfony book"
"medium"
"Fabien Potencier"
"Release Doctrine 2.0"
"minor"
"Jonathan Wage"
"Release symfony 1.4"
"major"
"Kris Wallsmith"
"Document Lime 2 Core API"
"medium"
"Bernard Schussek"
"Apple MacBook Pro 15.4 inches"
3
"External Hard Drive 320 GB"
5
"USB Keyboards"
2
"Laser Printer"
1
Une fois le plugin csDoctrineActAsSortablePlugin correctement installé et le modèle de
données prêt, le nouveau plugin requiert d'être activé dans la classe de configuration
ProjectConfiguration du fichier config/ProjectConfiguration.class.php.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
$this->enablePlugins(array(
'sfDoctrinePlugin',
'csDoctrineActAsSortablePlugin'
));
}
}
Ensuite, la base de données, le modèle, les formulaires et les filtres doivent être générés et les
données initiales chargées dans la base afin d'alimenter les nouvelles tables créées. Cette tâche
est accomplie en une fois grâce à la commande doctrine:build de symfony.
$ php symfony doctrine:build --all --no-confirmation
Le cache de symfony doit enfin être nettoyé pour finaliser le processus, et les ressources web
des plugins doivent être publiées sous la racine web.
$ php symfony cache:clear
$ php symfony plugin:publish-assets
La section suivante explique enfin comment construire pas à pas les modules d'administration
avec les outils de l'Admin Generator, et comment profiter des classes d'actions de base
personnalisées.
Construire le Backend
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Cette partie du chapitre décrit les étapes nécessaires pour configurer une nouvelle application
d'administration. Celle-ci contiendra les deux modules générés qui gèrent les listes de courses et
de tâches. Par conséquent, la première chose à réaliser est de générer une application backend
pour héberger ces futurs modules.
$ php symfony generate:app backend
Avant symfony 1.3, et bien que l'Admin Generator soit un outil très abouti, les développeurs
étaient néanmoins contraints de dupliquer du code commun entre les différents modules
générés. Or, aujourd'hui, la tâche doctrine:generate-admin introduit une nouvelle option -actions-base-class qui permet au développeur de définir une classe d'actions de base pour
chaque module généré.
Comme les deux modules à générer sont sensiblement les mêmes, ils auront certainement
besoin de partager quelques actions communes. Le code de ces dernières peut ainsi être localisé
dans une classe d'actions plus générique située dans le répertoire lib/actions du projet. Le
code de cette classe maîtresse est présentée ci-dessous.
// lib/actions/sfSortableModuleActions.class.php
class sfSortableModuleActions extends sfActions
{
}
Après que la nouvelle classe sfSortableModuleActions ait été créée et le cache de symfony
nettoyé, les deux modules de l'application peuvent alors être générés dans l'application
backend.
$ php symfony doctrine:generate-admin --module=shopping --actions-baseclass=sfSortableModuleActions backend sfShoppingItem
$ php symfony doctrine:generate-admin --module=todo --actions-baseclass=sfSortableModuleActions backend sfTodoItem
L'Admin Generator génère des modules dans deux répertoires séparés. Le premier répertoire
conteneur est bien sûr apps/backend/modules bien qu'en réalité la majorité des modules
générés se trouve dans le répertoire cache/backend/dev/modules. Tous les fichiers localisés
dans ce répertoire sont régénérés à chaque fois que le cache est nettoyé ou bien lorsque la
configuration d'un module change.
Parcourir les fichiers mis en cache est une excellente manière de mieux comprendre comment
symfony et l'Admin Generator fonctionnent ensemble sous le capot. Par conséquent, les
nouvelles classes dérivées de sfSortableModuleActions peuvent être retrouvées
respectivement dans les fichiers
cache/backend/dev/modules/autoShopping/actions/actions.class.php et
cache/backend/dev/modules/autoTodo/actions/actions.class.php. Par défaut, symfony
aurait généré ces classes en les faisant hériter de la classe sfActions.
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Les deux modules d'administration sont à présent prêts à être utilisés et personnalisés. La
personnalisation des modules d'administration ne constitue pas l'objet de ce chapitre car cette
tâche est particulièrement bien documentée, à commencer par le livre de référence : symfony
Reference Book.
Modifier la Position d'un Enregistrement
La section précédente a décrit comment construire deux modules d'administration entièrement
fonctionnels et qui héritent de la même classe d'actions. L'objectif suivant est de créer une
action partagée qui offre au développeur la possibilité d'ordonner les objets d'une liste les uns
par rapport aux autres. Satisfaire ce besoin fonctionnel est une tâche aisée dans la mesure où le
plugin installé fournit une API complète pour manipuler les changements de position des objets.
La première étape à réaliser pour y parvenir consiste tout d'abord à créer deux nouvelles routes
capables de déplacer un enregistrement vers le haut ou vers le bas dans la liste. Comme le
générateur d'administration s'appuie sur une route sfDoctrineRouteCollection, les nouvelles
routes peuvent être déclarées et attachées à la collection grâce au fichier de configuration
config/generator.yml de chaque module:
# apps/backend/modules/shopping/config/generator.yml
generator:
class: sfDoctrineGenerator
param:
model_class:
sfShoppingItem
theme:
admin
non_verbose_templates: true
with_show:
false
singular:
~
plural:
~
route_prefix:
sf_shopping_item
with_doctrine_route:
true
actions_base_class:
sfSortableModuleActions
config:
actions: ~
fields: ~
list:
max_per_page:
100
sort:
[position, asc]
display:
[position, name, quantity]
object_actions:
moveUp:
{ label: "move up", action: "moveUp" }
moveDown:
{ label: "move down", action: "moveDown" }
_edit:
~
_delete:
~
filter: ~
form:
~
edit:
~
new:
~
Les modifications apportées dans le fichier précédent doivent être répétées pour le fichier de
configuration du module todo comme le montre le code ci-dessous.
# apps/backend/modules/todo/config/generator.yml
generator:
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
class: sfDoctrineGenerator
param:
model_class:
sfTodoItem
theme:
admin
non_verbose_templates: true
with_show:
false
singular:
~
plural:
~
route_prefix:
sf_todo_item
with_doctrine_route:
true
actions_base_class:
sfSortableModuleActions
config:
actions: ~
fields: ~
list:
max_per_page:
sort:
display:
object_actions:
moveUp:
moveDown:
_edit:
~
_delete:
~
filter: ~
form:
~
edit:
~
new:
~
100
[position, asc]
[position, name, priority, assigned_to]
{ label: "move up", action: "moveUp" }
{ label: "move down", action: "moveDown" }
Ces deux fichiers YAML décrivent la configuration pour les deux modules shopping et todo.
Chacun d'entre eux a été personnalisé afin de convenir aux besoins de l'utilisateur final. Tout
d'abord, la vue liste est à présent triée par ordre ascendant suivant la colonne position.
Ensuite, le nombre maximum d'enregistrements par page a été augmenté jusqu'à la valeur 100
afin d'empêcher une pagination trop hâtive.
Enfin, le nombre de colonnes affichées a été réduit à l'ensemble suivant : position, name,
priority, assigned_to et quantity. De plus, chaque module dispose désormais de deux
nouvelles actions : moveUp et moveDown. Le rendu final de ces deux modules devrait ressembler
aux captures d'écran ci-après.
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
Ces deux nouvelles actions ont été déclarées mais ne réalisent encore rien pour le moment.
Chacune d'entre elles doit être explicitement créée dans la classe d'actions partagées,
sfSortableModuleActions, comme le montre le listing ci-dessous. Le plugin
csDoctrineActAsSortablePlugin fournit deux méthodes supplémentaires pour chaque objet de
modèle : promote() et demote(). Ces deux méthodes sont ici respectivement utilisées dans les
actions moveUp et moveDown.
// lib/actions/sfSortableModuleActions.class.php
class sfSortableModuleActions extends sfActions
{
/**
* Moves an item up in the list.
*
* @param sfWebRequest $request
*/
public function executeMoveUp(sfWebRequest $request)
{
$this->item = $this->getRoute()->getObject();
$this->item->promote();
$this->redirect($this->getModuleName());
}
/**
* Moves an item down in the list.
*
* @param sfWebRequest $request
*/
public function executeMoveDown(sfWebRequest $request)
{
$this->item = $this->getRoute()->getObject();
$this->item->demote();
$this->redirect($this->getModuleName());
}
}
Grâce à ces deux actions partagées, les deux listes de courses et de tâches sont désormais
ordonnables. Par ailleurs, elles sont beaucoup plus faciles à maintenir et à tester à l'aide de tests
fonctionnels. N'hésitez pas à améliorer l'interface et l'expérience utilisateur de chaque module en
redéfinissant les templates d'actions d'objet afin de supprimer le premier lien moveUp et le
dernier lien moveDown de chaque liste.
Bonus: Améliorer l'Expérience Utilisateur
Avant de clore ce chapitre, il convient de parfaire les deux listes en améliorant l'expérience
utilisateur. Tout le monde s'accorde à dire que déplacer un enregistrement vers le haut (ou vers
le bas) en cliquant sur un lien n'est pas si intuitif qu'il n'y parait pour l'utilisateur final.
Une bien meilleure approche consiste définitivement à inclure des comportements JavaScript et
Ajax pour rendre les lignes du tableau déplaçables. Une fois de plus, il n'est pas question de
réinventer la roue, c'est pourquoi ces comportements Ajax seront traités à l'aide du plugin
jQuery Table Drag and Drop. Un appel Ajax sera exécuté à chaque fois que l'utilisateur
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
déplacera une ligne du tableau HTML à une autre position dans celui-ci.
La première étape de cette nouvelle série d'améliorations consiste à récupérer et installer le
framework jQuery dans le répertoire web/js. Puis il s'agit de répéter cette même opération pour
le plugin Table Drag and Drop, dont les sources sont hébergées dans un dépôt Subversion
Google Code. Il ne reste plus qu'à inclure ces deux scripts JavaScript dans le layout de
l'application ou bien dans le fichier de configuration view.yml.
Pour fonctionner, la vue liste de chaque module doit inclure un petit morceau de code JavaScript
et les deux tableaux HTML doivent également posséder un attribut id. Comme tous les
templates et les partiels de l'Admin Generator sont surchargeables, le fichier _list.php (situé
dans le cache par défaut) devrait alors être copié dans chaque module.
Cependant, copier le fichier _list.php dans le répertoire templates/ de chaque module n'est
pas tout à fait DRY. Il suffit en effet de copier le fichier
cache/backend/dev/modules/autoShopping/templates/_list.php dans le répertoire
apps/backend/templates, puis le renommer en _table.php. Ensuite, le contenu actuel de ce
nouveau fichier doit être remplacé par le code suivant.
<div class="sf_admin_list">
<?php if (!$pager->getNbResults()): ?>
<p><?php echo __('No result', array(), 'sf_admin') ?></p>
<?php else: ?>
<table cellspacing="0" id="sf_item_table">
<thead>
<tr>
<th id="sf_admin_list_batch_actions"><input
id="sf_admin_list_batch_checkbox" type="checkbox" onclick="checkAll();" /></th>
<?php include_partial(
$sf_request->getParameter('module').'/list_th_tabular',
array('sort' => $sort)
) ?>
<th id="sf_admin_list_th_actions">
<?php echo __('Actions', array(), 'sf_admin') ?>
</th>
</tr>
</thead>
<tfoot>
<tr>
<th colspan="<?php echo $colspan ?>">
<?php if ($pager->haveToPaginate()): ?>
<?php include_partial(
$sf_request->getParameter('module').'/pagination',
array('pager' => $pager)
) ?>
<?php endif; ?>
<?php echo format_number_choice(
'[0] no result|[1] 1 result|(1,+Inf] %1% results',
array('%1%' => $pager->getNbResults()),
$pager->getNbResults(), 'sf_admin'
) ?>
<?php if ($pager->haveToPaginate()): ?>
<?php echo __('(page %%page%%/%%nb_pages%%)', array(
'%%page%%' => $pager->getPage(),
'%%nb_pages%%' => $pager->getLastPage()),
'sf_admin'
) ?>
<?php endif; ?>
</th>
</tr>
</tfoot>
<tbody>
<?php foreach ($pager->getResults() as $i => $item): ?>
<?php $odd = fmod(++$i, 2) ? 'odd' : 'even' ?>
<tr class="sf_admin_row <?php echo $odd ?>">
<?php include_partial(
$sf_request->getParameter('module').'/list_td_batch_actions',
array(
'sf_'. $sf_request->getParameter('module') .'_item' => $item,
'helper' => $helper
)) ?>
<?php include_partial(
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
$sf_request->getParameter('module').'/list_td_tabular',
array(
'sf_'. $sf_request->getParameter('module') .'_item' => $item
)) ?>
<?php include_partial(
$sf_request->getParameter('module').'/list_td_actions',
array(
'sf_'. $sf_request->getParameter('module') .'_item' => $item,
'helper' => $helper
)) ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<script type="text/javascript">
/* <![CDATA[ */
function checkAll() {
var boxes = document.getElementsByTagName('input');
for (var index = 0; index < boxes.length; index++) {
box = boxes[index];
if (
box.type == 'checkbox'
&&
box.className == 'sf_admin_batch_checkbox'
)
box.checked = document.getElementById('sf_admin_list_batch_checkbox').checked
}
return true;
}
/* ]]> */
</script>
Enfin, il ne reste plus qu'à créer un nouveau fichier _list.php à l'intérieur de chaque répertoire
templates/ des deux modules, et d'ajouter les codes suivants dans chacun d'eux.
// apps/backend/modules/shopping/templates/_list.php
<?php include_partial('global/table', array(
'pager' => $pager,
'helper' => $helper,
'sort' => $sort,
'colspan' => 5
)) ?>
// apps/backend/modules/shopping/templates/_list.php
<?php include_partial('global/table', array(
'pager' => $pager,
'helper' => $helper,
'sort' => $sort,
'colspan' => 8
)) ?>
Pour changer la position d'une ligne, les deux modules ont besoin d'implémenter une nouvelle
action qui se charge de traiter la requête ajax à venir. Comme il l'a été montré précédemment
dans ce chapitre, la nouvelle action executeMove() trouve naturellement sa place dans la classe
d'actions communes sfSortableModuleActions.
// lib/actions/sfSortableModuleActions.class.php
class sfSortableModuleActions extends sfActions
{
/**
* Performs the Ajax request, moves an item to a new position.
*
* @param sfWebRequest $request
*/
public function executeMove(sfWebRequest $request)
{
$this->forward404Unless($request->isXmlHttpRequest());
$this->forward404Unless($item = Doctrine_Core::getTable($this->configuration>getModel())->find($request->getParameter('id')));
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
$item->moveToPosition((int) $request->getParameter('rank', 1));
return sfView::NONE;
}
}
L'action executeMove() fait appel à une méthode getModel() sur l'objet de configuration de
l'Admin Generator. Cette nouvelle méthode doit donc être implémentée dans les deux classes de
configuration todoGeneratorConfiguration et shoppingGeneratorConfiguration comme
expliqué ci-dessous.
// apps/backend/modules/shopping/lib/shoppingGeneratorConfiguration.class.php
class shoppingGeneratorConfiguration extends BaseShoppingGeneratorConfiguration
{
public function getModel()
{
return 'sfShoppingItem';
}
}
// apps/backend/modules/todo/lib/todoGeneratorConfiguration.class.php
class todoGeneratorConfiguration extends BaseTodoGeneratorConfiguration
{
public function getModel()
{
return 'sfTodoItem';
}
}
Il reste encore une toute dernière opération à réaliser. En effet, pour l'instant, les lignes des
deux tableaux ne sont pas déplaçables et aucune requête ajax n'est exécutée lorsqu'une ligne
du tableau est relâchée. Pour y parvenir, les deux modules ont besoin chacun d'une route
spécifique pour accéder à leur action move respective. Par conséquent, le fichier
apps/backend/config/routing.yml doit accueillir les deux nouvelles routes ci-dessous.
<?php foreach (array('shopping', 'todo') as $module) : ?>
<?php echo $module ?>_move:
class: sfRequestRoute
url: /<?php echo $module ?>/move
param:
module: "<?php echo $module ?>"
action: move
requirements:
sf_method: [get]
<?php endforeach ?>
Afin d'éviter la duplication de code, les deux routes sont générées dynamiquement à l'intérieur
d'une boucle foreach, et leur identifiant repose sur le nom du module afin de pouvoir les
retrouver facilement dans la vue.
Enfin, il ne reste plus que le fichier apps/backend/templates/_table.php qui doit implémenter
le code JavaScript nécessaire à l'initialisation du comportement de glisser / déposer, et les
requêtes Ajax correspondantes.
<script type="text/javascript" charset="utf-8">
$().ready(function() {
$("#sf_item_table").tableDnD({
onDrop: function(table, row) {
var rows = table.tBodies[0].rows;
// Get the moved item's id
var movedId = $(row).find('td input:checkbox').val();
// Calculate the new row's position
var pos = 1;
for (var i = 0; i<rows.length; i++) {
var cells = rows[i].childNodes;
// Perform the ajax request for the new position
if (movedId == $(cells[1]).find('input:checkbox').val()) {
$.ajax({
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Tirer Profit de l'Héritage de Table avec Doctrine | symfony | Web PHP Framework
url:"<?php echo url_for('@'. $sf_request>getParameter('module').'_move') ?>?id="+ movedId +"&rank="+ pos,
type:"GET"
});
break;
}
pos++;
}
},
});
});
</script>
Le tableau HTML est à présent entièrement fonctionnel. Les lignes sont glissables et déposables,
et la nouvelle position d'une ligne déplacée est automatiquement sauvée à l'aide d'un appel
Ajax. Avec seulement quelques lignes de code, l'usabilité de l'interface d'administration a été
grandement améliorée afin d'offrir une meilleure expérience utilisateur. L'Admin Generator est
suffisamment flexible pour être étendu et personnalisé, et il fonctionne de plus parfaitement
avec l'héritage de table Doctrine.
N'hésitez pas à améliorer ces deux modules en retirant les actions moveUp et moveDown
obsolètes, ou bien en ajoutant d'autres personnalisations qui conviennent à vos besoins.
Conclusion
Ce chapitre a décrit combien l'héritage de table de Doctrine est une puissante fonctionnalité qui
aide le développeur à coder plus vite, mais aussi à améliorer significativement l'organisation de
son code.
Cet outil de Doctrine est entièrement intégré à plusieurs niveaux dans symfony. Ainsi, les
développeurs sont désormais fortement encouragés à l'utiliser et en tirer parti dans le but
d'augmenter leur efficacité, d'améliorer l'organisation de leur code et bien sûr de parfaire la
productivité de leurs projets.
« Techniques Avancées avec Doctrine
Plonger dans les Entrailles de Symfony »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/09-Doctrine-Form-Inheritance[31/12/2010 02:34:10]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Plonger dans les Entrailles de Symfony
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Geoffrey Bachelet
Vous-êtes vous déjà posé la question de savoir ce qui arrive à une
requête HTTP lorsqu'elle atteint une application symfony ? Si oui, vous
êtes au bon endroit.
Ce chapitre expliquera en profondeur comment symfony traite chaque
requête pour créer et retourner une réponse. Bien sur, il ne décrira pas
seulement le processus car cela manquerait d'intérêt. Par conséquent,
ce chapitre s'intéressa à étudier ce qu'il est possible de réaliser et à
quels endroits le développeur peut interagir au cours de ce processus.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
L'Amorçage : le Bootstrap
Tout commence dans le contrôleur de l'application. En considérant que
le projet est configuré de manière traditionnelle avec un contrôleur
frontend et un environnement de développement dev, il en résulte donc qu'un contrôleur
frontal pour cette configuration est présent dans le fichier web/frontend_dev.php.
Que se passe-t-il exactement dans ce fichier ? En quelques lignes de code, symfony récupère la
configuration de l'application puis crée une instance de la classe sfContext, qui se charge de
l'expédition de la requête. La configuration de l'application est nécessaire lors de la création de
l'objet sfContext, qui demeure également le moteur dont dépend l'application.
Symfony donne déjà un peu de contrôle au développeur sur ce qu'il se passe en lui
permettant de passer un répertoire racine personnalisé en quatrième argument de
ProjectConfiguration::getApplicationConfiguration(), ainsi qu'une classe de contexte
personnalisée dans le troisième (et dernier) argument de sfContext::createInstance().
Cette classe doit bien évidemment étendre la classe originale sfContext.
La récupération de la configuration de l'application est une étape très importante. Tout d'abord,
l'objet sfProjectConfiguration s'occupe de deviner la classe de configuration de l'application,
qui se trouve plus généralement dans la classe ${application}Configuration du fichier
apps/${application}/config/${application}Configuration.class.php.
La classe sfApplicationConfiguration étend en réalité la classe ProjectConfiguration, ce
qui signifie que toute méthode définie dans ProjectConfiguration est ainsi partagée entre
toutes les applications. Cela signifie aussi que la classe sfApplicationConfiguration partage
son constructeur avec les classes ProjectConfiguration et sfProjectConfiguration. C'est un
avantage car la plupart du projet est configurée dans le constructeur de
sfProjectConfiguration.
Dans un premier temps, plusieurs valeurs utiles seront calculées et stockées. Ces dernières
contiennent par exemple les variables de configuration du répertoire parent de l'application ainsi
que le répertoire dans lequel se trouvent les librairies de symfony. L'objet
sfProjectConfiguration crée aussi un nouvel objet d'expédition d'évènement, "l'event
dispatcher", de type sfEventDispatcher ; à moins qu'un autre événement ait été fourni comme
cinquième argument de la méthode statique
ProjectConfiguration::getApplicationConfiguration() du contrôleur frontal.
Juste après cela, le développeur a la possibilité d'interagir avec le processus de configuration en
redéfinissant la méthode setup() de l'objet ProjectConfiguration. C'est en effet le meilleur
endroit pour activer ou bien désactiver des plugins en utilisant au choix les méthodes d'instance
sfProjectConfiguration::setPlugins(), sfProjectConfiguration::enablePlugins(),
sfProjectConfiguration::disablePlugins() or
sfProjectConfiguration::enableAllPluginsExcept().
Ensuite, les plugins sont chargés par l'intermédiaire de la méthode
sfProjectConfiguration::loadPlugins(), et le développeur peut alors prendre le contrôle sur
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
Chapter Content
L'Amorçage : le Bootstrap
Procédure d'Amorçage et Résumé de
la Configuration
L'Objet sfContext et les Factories
Utiliser l'Evénement
request.filter_parameter
Exemple d'Utilisation de l'Evénement
routing.load_configuration
Tirer Profit de l'Evénement
template.filter_parameters
Résumé de sfContext
Quelques Mots à propos des
Gestionnaires de Configuration
L'Expédition et l'Exécution de la
Requête
Résumé du Processus d'Expédition
La Chaîne de Filtres
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
ce processus via la méthode sfProjectConfiguration::setupPlugins() qui peut aussi être
redéfinie.
Le Filtre de Sécurité
L'initialisation des plugins est assez simple. Pour chaque plugin, symfony vérifie l'existence d'une
classe ${plugin}Configuration, par exemple sfGuardPluginConfiguration, qu'il instancie si
elle existe. Sinon, la classe sfPluginConfigurationGeneric est utilisée. Il est possible
d'intervenir dans la configuration d'un plugin à travers deux méthodes.
Le Filtre de Rendu
${plugin}Configuration::configure(), avant que l'autochargement de classes soit
réalisé,
Le Filtre du Cache
Le Filtre d'Exécution
Résumé de l'Exécution de la Chaîne
de Filtrage
Résumé général
Conclusion
${plugin}Configuration::initialize(), après l'autochargement de classes.
Puis, l'objet sfApplicationConfiguration exécute sa méthode configure(), qui peut alors
être utilisée pour personnaliser chaque configuration d'application avant que l'essentiel du
processus d'initialisation de configuration interne ne commence réellement dans
sfApplicationConfiguration::initConfiguration().
Cette partie du processus de configuration de symony s'occupe de plein de choses et il y a
plusieurs points d'entrées pour lesquels le développeur a la capacité d'agir au cours du
processus. Par exemple, il peut interagir avec la configuration de l'autochargeur de classes en le
connectant à l'évènement autoload.filter_config.
Puis, plusieurs fichiers de configuration importants sont chargés, dont les fichiers settings.yml
and app.yml. Enfin, un dernier morceau de configuration est disponible à travers chaque fichier
config/config.php ou alors à partir de la méthode initialize() de la classe de configuration.
Si le paramètre sf_check_lock est activé, symfony cherchera un fichier de verrouillage, qui
aura été créé par l'exécution de la commande project:disable par exemple. Si ce fichier
existe, les fichiers suivants sont recherchés, et le premier qui est disponible est alors inclus. A
ce moment là, le script se termine immédiatement.
1. apps/${application}/config/unavailable.php,
2. config/unavailable.php,
3. web/errors/unavailable.php,
4. lib/vendor/symfony/lib/exception/data/unavailable.php,
Enfin, le développeur dispose encore d'une dernière chance de personnaliser l'initialisation de
l'application à travers la méthode sfApplicationConfiguration::initialize().
Procédure d'Amorçage et Résumé de la Configuration
Récupération de la configuration de l'application,
ProjectConfiguration::setup() (définition des plugins ici),
Chargement des plugins,
${plugin}Configuration::configure(),
${plugin}Configuration::initialize(),
ProjectConfiguration::setupPlugins() (installation des plugins ici),
${application}Configuration::configure(),
autoload.filter_config est notifié,
Chargement des fichiers settings.yml et app.yml,
${application}Configuration::initialize(),
Création d'une instance de sfContext.
L'Objet sfContext et les Factories
Avant de plonger dans le processus d'expédition, il convient de parler d'un point vital du flux de
traitement de symfony : les factories.
Dans symfony, les factories sont un ensemble de composants ou de classes sur lesquels se base
l'application. logger, i18n sont des exemples de ces factories, et chacune d'elles est configurée
dans le fichier factories.yml. Ce dernier est ensuite compilé par un gestionnaire de
configuration (il sera évoqué plus tard) et converti en code PHP brut qui instancie les objets
factory. Le code compilé est disponible dans le cache du projet dans le fichier
cache/frontend/dev/config/config_factories.yml.php.
Le chargement des factories intervient après l'initialisation de sfContext. Pour plus
d'informations, veuillez consulter sfContext::initialize() et
sfContext::loadFactories()
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
Au point où nous en sommes, il est déjà possible de personnaliser une grande partie du
comportement de symfony en éditant le fichier de configuration factories.yml. Les classes
factory de symfony peuvent d'ailleurs même être remplacées par des classes personnalisées.
Pour en savoir plus au sujet des factories, Le Livre de Référence de symfony et le fichier
factories.yml sont des ressources inestimables.
En regardant de plus près le fichier config_factories.yml.php généré, on s'aperçoit que les
factories sont instanciées dans un ordre précis. Ce dernier est très important car certaines
factories sont dépendantes des autres. C'est le cas, par exemple, du composant routing qui a
bien évidemment besoin du composant request pour retrouver les informations dont il a besoin.
Qu'en est-il par exemple de la factory request ? Par défaut, la classe sfWebRequest représente
l'objet de la requête de l'utilisateur (request). A l'instanciation, sfWebRequest::initialize()
est appelée, et collecte diverses informations pertinentes telles que les paramètres GET / POST
et la méthode HTTP utilisée. Des traitements complémentaires sur cet objet peuvent être
réalisés à l'aide de l'évènement request.filter_parameters.
Utiliser l'Evénement request.filter_parameter
En supposant par exemple un site qui propose une API publique aux utilisateurs, celle-ci sera
rendue disponible à travers le protocole HTTP, et chaque utilisateur désirant en faire usage
devra fournir une clé valide dans l'en-tête de la requête (par exemple X_API_KEY) afin d'être
autorisé par l'application. Ce mécanisme peut être mis en place très facilement en utilisant
l'événement request.filter_parameter.
class apiConfiguration extends sfApplicationConfiguration
{
public function configure()
{
// ...
$this->dispatcher->connect('request.filter_parameters', array(
$this, 'requestFilterParameters'
));
}
public function requestFilterParameters(sfEvent $event, $parameters)
{
$request = $event->getSubject();
$api_key = $request->getHttpHeader('X_API_KEY');
if (null === $api_key || false === $api_user = Doctrine_Core::getTable('ApiUser')>findOneByToken($api_key))
{
throw new RuntimeException(sprintf('Invalid api key "%s"', $api_key));
}
$request->setParameter('api_user', $api_user);
return $parameters;
}
}
La valeur de la clé utilisateur de l'API publique sera donc accessible depuis l'objet de requête de
symfony.
public function executeFoobar(sfWebRequest $request)
{
$api_user = $request->getParameter('api_user');
}
Cette technique peut servir entre autres à valider des appels sur des services web.
L'événement request.filter_parameters intègre de nombreuses informations sur la
requête. L'API de la méthode sfWebRequest::getRequestContext() est disponible en ligne
pour plus d'informations.
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
La factory suivante importante concerne le routage. L'initialisation du routage est relativement
simple et consiste principalement en la récupération et le paramétrage de certaines options. Des
interactions complémentaires avec le processus sont disponibles au travers de l'événement
routing.load_configuration.
L'événement routing.load_configuration donne accès à l'instance de l'objet de routing
courant Par défaut, il s'agit de l'objet
sfPatternRouting). Les routes enregistrées sont alors manipulables de plusieurs manières.
Exemple d'Utilisation de l'Evénement routing.load_configuration
Par exemple, il est possible d'ajouter une route très facilement de manière programmatique.
public function setup()
{
// ...
$this->dispatcher->connect('routing.load_configuration', array(
$this, 'listenToRoutingLoadConfiguration'
));
}
public function listenToRoutingLoadConfiguration(sfEvent $event)
{
$routing = $event->getSubject();
if (!$routing->hasRouteName('my_route'))
{
$routing->prependRoute('my_route', new sfRoute(
'/my_route', array('module' => 'default', 'action' => 'foo')
));
}
}
L'analyse de l'URL intervient après l'initialisation, via la méthode sfPatternRouting::parse().
Plusieurs méthodes sont impliquées dans ce processus, mais il suffit de dire que lorsque
l'exécution de la méthode parse() arrive à sa fin, la route correspondante a été trouvée,
instanciée et liée à des paramètres pertinents.
Pour plus d'informations au sujet du routing, le lecteur est invité à se reporter au chapitre
Techniques Avancées de Routage de cet ouvrage.
Lorsque toutes les factories ont été chargées et correctement initialisées, l'événement
context.load_factories est alors déclenché. Cet événement est important car c'est celui, au
niveau du framework, qui intervient le plus tôt et pour lequel le développeur peut avoir accès à
tous les objets factory de base de symfony (requête, réponse, utilisateur, identification, base de
données, etc.).
Il est aussi temps de se connecter à un autre événement très utile : l'événement
template.filter_parameters qui se produit chaque fois qu'un fichier est rendu par l'objet
sfPHPView. Il permet notamment au développeur de contrôler les paramètres envoyés au
template. L'objet sfContext utilise cet événement pour ajouter des paramètres utiles à chaque
template tels que les variables $sf_context, $sf_request, $sf_params, $sf_response et
$sf_user. Se connecter à l'événement template.filter_parameters peut ainsi permettre de
passer des paramètres globaux additionnels aux templates.
Tirer Profit de l'Evénement template.filter_parameters
Il arrive parfois que tous les templates d'une même application aient besoin d'avoir accès à un
objet particulier supplémentaire : un objet helper par exemple. Grâce à l'évènement
template.filter_parameters, le développeur est capable de transmettre à tous les templates
de l'application des paramètres supplémentaires de manière élégante en surchargeant la classe
ProjectConfiguration du projet.
public function setup()
{
// ...
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
$this->dispatcher->connect('template.filter_parameters', array(
$this, 'templateFilterParameters'
));
}
public function templateFilterParameters(sfEvent $event, $parameters)
{
$parameters['my_helper_object'] = new MyHelperObject();
return $parameters;
}
A présent, chaque template a accès à une instance de la classe MyHelperObject à travers la
variable $my_helper_object.
Résumé de sfContext
1. Initialisation de sfContext
2. Chargement des factories
3. Notifications des événements suivants :
1. request.filter_parameters
2. routing.load_configuration
3. context.load_factories
4. Ajout des paramètres globaux aux templates
Quelques Mots à propos des Gestionnaires de Configuration
Les gestionnaires de configuration, appelés aussi config handlers dans symfony, sont au coeur
du système de configuration du framework. Un gestionnaire de configuration a pour objectif de
comprendre ce qui se cache derrière un fichier de configuration.
Chaque gestionnaire de configuration est une classe qui est utilisée pour convertir un ensemble
de fichiers de configuration YAML en un bloc de code PHP qui peut être exécuté au besoin.
Chaque fichier de configuration est affecté à un gestionnaire de configuration spécifique dans le
fichier config_handlers.yml.
Pour être plus précis, la responsabilité d'un gestionnaire de configuration n'est pas d'analyser les
fichiers YAML car cette tâche est déjà remplie par la classe sfYaml. Chaque gestionnaire de
configuration crée un ensemble de directives PHP basées sur les informations du YAML et
sauvegarde ces dernières dans un fichier PHP qui pourra être chargé plus tard. La version
compilée de chaque fichier de configuration YAML peut être récupérée dans le répertoire de
cache.
Le gestionnaire de configuration utilisé le plus fréquemment est très certainement
sfDefineEnvironmentConfigHandler, qui permet de configurer des données en fonction de
l'environnement d'exécution. Ce gestionnaire de configuration s'occupe de récupérer uniquement
les paramètres de l'environnement courant.
Toujours pas convaincu ? Les quelques lignes suivantes expliquent ce que réalise le gestionnaire
de configuration
sfFactoryConfigHandler. Ce gestionnaire de configuration est utilisé pour compiler le fichier
factories.yml, qui est l'un des fichiers de configuration les plus importants dans symfony. Il
est particulier dans la mesure où il convertit un fichier de configuration YAML en un code PHP
qui se chargera à son tour d'instancier les factories. C'est ce dont il a été question un peu plus
haut dans ce chapitre. Cette classe n'est donc pas n'importe quel gestionnaire de configuration,
n'est-ce pas ?
L'Expédition et l'Exécution de la Requête
Assez parlé des factories, il s'agit à présent d'aborder le processus d'expédition de la requête, le
dispatching. Une fois l'initialisation de l'objet sfContext terminée, la dernière étape consiste à
appeler la méthode dispatch() du contrôleur sfFrontWebController::dispatch().
Le processus d'expédition de symfony est très simple. En réalité,
sfFrontWebController::dispatch(), prend les nom du module et de l'action depuis les
paramètres de la requête, puis les transmet à l'application via la méthode
sfController::forward().
A ce stade, si le routage n'a pas pu récupérer un nom de module ou un nom d'action depuis
l'url courante, une exception sfError404Exception est levée, ce qui conduit
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
automatiquement au transfert de la requête vers le module qui s'occupe de traiter les erreurs
404 (voir sf_error_404_module et sf_error_404_action). Il est important de noter qu'il
suffit de lever ce type d'exception depuis n'importe où dans l'application afin de reproduire ce
résultat.
La méthode forward est responsable d'une multitude de vérifications de pré-exécution, mais
également de la préparation et de la configuration des données de l'action qui sera exécutée.
Tout d'abord, le contrôleur vérifie la présence d'un fichier generator.yml pour le module
courant. Cette vérification est effectuée en premier (après un bref nettoyage des noms du
module et de l'action) car le fichier de configuration generator.yml, s'il existe, s'occupe de la
génération de la classe d'actions de base pour le module (à travers son gestionnaire de
configuration, sfGeneratorConfigHandler). C'est d'autant plus nécessaire pour l'étape suivante
du fait de la vérification de l'existence du module et de l'action. C'est le contrôleur qui s'en
occupe, à travers la méthode sfController::actionExists(), qui appelle en retour la
méthode sfController::controllerExists() Ici encore, si la méthode actionExists()
renvoie faux, une exception sfError404Exception est levée.
L'objet sfGeneratorConfigHandler est un gestionnaire de configuration spécial qui s'occupe
d'instancier la bonne classe de génération pour le module avant de l'exécuter. Pour plus
d'informations sur les gestionnaires de configuration, référez-vous à la section Quelques mots
à propos des gestionnaires de configuration de ce chapitre. Par ailleurs, pour en savoir plus
sur le fichier generator.yml, veuillez consulter le chapitre 6 du livre de référence de
symfony.
Il n'est pas possible de faire plus à ce stade, si ce n'est de surcharger la méthode
sfApplicationConfiguration::getControllerDirs() dans la classe de configuration de
l'application. Cette méthode retourne un tableau de répertoires dans lesquels se trouvent les
fichiers du contrôleur, avec un paramètre additionnel pour spécifier à symfony s'il doit vérifier ou
non l'activation des contrôleurs via l'option de configuration sf_enabled_modules de
settings.yml. Par exemple, getControllerDirs() pourrait ressembler à quelque chose de
similaire au code ci-dessous.
/**
* Controllers in /tmp/myControllers won't need to be enabled
* to be detected
*/
public function getControllerDirs($moduleName)
{
return array_merge(parent::getControllerDirs($moduleName), array(
'/tmp/myControllers/'.$moduleName => false
));
}
Si l'action n'existe pas, une exception sfError404Exception est levée.
La prochaine étape consiste à récupérer une instance du contrôleur contenant l'action. Cette
étape est gérée par la méthode sfController::getAction() qui, comme la méthode
actionExists(), est une façade pour la méthode sfController::getController().
Finalement, l'instance du contrôleur est ajoutée à la pile d'appel des actions : action
stack.
La pile d'appel des actions est une pile de type FIFO (First In First Out) qui contient toutes
les actions exécutées pendant la requête courante. Chaque élément de la pile est encapsulé
dans un objet sfActionStackEntry. La pile est quant à elle disponible à partie de la méthode
sfContext::getInstance()->getActionStack() ou bien avec le code $this>getController()->getActionStack() depuis une action.
Après quelques chargements de configuration supplémentaires, l'application sera prête à
exécuter l'action demandée. La configuration spécifique au module doit toujours être chargée, et
peut être trouvée à deux endroits différents. Tout d'abord, symfony cherchera après le fichier
module.yml, normalement présent dans
apps/frontend/modules/yourModule/config/module.yml.
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
Ce dernier étant un fichier de configuration YAML, il est par défaut stocké dans le cache. De
plus, ce fichier de configuration peut déclarer un module comme étant interne (internal), en
spécifiant le paramètre mod_yourModule_is_internal. Par conséquent, cela entraîne l'échec
d'une requête car un module interne ne peut pas être appelé publiquement.
Les modules internes étaient utilisés à l'origine pour générer du contenu de mail, grâce à la
méthode getPresentationFor() par exemple. D'autres techniques existent aujourd'hui pour
cela comme le rendu d'un template partiel à l'aide de la méthode renderPartial() d'une
classe d'actions.
Maintenant que le fichier module.yml est chargé, il est temps de vérifier une deuxième fois que
le module courant est activé. En effet, à ce stade, il est possible de modifier la valeur de
mod_$moduleName_enabled à false afin de désactiver le module.
Comme il l'a été précisé, il y'a deux manières d'activer ou de désactiver un module. La
différence est le résultat si le module est désactivé. Dans le premier cas, quand le paramètre
sf_enabled_modules est vérifié, un module désactivé entrainera la levée d'une exception
sfConfigurationException. Cette manière devrait être utilisée pour désactiver un module
durablement. Dans le second cas, via le paramètre mod_$moduleName_enabled, un module
désactivé entrainera un transfert de l'application vers le module désactivé. (voir le paramètre
sf_module_disabled_module et sf_module_disabled_action ). Il est vivement recommandé
d'utiliser cette manière lorsqu'il s'agit de >désactiver un module temporairement.
La dernière opportunité de configurer un module réside dans le fichier config.php
(apps/frontend/modules/yourModule/config/config.php) dans lequel du code PHP arbitraire
peut être déposé avant son exécution dans le contexte de la méthode
sfController::forward(). L'instance de la classe sfControler est disponible dans la variable
$this dans la mesure où le code est exécuté dans la classe sfController.
Résumé du Processus d'Expédition
1. sfFrontWebController::dispatch() est appelée
2. sfController::forward() est appelée
3. Vérification de l'existence du fichier generator.yml
4. Vérification de l'existence du module et de l'action
5. Récupération d'une liste de répertoires de contrôleurs
6. Récupération d'une instance de l'action
7. Chargement du module de configuration d'après le fichier module.yml et / ou config.php.
La Chaîne de Filtres
Maintenant que toute la configuration est prête, il est temps de commencer le véritable travail.
Il s'agit, dans ce cas particulier, de l'exécution de la chaîne de filtrage.
La chaine de filtres de symfony implémente un motif de conception connu sous le nom de
chaîne de responsabilités. Il s'agit d'un motif simple mais puissant qui permet de déterminer
si l'exécution de la chaîne doit continuer ou non pour chaque maillon de celle-ci. Chaque
maillon de la chaîne est aussi capable de s'exécuter avant et après le reste de la chaîne
d'exécution.
La configuration de la chaîne de filtrage est tirée du fichier filters.yml du module courant, ce
qui explique pourquoi une instance de l'action est nécessaire. L'ensemble des filtres de la chaîne
et l'ordre dans lequel ils sont appelés, peuvent être modifiés. Il ne faut pas pas oublier que le
filtre de rendu doit toujours être le premier dans la liste ; les explications sont données plus
loin. La configuration des filtres est la suivante par défaut.
rendering
security
cache
execution
rendering
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
security
cache
execution
Il est fortement recommandé d'ajouter des filtres personnalisés entre les >filtres security et
cache.
Le Filtre de Sécurité
Etant donné que le filtre de rendu attend que les autres filtres se terminent avant de réaliser
quoi que soit, le premier filtre exécuté en réalité est le filtre de sécurité. Ce filtre s'assure que
tout est correct en fonction de ce qui a été écrit dans le fichier de configuration security.yml.
Plus précisément, le filtre se charge de rediriger un utilisateur non identifié vers le module et
l'action de login.
En revanche, si l'utilisateur n'a pas des droits d'accès suffisants pour le module et l'action
demandés, le filtre transférera l'utilisateur vers le module secure. Il n'est pas inintéressant de
remarquer que ce filtre est exécuté si la sécurité est activée pour l'action donnée.
Le Filtre du Cache
Le filtre de cache intervient ensuite. Ce filtre a la possibilité d'empêcher l'exécution de filtres
futurs. En effet, si le cache est activé, et que les données nécessaires sont déjà disponibles,
pourquoi vouloir chercher à exécuter l'action quand même ? Bien sûr, ceci ne fonctionnera que
pour des pages qui peuvent être chargées en cache intégralement, ce qui n'est pas le cas de la
majorité des pages.
Ce filtre a toutefois une partie de code qui s'exécute après le filtre d'exécution et juste avant le
filtre de rendu. Ce code permet de paramétrer les bons en-têtes de cache HTTP, et de placer les
pages en cache si nécessaire, grâce à la méthode sfViewCacheManager::setPageCache()
Le Filtre d'Exécution
Enfin, le filtre d'exécution prend en charge l'exécution de la logique métier et gérera la vue
associée. Tout commence quand le filtre vérifie le cache pour l'action courante. Bien sûr, si
quelque chose se trouve dans le cache, l'exécution de l'action actuelle sera annulée et la vue
Success sera quant à elle exécutée.
Si l'action n'est pas trouvée dans le cache, alors il est temps d'exécuter la méthode
preExecute() du contrôleur, et d'exécuter enfin l'action elle-même. Ceci est accompli par une
d'instance de la classe d'action via un appel à sfActions::execute(). Cette méthode ne fait
que très peu de choses car elle vérifie simplement que l'action peut être appelée, puis elle
l'appelle. De retour dans le filtre, la méthode postExecute() de l'action est maintenant
exécutée.
La valeur de retour de l'action est très importante car elle déterminera la vue à exécuter. Par
défaut, si aucune valeur de retour n'est trouvée, c'est la valeur sfView::SUCCESS qui sera
retournée par défaut, ce qui correspond à la valeur Success et donc au template
indexSuccess.php.
Il reste une dernière étape avant la génération de la vue. Le filtre vérifie si deux valeurs de
retour spéciales ont été retournées sfView::HEADER_ONLY et sfView::NONE. Chacune de ces
deux valeurs réalise ce que leur nom suggère de faire : envoi des en-têtes HTTP seulement
(géré par sfWebResponse::setHeaderOnly()) ou bien désactivation complète de la génération
du template.
Bien que les noms des vues par défaut de symfony soient ALERT, ERROR, INPUT, NONE et
SUCCESS, le développeur a la possibilité de retourner n'importe quelle autre valeur qui lui
convient.
A partir du moment où l'on sait ce que l'on souhaite précisément obtenir comme rendu, alors
c'est que l'on est prêt à plonger dans la dernière étape du processus : l'exécution de la vue
courante.
La première chose à faire consiste à récupérer un objet sfView à l'aide de la méthode
sfController::getView(). Cet objet peut provenir de deux endroits différents. Tout d'abord, il
faut savoir que pour chaque action spécifique, un objet de vue personnalisée peut être configuré
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
dans une classe nommée actionSuccessView ou bien module_actionSuccessView, et présente
dans un fichier appelé apps/frontend/modules/module/view/actionSuccessView.class.php.
On considère ici que le module et l'action demandés se nomment respectivement module et
action. Sinon, c'est la classe définie dans la directive de configuration mod_module_view_class
qui sera utilisée, alors que sa valeur par défaut est sfPHPView.
Utiliser une classe de vue personnalisée permet d'exécuter du code spécifique à >la vue dans
la méthode sfView::execute(). Par exemple, c'est typiquement à cet endroit précis que l'on
peut imaginer l'initialisation d'un moteur de templates si l'on souhaite en utiliser un.
Il existe trois modes de rendu possibles pour rendre la vue :
1. sfView::RENDER_NONE" : c'est l'équivalent de sfView::NONE, il annule toute tentative de
rendu.
2. sfView::RENDER_VAR : peuple la présentation de l'action, qui est alors accessible depuis la
méthode sfActionStackEntry::getPresentation() correspondante à son entrée dans la
pile des appels.
3. sfView::RENDER_CLIENT, c'est le mode par défaut qui rend la vue et alimente le contenu
de la réponse.
En effet, le mode rendu est utilisé uniquement à travers la méthode
sfController::getPresentationFor() qui retourne le rendu pour un module et une action
donnés.
Le Filtre de Rendu
Ce chapitre touche presque à sa fin, mais il reste encore une dernière étape à franchir. A ce
stade, la chaîne de filtrage a pratiquement terminé son exécution. Or, il ne faut pas oublier que
le comportement de ce filtre est un peu particulier. En effet, il a attendu depuis le début que
tous les filtres de la chaîne de filtres finissent leur tâche respective avant de pouvoir commencer
la sienne. Le travail du filtre de rendu consiste à envoyer le contenu de la réponse au
navigateur en utilisant la méthode sfWebResponse::send().
Résumé de l'Exécution de la Chaîne de Filtrage
1. La chaîne de filtrage est instanciée avec sa configuration contenue dans le fichier
filters.yml
2. Le filtre de sécurité vérifie les autorisations et les droits d'accès
3. Le filtre de cache gère le cache pour la page courante
4. Le filtre d'exécution exécute l'action
5. Le filtre de rendu envoie la réponse générée à travers l'objet sfWebResponse
Résumé général
1. Récupération de la configuration de l'application
2. Création d'une instance de sfContext
3. Initialisation de sfContext
4. Chargement des Factories
5. Notification des événements :
1. request.filter_parameters
2. routing.load_configuration
3. context.load_factories
6. Ajout des paramètres globaux des templates
7. Appel de la méthode sfFrontWebController::dispatch()
8. Appel de la méthode sfController::forward()
9. Vérification de l'existence fichier generator.yml
10. Vérification de l'existence du module et de l'action
11. Récupération d'une liste des répertoires des contrôleurs
12. Récupération d'une instance de l'action
13. Chargement du module de configuration à travers le fichier module.yml et / ou
config.php
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Plonger dans les Entrailles de Symfony | symfony | Web PHP Framework
14. La chaîne de filtres est instanciée avec sa configuration contenue dans le fichier
filters.yml
15. Le filtre de sécurité vérifie les autorisations et les droits d'accès
16. Le filtre de cache gère le cache pour la page courante
17. Le filtre d'exécution exécute l'action
18. Le filtre de rendu envoie la réponse via l'objet sfWebResponse
Conclusion
C'est fini ! Le cycle de traitement d'une requête a été entièrement géré. La boucle a été bouclée
et nous sommes maintenant prêt à affronter toutes les suivantes.
Bien sûr, ce chapitre n'est qu'un aperçu de ce traitement, mais nous pourrions néanmoins écrire
un livre entier sur les processus internes de symfony. Nous vous invitons d'ores et déjà à
explorer le code source par vous-même - c'est et ce sera toujours le meilleur moyen
d'apprendre les mécanismes internes d'un framework ou d'une librairie.
« Tirer Profit de l'Héritage de Table avec Doctrine
Windows et symfony »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/10-Symfony-Internals[31/12/2010 02:34:18]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Windows et symfony
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Laurent Bonnet
Introduction
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Ce chapitre est un nouveau tutoriel qui couvre, étape par étape,
l'installation, le déploiement et les tests fonctionnels du framework
symfony sur une plateforme Windows Server 2008.
Afin de préparer le déploiement sur Internet, le tutoriel peut être
exécuté sur l'environnement d'un serveur dédié hébergé sur Internet.
Bien sûr, il est possible de compléter ce tutoriel sur un serveur local, ou
bien sur une machine virtuelle installée sur le poste de travail du
lecteur.
Support symfony!
Buy this book
or donate.
Les Raisons d'un Nouveau Tutoriel
Aujourd'hui, il existe seulement deux sources d'informations en rapport à Microsoft Internet
Information Server (IIS) sur le site officiel de symfony. Cependant, ces deux sources se
rapportent à des versions précédentes qui n'ont plus évolué avec les nouvelles versions des
systèmes d'exploitation de Microsoft Windows, particulièrement avec Windows Server 2008 (sorti
en février 2008), qui inclut de nombreux changements intéressants pour les développeurs PHP :
IIS version 7, la version embarquée dans Windows Server 2008, a été entièrement
réécrite en vue d'une architecture complètement modulaire.
IIS 7 a prouvé qu'il était très fiable, avec quelques correctifs intégrés dans Windows
Update depuis le lancement du produit.
IIS 7 intègre également l'accélérateur FastCGI, un pool d'applications multi-processus qui
tire profit du modèle natif de processus léger des systèmes d'exploitation de Windows.
L'implémentation de FastCGI de PHP améliore de 5 à 10 fois les performances à
l'exécution, sans cache, comparé aux déploiements traditionnels ISAPI ou CGI de PHP sur
Windows et IIS.
Plus récemment, Microsoft a levé le voile sur un accélérateur PHP, qui est encore à l'état
de Release Candidate à l'heure où ces lignes sont écrites.
Extension Prévue pour ce Tutoriel
Une section supplémentaire de ce chapitre est en cours d'élaboration et sera publiée rapidement
sur le site officiel du projet symfony après la publication de cet ouvrage. Elle couvre la connexion
à MS SQL Server via PDO et Microsoft prévoit à l'heure actuelle des améliorations à venir à ce
sujet.
[PHP_PDO_MSSQL]
extension=php_pdo_mssql.dll
Pour l'instant, les meilleures performances à l'exécution du code sont obtenues en utilisant le
connecteur PHP 5 natif de SQL Server, un connecteur open-source disponible sur Windows dans
sa version 1.1. Il est implémenté sous forme d'une nouvelle extension DLL pour PHP:
[PHP_SQLSRV]
extension=php_sqlsrv.dll
Il est possible d'utiliser aussi bien Microsoft SQL Server 2005 ou 2008 pour la base de données.
L'extension prévue pour ce tutoriel couvrira l'utilisation de l'édition gratuite : SQL Server Express.
Comment Faire Fonctionner ce Tutoriel sur Différents Systèmes Windows (32 bits
inclus)
Chapter Content
Introduction
Les Raisons d'un Nouveau Tutoriel
Comment Faire Fonctionner ce
Tutoriel sur Différents Systèmes
Windows (32 bits inclus)
Serveur Web utilisé tout au long du
Document
Bases de Données
Configuration de Windows Server
Vérifications Préliminaires Serveur Dédiés sur Internet
Installer PHP - Quelques Clics
Suffisent
Exécuter PHP depuis l'Interface en
Ligne de Commande (CLI)
Installation et Usage de la
Sandbox Symfony
Préparer le Terrain pour symfony
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ce document a été écrit spécifiquement pour les éditions 64 bits de Windows Server 2008.
Néanmoins, le lecteur sera capable d'utiliser d'autres versions sans complication supplémentaire.
La version exacte du système d'exploitation utilisé dans les captures d'écran est Windows
Server 2008 Enterprise Edition accompagnée d'un Service Pack 2 pour du matériel 64 bits.
Version 32 bits de Windows
Ce tutoriel est aisément portable sur des versions 32 bits de Windows, en remplaçant les
références suivantes dans le texte :
Sur les éditions 64-bits : C:\Program Files (x86)\ et C:\Windows\SysWOW64\
Sur les éditions 32-bits : C:\Program Files\ et C:\Windows\System32\
Test de l'Exécution
Création d'une Application Web
Sandbox : Configuration Web du
Front-End
Création d'un nouveau Projet
symfony
Télécharger, Créer un Répertoire et
Copier les Fichiers
Définition de l'Arborescence du Projet
Création et Initialisation
Création d'une Application Web
Configuration des Applications Prêtes
pour Internet
A Propos des Versions non Enterprise
De plus, si le serveur n'utilise pas une version Enterprise, ce n'est pas un problème. Ce
document est directement portable pour d'autres versions de logiciel Windows Server : Windows
Server 2008 Web, Standard ou Datacenter Windows Server 2008 Web, Standard ou Datacenter
avec Service Pack 2 Windows Server 2008 R2 Web, Standard, Enterprise ou Datacenter.
Be trained by symfony experts
Jan 24: Paris
Feb 21: Paris
Il est important de noter que toutes les éditions de Windows Server 2008 RC2 sont
seulement disponibles pour les systèmes d'exploitation 64 bits.
Les paramètres de configuration régionale utilisés dans ces captures d'écran sont en-US mais un
pack d'internationalisation pour le Français aurait pu aussi être installé.
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
A Propos des Versions Internationales
(Maîtrise de & Doctrine
- Français)
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Il est également possible d'exécuter ce tutoriel sur un système d'exploitation Windows client :
Windows XP, Windows Vista et Windows Seven, en mode x64 ou x86.
Search
Serveur Web utilisé tout au long du Document
powered by google
Le serveur web utilisé ici est Microsoft Internet Information Server dans sa version 7.0, qui est
inclus comme un rôle dans toutes les versions de Windows Server 2008. Ce tutoriel s'appuie sur
un serveur Windows Server 2008 entièrement fonctionnel tandis qu'IIS sera installé "from
scratch".
Les étapes d'installation utilisent les choix par défaut bien que deux autres modules spécifiques
seront ajoutés : FastCGI et URL Rewrite. Ces derniers interviennent notamment dans
l'architecture modulaire de IIS 7.0.
Bases de Données
SQLite est la base de données pré-configurée dans un bac à sable symfony. Sur Windows, il n'y
a rien de plus à installer particulièrement. En effet, le support de SQLite est directement
implémenté dans l'extension PHP PDO pour SQLite, qui est d'ailleurs installée par défaut avec
PHP. Par conséquent, il n'est pas nécessaire de télécharger et d'exécuter une instance séparée
de SQLITE.EXE.
[PHP_PDO_SQLITE]
extension=php_pdo_sqlite.dll
Configuration de Windows Server
Il est conseillé d'utiliser une installation récente de Windows Server dans le but de faire
correspondre les captures d'écran aux étapes de ce chapitre avec l'écran du lecteur.
Bien sûr, travailler directement sur une machine existante est concevable bien que des
différences puissent subsister à cause du système d'exploitation installé, de l'exécution ou bien
des configurations régionales.
Afin d'obtenir les mêmes copies d'écran présentées dans ce tutoriel, il est recommandé au
lecteur de se procurer un Windows Server dédié sur un environnement virtuel, disponible
gratuitement sur Internet pour une période de 30 jours.
Comment obtenir un essai gratuit à Windows Server?
Il est bien sûr possible d'utiliser n'importe quel serveur dédié avec un accès à Internet. Un
serveur physique ou un serveur virtuel dédié (VDS) fera largement l'affaire.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Un serveur limité 30 jours avec Windows est disponible à l'essai grâce à Ikoula, un hébergeur
web français qui offre une liste complète de services pour les développeurs et les graphistes. Cet
essai gratuit démarre bien sûr à 0 € par mois pour une machine virtuelle Windows qui tourne sur
un environnement Microsoft Hyper-V. Par ailleurs, une machine virtuelle entièrement
fonctionnelle, intégrant une version Windows Server 2008 Web, Standard, Entreprise ou
Datacenter, peut être mise à disposition gratuitement pendant une période de 30 jours.
Pour commander, il suffit de se connecter au site http://www.ikoula.com/flex_server et de cliquer
sur le bouton "Testez gratuitement".
Afin d'obtenir les mêmes messages décrits dans ce document, le système d'exploitation
commandé avec le serveur Flex est : "Windows Server 2008 Enterprise Edition 64 bits". Il s'agit
d'une distribution x64, livrée avec les deux locales fr-FR et en-US. Il est donc très facile de
passer de fr-FR à en-US et vice-versa depuis le panneau de commande "Windows Control
Panel". Plus précisément, ce paramètre se trouve dans "Regional and Language Options", situées
sous l'onglet "Keyboards and Languages". Il suffit de cliquer sur "Install/uninstall languages".
Il est impératif de posséder les accès Administrateur sur le serveur.
Si le lecteur travaille depuis une station de travail distante, il devra alors exécuter les Remote
Desktop Services, plus connus sous le nom de Terminal Server Client en anglais, et s'assurer
qu'il dispose des droits Administrateur.
La distribution utilisée ici est Windows Server 2008 accompagnée du Service Pack 2.
Windows Server 2008 a été installé avec l'environnement graphique, qui correspond au thème
de Windows Vista. Il est également possible d'utiliser la ligne de commande uniquement sur les
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
versions de Windows Server 2008 intégrant les mêmes services dans le but de réduire le poids
de la distribution (1.5 Go au lieu de 6.5 Go). Cela réduit aussi les attaques en surface et le
nombre de correctifs Windows Update qui ont besoin d'être appliqués.
Vérifications Préliminaires - Serveur Dédiés sur Internet
Maintenant que le serveur est directement accessible depuis Internet, c'est aussi une bonne idée
de vérifier que le pare-feu de Windows fournit une protection résidente active. Les seules
exceptions à vérifier sont les suivantes :
Core Networking
Remote Desktop, (si atteint à distance)
Secure World Wide Web Services (HTTPS)
World Wide Web Services (HTTP)
Ensuite, il est toujours bon d'exécuter Windows Update afin de s'assurer que toutes les parties
du système d'exploitation sont à jour avec les derniers correctifs, patches et documentation.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
En guise de dernière étape de préparation, et dans un souci de supprimer toutes les paramètres
conflictuels potentiels de la configuration existante de Windows ou de IIS, il est recommandé de
désinstaller le rôle Web du serveur Windows s'il était précédemment installé.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Installer PHP - Quelques Clics Suffisent
A présent, IIS et PHP peuvent être installés en une opération triviale.
PHP n'est PAS une partie de la distribution Windows Server 2008, par conséquent, il faut tout
d'abord installer Microsoft Web Platform Installer 2.0, abrégé Web PI dans les prochaines
sections.
Web PI prend le soin d'installer toutes les dépendances nécessaires pour exécuter PHP sur
n'importe quel système Windows / IIS. Par conséquent, il déploie IIS avec les Services de Rôle
(Role Service) minimaux pour le Serveur Web (Web Server), et fournit les options minimales
pour l'exécution de PHP.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
L'installation de Microsoft Web Platform Installer 2.0 contient un analyseur de configuration qui
vérifie les modules existants, propose des modules de mises à jour nécessaires, et qui permet
aussi de bêta-tester des extensions non publiées de la plateforme Microsoft Web Platform.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Web PI 2.0 offre une installation de PHP en un seul clic. La sélection installe l'implémentation
Win 32 "non-thread safe" de PHP, qui est la meilleure associée à IIS 7 et FastCGI. Il offre
également le binaire PHP le plus récent testé : ici 5.2.11. Pour le trouver, il suffit de sélectionner
l'onglet "Frameworks and Runtimes" sur la gauche.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Après avoir choisi PHP, Web PI 2.0 sélectionne automatiquement toutes les dépendances
nécessaires au service des pages web .php stockées sur le serveur, y compris les services
minimaux de rôles de IIS 7.0:
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ensuite, il suffit de cliquer sur "Install" puis de sélectionner le bouton "I Accept". L'installation
des composants IIS commence, tandis que, en parallèle, le binaire de PHP est téléchargé et
quelques modules sont mis à jour. Une mise à jour de IIS 7.0 FastCGI par exemple.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Enfin, le programme de configuration de PHP s'exécute, et, après quelques minutes devrait
afficher un écran similaire à la capture ci-dessous :
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Il ne reste alors plus qu'à cliquer sur "Finish". Windows Server écoute à présent et est capable
de répondre sur le port 80. La capture d'écran ci-dessous atteste de son bon fonctionnement.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Maintenant, pour vérifier que PHP est correctement installé et disponible depuis IIS, il suffit de
créer un petit fichier phpinfo.php accessible sur le port 80 du serveur web par défaut. Ce fichier
doit être créé dans le répertoire C:\inetpub\wwwroot.
Avant de faire cela, il convient de s'assurer que toutes les extensions des fichiers sont visibles
dans l'Explorateur de Windows. Pour ce faire, il suffit de sélectionner l'option "Unhide Extensions
for Known Files Types" dans les paramètres de l'explorateur.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
La nouvelle étape consiste à ouvrir l'explorateur Windows afin de se rendre dans le dossier
C:\inetpub\wwwroot. Puis, un clic droit sur l'option "New Text Document" permet de créer un
nouveau fichier et de le renommer avec le nom phpinfo.php, avant de copier l'appel à la
fonction PHP phpinfo() en guise de contenu.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ensuite, il ne reste plus qu'à vérifier l'exécution du fichier dans le navigateur en ajoutant
/phpinfo.php à la fin de l'URL du serveur.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Enfin, pour s'assurer que symfony s'installera sans aucun problème, il suffit de télécharger le
fichier http://sf-to.org/1.3/check.php et de l'exécuter.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ce fichier doit alors être copié dans le même répertoire que phpinfo.php
(C:\inetpub\wwwroot) et renommé en check_configuration.php si nécessaire.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Enfin, il ne reste plus qu'à réouvrir le navigateur une dernière fois et d'ajouter
/check_configuration.php à la fin de l'URL du serveur comme le montre la capture d'écran cidessous.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Exécuter PHP depuis l'Interface en Ligne de Commande (CLI)
Afin de pouvoir exécuter plus tard des tâches symfony en ligne de commande, il est nécessaire
de s'assurer que PHP.EXE est accessible depuis l'invité de commande et qu'il s'exécute
correctement. Pour ce faire, il suffit d'ouvrir un invité de commande, de se positionner dans le
répertoire C:\inetpub\wwwroot et enfin de taper :
PHP phpinfo.php
Le message d'erreur suivant devrait apparaître à l'écran :
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Si rien n'est fait, l'exécution de PHP.EXE tiendra du fait de l'absence du fichier MSVCR71.DLL. Ce
fichier DLL doit être récupéré puis installé au bon endroit.
Le fichier MSVCR71.DLL est une ancienne version de Microsoft Visual C++ qui date d'avant 2003.
Il est donc intégré dans le paquet redistribuable du Framework .Net 1.1.
Le paquet redistribuable du Framework .Net 1.1 est téléchargeable depuis le site MSDN
Le fichier recherché est installé dans le répertoire suivant :
C:\Windows\Microsoft.NET\Framework\v1.1.4322
Il suffit de copier le fichier MSVCR71.DLL dans le répertoire ci-dessous :
sur les systèmes x64 : le répertoire C:\windows\syswow64 ;
sur les systèmes x86 : le répertoire C:\windows\system32.
A présent, le Framework .Net 1.1 peut être désinstallé.
L'exécutable PHP.EXE peut quant à lui être exécuté depuis l'invité de commande sans erreur. Par
exemple :
PHP phpinfo.php
PHP check_configuration.php
Plus tard dans ce chapitre, il s'agira de vérifier que le fichier SYMFONY.BAT - correspondant à la
syntaxe de la commande symfony - de la distribution "sandbox" provoque lui aussi la réponse
attendue.
Installation et Usage de la Sandbox Symfony
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Le paragraphe suivant est un résumé du tutoriel officiel "Getting Started with symfony" au sujet
de "la Sandbox" : La sandbox, bac à sable en français, est un projet symfony pré-configuré et
très facile d'installation, qui fonctionne en l'état avec des paramètres de configuration prédéfinis
par défaut. C'est un excellent moyen de tester symfony sans pour autant se soucier d'une
installation propre qui respecte les bonnes pratiques web.
La sandbox est pré-configurée à être utilisée avec SQLite en guise de moteur de base de
données. Sur Windows, il n'y a rien de particulier à installer puisque le support de SQLite est
directement implémenté dans l'extension PDO de PHP pour SQLite. Cette extension PDO est
installée par défaut au moment de l'installation de PHP. Cette tâche a d'ores et déjà été
accomplie plus tôt lorsque l'environnement d'exécution PHP a été installé via l'outil Microsoft
Web PI.
A ce stade, il convient de vérifier que l'extension SQLite est correctement installée et référencée
dans le fichier PHP.INI, qui réside dans le répertoire C:\Program Files (x86)\PHP. De plus, il
faut s'assurer que le support de SQLite est activé en vérifiant que le connecteur PDO DLL
adéquat est bien défini à la valeur C:\Program Files (x86)\PHP\ext\php_pdo_sqlite.dll.
Préparer le Terrain pour symfony
Le projet initialisé dans le bac à sable de symfony est "prêt à être installé et exécuté". Il se
présente sous la forme d'une archive .zip. L'archive doit tout d'abord être téléchargée puis
extraite dans un emplacement temporaire, comme le répertoire "Downloads", disponible en
lecture et écriture dans le répertoire C:\Users\Administrator.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Il s'agit à présent de créer un répertoire pour la destination finale du bac à sable, comme le
dossier F:\dev\sfsandbox.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ensuite, tous les fichiers doivent être sélectionnés - CTRL-A - dans l'explorateur Windows depuis
l'emplacement de téléchargement (source), puis copiés dans le répertoire F:\dev\sfsandbox
nouvellement créé. Ce n'est pas moins de 2599 objets qui sont copiés dans le répertoire final.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Test de l'Exécution
Il convient maintenant de tester que l'installation du projet a réussi. Pour ce faire, il suffit
d'ouvrir un nouvel invité de commande, puis de se positionner dans le répertoire
F:\dev\sfsandbox afin d'exécuter la commande suivante.
PHP symfony -V
L'exécution de cette commande devrait retourner ceci :
symfony version 1.3.0 (F:\dev\sfsandbox\lib\symfony)
Depuis le même terminal de commande, la commande suivante peut aussi être exécutée :
SYMFONY.BAT -V
Cette commande retourne aussi le même résultat :
symfony version 1.3.0 (F:\dev\sfsandbox\lib\symfony)
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Création d'une Application Web
La création d'une application web sur le serveur local nécessite l'utilisation du gestionnaire de
IIS7, qui est panneau de contrôle utilisateur graphique pour toutes les activités relatives à IIS.
Toutes les actions déclenchées depuis cette interface graphique sont pour le moment exécutées
en coulisses via l'interface en ligne de commande.
La console d'administration de IIS est accessible depuis le menu Start dans Programs,
Administrative Tools, Internet Information Server (IIS) Manager.
Reconfigurer le Site Web par Défaut pour Eviter les Conflits sur le Port 80
Il s'agit maintenant de s'assurer que seule la sandbox symfony est capable de répondre sur le
port 80 (HTTP). Pour y parvenir, le paramètre "Default Web Site" existant doit être modifié afin
d'écouter sur le port 8080.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Si le pare-feu de Windows est activé, alors une exception pour le port 8080 devra être créée
manuellement afin que la requête puisse être capable d'atteindre le "Default Web Site". Pour ce
faire, il suffit de se rendre dans le panneau de contrôle de Windows, puis de sélectionner le
pare-feu Windows, de cliquer sur "Allow a program through Windows Firewall", et enfin de
cliquer sur "Add port" pour autoriser cette exception. Il ne reste plus qu'à cocher la case pour
l'activer après sa création.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Ajouter un nouveau Site Web pour la Sandbox
L'étape suivante consiste à ouvrir IIS Manager depuis le menu "Administration Tools". Dans le
panneau de gauche, il convient de sélectionner l'icône "Sites" puis de faire un clic droit dessus et
de choisir "Add Web Site" dans le menu ouvert. A présent, un nom doit être spécifié pour le site,
par exemple "Symfony Sandbox" ainsi qu'un chemin physique, D:\dev\sfsandbox. Les autres
champs peuvent rester tels qu'ils sont. La boîte de dialogue devrait ressembler à celle de la
capture ci-dessous.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Après un clic sur OK, si un petit x apparaît sur l'icône du site web (dans Feature View / Sites),
alors il ne faut pas hésiter à cliquer sur "Restart" dans le panneau de droite pour le faire
disparaître.
Vérifier si le Site Web Répond
Depuis IIS Manager, il s'agit maintenant de sélectionner le site "Symfony Sandbox", et, dans le
panneau de droite, de cliquer sur "Browse *.80 (http)".
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Bien que cela ne soit pas prévu, un message d'erreur explicite devrait apparaître à l'écran :
HTTP Error 403.14 - Forbidden. Le serveur web est par défaut configuré pour empêcher le
listage du contenu d'un répertoire.
Il s'agit de la configuration par défaut du serveur web qui spécifie que le contenu de ce
répertoire ne doit pas être listé. Comme aucun fichier index.php ou index.html n'existe dans le
répertoire D:\dev\sfsandbox, le serveur retourne normalement le message d'erreur
"Forbidden". Il n'y a donc aucune raison de s'inquiéter.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Maintenant, en remplaçant http://localhost par http://localhost/web dans la barre
d'adresse du navigateur, ce dernier, Internet Explorer par défaut, devrait afficher "Symfony
Project Created" comme le montre la capture ci-dessous.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Par ailleurs, il se peut qu'une barre jaune clair apparaisse en haut de la fenêtre indiquant
"Intranet settings are now turned off by default". Cette alerte indique seulement que les
paramètres de configuration Intranet sont moins sécurisés que les paramètres Internet. Là
encore, il n'y a aucune raison de s'inquiéter et les options de l'alerte peuvent être sélectionnées
en toute sécurité.
Pour fermer cette alerte de manière permanente, il suffit de faire un clic droit dessus, puis de
sélectionner l'option appropriée.
Cet écran confirme que le fichier index.php par défaut a été correctement chargé depuis le
chemin D:\dev\sfsandbox\web\index.php, puis exécuté et que les bibliothèques de symfony
sont normalement configurées.
Il ne reste maintenant plus qu'une dernière tâche à réaliser avant de pouvoir véritablement
jouer avec la sandbox de symfony : configurer la page web de l'application front-end en
important les règles de réécriture d'URL. Ces règles sont implémentées dans les fichiers
.htaccess et peuvent être contrôlées en quelques clics depuis IIS Manager.
Sandbox : Configuration Web du Front-End
Il s'agit maintenant de configurer l'application front-end de la sandbox dans le but de
commencer à jouer avec les véritables fonctionnalités de symfony. Par défaut, la page front-end
peut être atteinte et exécutée correctement lorsqu'elle est appelée depuis la machine locale (par
exemple, avec le nom localhost ou l'adresse 127.0.0.1).
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Pour s'assurer que la sandbox est complètement fonctionnelle sur Windows Server 2008, il est
possible d'explorer la configuration, les logs et les horodateurs dans les panneaux de la barre de
débogage en haut à droite de l'écran.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Bien que l'application sandbox puisse être accessible depuis Internet ou bien depuis une adresse
IP locale, la sandbox est principalement structurée comme un outil d'apprentissage du
framework symfony sur une machine locale. Par conséquent, ce chapitre couvrira des détails
relatifs à l'accès distant dans une prochaine section.
Création d'un nouveau Projet symfony
Créer un environnement de projet symfony pour de réelles intentions de développement est
presque aussi simple qu'une installation à partir de la sandbox. Les lignes qui suivent décrivent
le processus d'installation complet à partir d'une procédure simplifiée, dans la mesure où elle est
équivalente à l'installation et au déploiement d'une sandbox.
La différence réside dans le fait que, pour cette section du "projet", c'est la configuration de
l'application web qui est concernée afin de la faire fonctionner depuis n'importe où sur Internet.
Comme la sandbox, le projet symfony est pré-configuré par défaut pour utiliser SQLite en guise
de moteur de base de données. Tout ceci a été installé et configuré plus tôt dans ce chapitre.
Télécharger, Créer un Répertoire et Copier les Fichiers
Chaque version de symfony peut être téléchargée sous forme d'une archive .zip et ensuite
utilisée pour créer un projet depuis zéro. Il suffit, pour ce faire, de télécharger l'archive
contenant les bibliothèques depuis le site officiel de symfony. Ensuite, le contenu de l'archive
peut alors être extrait dans une destination temporaire, comme le répertoire "downloads".
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
A ce stade, l'objectif est de créer une arborescence complète pour la destination finale du projet.
Cette étape est un peu plus difficile qu'avec la sandbox.
Définition de l'Arborescence du Projet
Cette nouvelle section s'intéresse à la création d'une arborescence de fichiers pour le projet.
Pour commencer, il convient de se positionner dans le volume racine, D: par exemple. Puis, un
nouveau répertoire \dev doit être créé sur D: dans lequel il faut également créer un sousrépertoire sfproject.
D:
MD
CD
MD
CD
dev
dev
sfproject
sfproject
Le pointeur de fichier se situe maintenant dans le répertoire D:\dev\sfproject.
A partir de cet emplacement, les sous-répertoires lib, vendor et symfony peuvent être créés en
cascade.
MD
CD
MD
CD
MD
CD
lib
lib
vendor
vendor
symfony
symfony
Le pointeur de fichier est à présent dans D:\dev\sfproject\lib\vendor\symfony.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Tous les fichiers doivent maintenant être sélectionnés (CTRL-A dans l'explorateur Windows)
depuis l'emplacement de téléchargement (source), puis copiés depuis Downloads dans
D:\dev\sfproject\lib\vendor\symfony. Ce sont environ 3819 fichiers qui ont été ainsi copiés
dans le répertoire de destination finale.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Création et Initialisation
Il est temps d'ouvrir un nouvel invité de commande, puis de se positionner dans le répertoire
D:\dev\sfproject juste avant d'exécuter la commande suivante.
PHP lib\vendor\symfony\data\bin\symfony -V
Celle-ci devrait retourner le résultat ci-dessous :
symfony version 1.3.0 (D:\dev\sfproject\lib\vendor\symfony\lib)
L'exécution de la commande PHP ci-après suffit à elle-même pour initialiser un nouveau projet :
PHP lib\vendor\symfony\data\bin\symfony generate:project sfproject
Le résultat de son exécution provoque à l'écran l'affichage d'une suite d'opérations sur des
fichiers comprenant quelques commandes chmod 777.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Toujours à l'intérieur de l'invité de commande, il s'agit maintenant de créer une nouvelle
application symfony en exécutant la commande suivante.
PHP lib\vendor\symfony\data\bin\symfony generate:app sfapp
Là encore, le résultat consiste en une liste d'opérations sur les fichiers incluant des commandes
chmod 777. D'ici, au lieu de taper PHP lib\vendor\symfony\data\bin\symfony chaque fois que
c'est nécessaire, il suffit de copier le fichier symfony.bat de son point d'origine dans le
répertoire racine du projet.
copy lib\vendor\symfony\data\bin\symfony.bat
Maintenant, il existe une commande plus commode à exécuter dans l'invité en ligne de
commande depuis le répertoire D:\dev\sfproject. Toujours dans le répertoire
D:\dev\sfproject, la nouvelle commande peut être exécutée.
symfony -V
afin d'obtenir la réponse traditionnelle attendue :
symfony version 1.3.0 (D:\dev\sfproject\lib\vendor\symfony\lib)
Création d'une Application Web
Dans les lignes suivantes, le lecteur est supposé avoir lu les étapes préliminaires de création
d'une application web de sandbox pour reconfigurer le site web par défaut ("Default Web Site"),
afin qu'il n'interfère pas avec le port 80.
Ajout d'un nouveau Site Web pour ce Projet
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Il s'agit maintenant d'ouvrir IIS Manager depuis les outils d'administration (Administration Tools)
de Windows. Dans le panneau de gauche, il convient de sélectionner l'icône "Sites" puis
d'effectuer un clic droit afin de choisir la valeur "Add Web Site" dans la nouvelle fenêtre de
menu. Ensuite, le lecteur est invité à saisir un nom de site, par exemple "Symfony Project",
ainsi qu'un chemin physique absolu, D:\dev\sfproject. Les autres champs, quant à eux,
peuvent rester tels quels et la boîte de dialogue ci-dessous devrait alors faire son apparition.
Après avoir cliqué sur OK, si une petite croix, x, apparaît sur l'icône du site web (dans Features
View / Sites). Il ne faut pas hésiter à cliquer sur "Restart" dans le panneau de droite pour la
faire disparaître.
Vérifier si le Site Web Répond
Depuis IIS Manager, il s'agit de sélectionner le site "Symfony Project", et, dans le panneau de
droite, de cliquer "Browse *.80 (http)". Le même message explicite obtenu au moment de la
configuration de la sandbox devrait apparaître à l'écran :
HTTP Error 403.14 - Forbidden
Le serveur Web est toujours configuré pour ne pas afficher le contenu du répertoire. En tapant
http://localhost/web dans la barre d'adresse du navigateur, une nouvelle page indiquant
"Symfony Project Created" devrait apparaître. Cette dernière est légèrement différente de celle
précédemment obtenue avec la sandbox dans la mesure où elle ne contient aucune image.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Les images ne sont pas présentes pour le moment, bien qu'elles existent dans le répertoire /sf
dans les librairies de symfony. Il est facile de les relier au répertoire /web en ajoutant un
répertoire virtuel dans /web, intitulé sf, et pointant vers
D:\dev\sfproject\lib\vendor\symfony\data\web\sf.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Désormais, le projet dispose de sa propre page d'accueil par défaut "Symfony Project Created"
avec toutes les images chargées.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Et enfin, l'application symfony entière fonctionne. Pour s'en convaincre, il suffit de taper l'url de
l'application web, http://localhost/web/sfapp_dev.php, dans le navigateur web.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Il ne reste alors plus qu'à effectuer un test en local : vérifier la configuration, les logs et les
horodateurs dans les panneaux de la barre de débogage en haut de l'écran pour s'assurer que le
projet est entièrement fonctionnel.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Configuration des Applications Prêtes pour Internet
Le projet symfony générique fonctionne désormais localement comme la sandbox depuis le
serveur local, situé à l'adresse http://localhost ou à l'adresse IP http://127.0.0.1.
Maintenant, il est temps de rendre l'application accessible au grand public sur Internet.
La configuration par défaut du projet protège l'application d'être exécutée depuis un
emplacement distant, bien qu'en réalité, elle devrait être autorisée à accéder aux deux fichiers
index.php et sfapp_dev.php. Il s'agit donc d'exécuter le projet depuis un serveur web, en
utilisant l'adresse IP externe du serveur (par exemple 94.125.163.150) et le FQDN du serveur
dédié virtuel (par exemple 12543hpv163150.ikoula.com). Il est également possible d'utiliser les
deux adresses depuis l'intérieur du serveur, dans la mesure où elle ne sont pas reliées à la
boucle locale 127.0.0.1.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
L'accès aux fichiers index.php et sfapp_dev.php depuis un emplacement distant est désormais
fonctionnel comme il l'a été expliqué avant. En revanche, l'exécution du fichier sfapp_dev.php
échoue dans la mesure où il n'est pas autorisé par défaut. Cela empêche des utilisateurs
malicieux d'accéder à l'environnement de développement qui contient des informations
potentiellement sensibles à propos du projet. Le fichier sfapp_dev.php peut être édité pour le
faire fonctionner depuis un emplacement distant mais c'est très fortement déconseillé pour des
raisons évidentes de sécurité. Ce fichier ne devrait d'ailleurs jamais être déployé sur un serveur
de production.
Enfin, il ne reste plus qu'à simuler un véritable nom de domaine en éditant le fichier hosts. Ce
fichier s'occupe de résoudre le nom local FQDN sans avoir besoin d'installer un serveur DNS sur
Windows. Le service DNS est disponible sur toutes les versions de Windows Server 2008 R2, et
aussi dans Windows Server 2008 Standard, Enterprise et Datacenter.
Sur les systèmes d'exploitation x64, le fichier hosts est situé par défaut dans le répertoire
C:\Windows\SysWOW64\Drivers\etc. Le fichier hosts est pré-rempli afin de résoudre le nom de
domaine local localhost vers l'adresse IP 127.0.0.1, en IPv4 et ::1 en IPv6.
Pour finir, un faux nom de domaine, tel que sfwebapp.local et résolu localement peut être
ajouté au fichier.
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Le projet symfony s'exécute à présent sur le Web, sans aucun DNS, depuis une session du
navigateur web exécutée sur le serveur web.
« Plonger dans les Entrailles de Symfony
Développer pour Facebook »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Windows et symfony | symfony | Web PHP Framework
Pirum - PEAR channel server
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/11-Windows-and-Symfony[31/12/2010 02:34:54]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Développer pour Facebook
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Fabrice Bernhard
Facebook, avec plus de 350 millions de membres aujourd'hui, est
devenu le réseau social de référence sur Internet. L'une de ses
principales qualités est la mise à disposition de la plate-forme
Facebook, une API qui permet aux développeurs de créer des
applications directement dans le site Facebook mais aussi de connecter
d'autres sites Internet avec le système d'authentification et le graphe
social de Facebook.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Comme le site Facebook est écrit en PHP, il n'est pas étonnant que la
Buy this book
or donate.
librairie client officielle qui permette d'utiliser l'API soit aussi développée
en PHP. Cela fait de facto de symfony un choix logique pour développer
rapidement et proprement des applications Facebook ou bien des sites
Facebook Connect. Mais les nombreuses fonctionnalités de symfony
permettent de s'adapter particulièrement bien à la programmation pour Facebook et de gagner
ainsi en temps et en qualité.
Ce chapitre couvre les spécificités de la programmation Facebook avec symfony. Après un rapide
résumé de l'API Facebook et de son utilisation, les sections suivantes expliqueront comment
utiliser symfony au mieux en développant pour Facebook et comment bénéficier des
contributions de la communauté au travers du plugin sfFacebookConnectPlugin.
Il s'agira d'illustrer les notions acquises au travers d'une simple application "Hello you!", avant
de dévoiler quelques astuces pour résoudre les problèmes les plus courants de développement
avec Facebook.
Développer pour Facebook
Bien que l'API soit globalement la même, il existe deux cas d'utilisation très différents. Le
premier concerne la création d'une application Facebook dans le site Facebook.com tandis que la
seconde consiste à implémenter Facebook Connect sur un site web externe.
Les Applications Facebook
Les applications Facebook sont des applications web contenues dans Facebook. Leur principal
atout réside dans le fait qu'elles sont directement incluses dans le site communautaire Facebook
et son réseau social de plus de 300 millions de personnes, permettant ainsi à n'importe quelle
application virale de se propager à une vitesse incroyable.
Farmville est l'exemple le plus récent et le plus impressionnant, avec plus de 60 millions
d'utilisateurs actifs chaque mois et plus de 2 millions de fans convertis en quelques mois ! C'est
l'équivalent de la population française qui revient chaque mois sur cette application pour
travailler sur leur ferme virtuelle.
Les applications Facebook interagissent avec le site Facebook et son réseau social sous
différentes formes. Voici un bref aperçu des endroits où l'application Facebook pourra apparaître.
Le Canevas (Canvas)
Le canevas est habituellement la partie principale de l'application. C'est un petit site intégré dans
le cadre du site Facebook.
L'Onglet de Profil (Profile Tab)
L'application peut aussi être contenue dans un onglet du profil d'un utilisateur ou d'une page de
fan. Les principales contraintes sont alors les suivantes :
une page seulement. Il est impossible de définir des liens directs vers d'éventuelles souspages de l'onglet.
pas d'interaction dynamique au démarrage, que ce soit flash ou JavaScript. Pour proposer
des fonctionnalités dynamiques, l'application doit attendre une interaction de l'utilisateur,
un clic sur un lien ou un bouton par exemple.
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
Chapter Content
Développer pour Facebook
Les Applications Facebook
Facebook Connect
Configurer un Premier Projet avec
le Plugin sfFacebookConnectPlugin
Créer l'Application sur Facebook
Installer et Configurer
sfFacebookConnectPlugin
Configurer une Application Facebook
Configurer un Site Facebook Connect
Connecter sfGuard avec Facebook
Choisir entre FBML et XFBML :
Problème Résolu par symfony
L'Application de Démonstration "Hello
You"
Facebook Connect
Comment Facebook Connect
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
La Boîte de Profil (Profile Box)
C'est un reste de l'ancienne version de Facebook, qui est devenue quelque peu obsolète. La
boîte de profil permet d'afficher quelques informations dans un widget rectangulaire que l'on
retrouve dans l'onglet "Boîtes" du profil d'un utilisateur.
L'Onglet Informations
Certaines informations statiques relatives à l'utilisateur et l'application peuvent être affichées
dans l'onglet "Informations" du profil. Ces informations apparaitront juste sous l'âge de
l'utilisateur, son adresse et son court CV.
Publication dans le Flux d'Actualités
L'application peut publier des actualités, liens, photos, vidéos dans le flux d'actualités, sur le
mur d'un ami ou bien directement modifier le statut de l'utilisateur.
La Page d'Informations
C'est la page de profil de l'application, créée automatiquement par Facebook. C'est ici que le
créateur de l'application peut interagir avec ses utilisateurs. Cette page concerne plus
particulièrement l'équipe marketing que l'équipe de développement.
fonctionne et Différentes Stratégies
d'Intégration
Le Filtre Facebook Connect
Implémentation Propre afin d'Eviter
l'Erreur Fatale d'IE
Bonnes Pratiques pour les
Applications Facebook
Configurer Plusieurs Serveurs
Facebook Connect de Test
Utiliser le Système de Log de symfony
pour Déboguer le FBML
Eviter les Mauvaises Redirections
Facebook avec un Proxy
Utiliser le Helper fb_url_for() dans
les Applications Facebook
Rediriger dans une Application
Facebook
Connecter des Utilisateurs Existants
avec leur Compte Facebook
Aller Plus Loin
Facebook Connect
Facebook Connect permet à n'importe quel site web d'apporter quelques-unes des puissantes
fonctionnalités de Facebook à ses propres utilisateurs. Les sites web qui en bénéficient se
reconnaissent déjà à la présence d'un gros bouton bleu "Connect with Facebook". Parmi les plus
connus : digg.com, cnet.com, netvibes.com, yelp.com, etc.
La section suivante donne la liste des quatre principales raisons d'implémenter Facebook
Connect sur un site existant.
Système d'Authentification en un Clic
Tout comme OpenID, Facebook Connect permet aux sites web de proposer à ses utilisateurs une
connexion automatique à partir de leur session Facebook. Une fois la "connexion" entre le site
web et Facebook approuvée par l'utilisateur, la session Facebook est automatiquement
transmise au site, permettant d'épargner à l'utilisateur une énième procédure d'enregistrement
et de mot de passe à mémoriser.
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
Obtenir plus d'Informations sur l'Utilisateur
Une autre fonctionnalité clef de Facebook Connect est la quantité d'informations apportées.
Alors qu'un utilisateur ne délivrera habituellement qu'un minimum d'informations le concernant
sur un nouveau site, Facebook Connect permet quant à lui d'obtenir facilement des informations
additionnelles telles que l'âge, le sexe, la localisation, la photo de profil, etc. enrichissant
d'autant plus le site.
Les conditions d'utilisation de Facebook Connect rappellent clairement que la conservation des
informations personnelles est interdite sans l'accord explicite de l'utilisateur. Mais l'information
disponible peut être utilisée pour pré-remplir des formulaires et demander confirmation en un
clic. Le site web peut aussi se contenter des informations publiques telles que le prénom et le
nom sans avoir besoin de les enregistrer.
Communication Virale grâce au Flux d'Actualités
La possibilité d'interagir avec le flux d'actualités de l'utilisateur, d'inviter des amis ou de bien de
publier sur le mur d'un ami permet au site web d'utiliser pleinement le potentiel viral de
Facebook pour communiquer.
En effet, n'importe quel site avec une composante communautaire peut ainsi véritablement
bénéficier de cette fonctionnalité, tant que l'information publiée sur Facebook a un intérêt social
qui peut intéresser des amis et des amis d'amis.
Tirer Parti du Graphe Social Existant
Pour un site web dont le service dépend d'un graphe social (un réseau d'amis ou de
connaissances), le coût pour démarrer une première communauté, avec suffisamment de liens
entre les utilisateurs pour leur permettre d'interagir et de bénéficier du service, est colossal.
En donnant un accès facile à la liste des amis Facebook d'un utilisateur, Facebook Connect
réduit considérablement ce coût, en évitant à l'utilisateur de devoir chercher ses "amis déjà
enregistrés".
Configurer un Premier Projet avec le Plugin sfFacebookConnectPlugin
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
powered by google
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
Créer l'Application sur Facebook
Pour commencer, un compte Facebook est nécessaire, avec l'application "Developer" installée.
Pour créer l'application, la seule information nécessaire dans un premier temps est le nom de
l'application.
Installer et Configurer sfFacebookConnectPlugin
La prochaine étape est de lier les utilisateurs Facebook avec les utilisateurs sfGuard. C'est la
principale fonction du sfFacebookConnectPlugin, que j'ai créé et auquel d'autres développeurs
symfony ont rapidement contribué. Une fois ce plugin installé, il y a une étape de configuration
simple mais nécessaire. Les paramètres API key, application secret, et application ID
doivent être précisés dans le fichier app.yml de l'application symfony.
# default values
all:
facebook:
api_key: xxx
api_secret: xxx
api_id: xxx
redirect_after_connect: false
redirect_after_connect_url: ''
connect_signin_url: 'sfFacebookConnectAuth/signin'
app_url: '/my-app'
guard_adapter: ~
js_framework: none # none, jQuery or prototype.
sf_guard_plugin:
profile_class: sfGuardUserProfile
profile_field_name: user_id
profile_facebook_uid_name: facebook_uid # WARNING this column must be of type
varchar! 100000398093902 is a valid uid for example!
profile_email_name: email
profile_email_hash_name: email_hash
facebook_connect:
load_routing:
true
user_permissions: []
Avec les versions de symfony antérieures à 1.2, l'option load_routing doit être définie à la
valeur false, car elle utilise le nouveau système de routing sfRouting.
Configurer une Application Facebook
Si le projet est une application Facebook et pas un site Facebook Connect, le seul autre
paramètre important est app_url qui précise l'adresse relative de l'application dans Facebook.
Par exemple pour l'application http://apps.facebook.com/my-app la valeur du paramètre
app_url sera /my-app.
Configurer un Site Facebook Connect
Si le projet est un site Facebook Connect, les valeurs par défaut des autres paramètres pourront
être conservées la plupart du temps :
redirect_after_connect permet de modifier le comportement du plugin après un clic
sur le bouton "Connect with Facebook". Par défaut le plugin reproduit le comportement de
sfGuardPlugin après la création d'un nouveau compte.
js_framework permet de préciser l'utilisation d'un framework JavaScript. Il est fortement
recommandé d'en utiliser un, tel que jQuery par exemple, sur les sites Facebook Connect.
En effet, l'API JavaScript de Facebook est relativement lourde et peut entraîner des
erreurs fatales (!) sur IE6 si le chargement du fichier a lieu en cours de rendu du DOM.
user_permissions est le tableau des permissions qui seront affectées à un nouvel
utilisateur Facebook.
Connecter sfGuard avec Facebook
Le lien entre un utilisateur Facebook et le système d'authentification de sfGuardPlugin est
réalisé assez logiquement en utilisant une colonne facebook_uid dans la table Profile.
Le plugin part du principe que le lien entre l'objet sfGuardUser et son profil est obtenu en
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
utilisant la méthode getProfile(). C'est le comportement par défaut avec
sfPropelGuardPlugin mais doit être configuré spécifiquement avec sfDoctrineGuardPlugin.
Voici un schema.yml type :
Pour Propel :
sf_guard_user_profile:
_attributes: { phpName: UserProfile }
id:
user_id:
{ type: integer, foreignTable: sf_guard_user, foreignReference:
id, onDelete: cascade }
first_name:
{ type: varchar, size: 30 }
last_name:
{ type: varchar, size: 30 }
facebook_uid:
{ type: varchar, size: 20 }
email:
{ type: varchar, size: 255 }
email_hash:
{ type: varchar, size: 255 }
_uniques:
facebook_uid_index: [facebook_uid]
email_index:
[email]
email_hash_index:
[email_hash]
Pour Doctrine :
sfGuardUserProfile:
tableName:
sf_guard_user_profile
columns:
user_id:
{ type: integer(4), notnull: true }
first_name:
{ type: string(30) }
last_name:
{ type: string(30) }
facebook_uid:
{ type: string(20) }
email:
{ type: string(255) }
email_hash:
{ type: string(255) }
indexes:
facebook_uid_index:
fields: [facebook_uid]
unique: true
email_index:
fields: [email]
unique: true
email_hash_index:
fields: [email_hash]
unique: true
relations:
sfGuardUser:
type: one
foreignType: one
class: sfGuardUser
local: user_id
foreign: id
onDelete: cascade
foreignAlias: Profile
Si le projet utilise Doctrine mais que la propriété foreignAlias n'est pas définie à Profile,
alors le plugin ne fonctionnera pas. Mais une simple méthode getProfile()dans la classe
sfGuardUser qui pointe vers la table Profile suffit à contourner le problème !
Il faut bien faire attention au type de la colonne facebook_uid qui doit être un varchar car les
nouveaux profils Facebook ont des uids supérieurs à 10^15.
Il convient de rester prudent en utilisant une colonne varchar indexée plutôt que de vouloir
tenter l'utilisation d'une colonne bigint dont le comportement n'est pas toujours le même selon
le SGBD ou l'ORM utilisé.
Les deux autres colonnes sont moins importantes : email et email_hash ne servent que dans le
cas où Facebook Connect est utilisé sur un site web qui possède déjà des utilisateurs. Dans ce
cas Facebook propose un traitement assez complexe pour tenter d'associer les comptes existants
avec les utilisateurs qui essaieraient ensuite Facebook Connect en utilisant un hash de l'email du
compte existant. Bien sûr, le processus est simplifié par une tâche fournie dans le plugin
sfFacebookConnectPlugin décrite plus tard dans ce chapitre.
Choisir entre FBML et XFBML : Problème Résolu par symfony
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
Maintenant que tout est correctement configuré, le développement de l'application peut
commencer. Facebook propose plusieurs balises spécifiques qui permettent d'afficher de
véritables fonctionnalités, comme par exemple un formulaire d'invitation d'amis ou un système
entier de commentaires.
Ces balises sont des tags FBML ou XFBML. Les balises FBML et XFBML sont assez similaires, mais
le choix entre l'un ou l'autre des formats dépend en fait de l'affichage ou non de l'application
par Facebook. Si le projet est un site Facebook Connect, alors il n'y a qu'un choix possible :
XFBML. Si c'est une application Facebook, il y a deux choix :
Inclure l'application dans Facebook au travers d'une IFrame et utiliser du XFBML dans
cette IFrame ;
Laisser Facebook se charger du rendu directement dans leur page, et utiliser le FBML.
Facebook encourage les développeurs à utiliser leur "inclusion transparente" ou en d'autres
termes le FBML. En effet elle dispose de certains avantages :
Pas d'IFrame, qui restent toujours difficiles à gérer, car il faut en permanence se souvenir
si le lien concerne l'IFrame ou bien la fenêtre entière ;
Les balises FBML sont interprétées directement par le serveur Facebook et permettent
ainsi d'afficher des informations privées concernant l'utilisateur sans avoir à communiquer
au préalable avec le serveur Facebook. Cela constitue donc un aller / retour en moins
entre les deux serveurs ;
Pas besoin de transmettre les informations de session Facebook de page en page.
Mais le FBML a des inconvénients certains :
Tout le JavaScript est automatiquement inclus dans une sandbox, rendant impossible
toute utilisation de librairie extérieure, comme une Google Maps, jQuery ou tout autre
système de statistiques tels que Google Analytics qui est supporté officiellement par
Facebook ;
Le FBML est censé être plus rapide car certains appels au serveur sont économisés.
Cependant si l'application n'est pas particulièrement lourde, l'héberger sur son propre
serveur sera beaucoup plus rapide ;
C'est plus difficile à déboguer, particulièrement pour les erreurs 500 qui sont attrapées
par Facebook et remplacées par une erreur standard.
Donc quel est le choix recommandé ? La bonne nouvelle c'est qu'avec symfony et le plugin
sfFacebookConnectPlugin, il n'y a pas de choix à faire ! Il est possible d'écrire des applications
agnostiques et passer indifféremment de l'IFrame à l'inclusion directe dans Facebook ou au site
externe Facebook Connect avec le même code. Ceci est possible car, techniquement, la
principale différence entre ces trois environnements réside dans le layout... qui est très facile à
interchanger dans symfony. Voici deux exemples de layout :
Le layout d'une application FBML :
<?php sfConfig::set('sf_web_debug', false); ?>
<fb:title><?php echo sfContext::getInstance()->getResponse()->getTitle() ?></fb:title>
<?php echo $sf_content ?>
Le layout d'une application XFBML ou d'un site Facebook Connect :
<?php use_helper('sfFacebookConnect')?>
<!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"
xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<?php include_http_metas() ?>
<?php include_metas() ?>
<?php include_title() ?>
<script type="text/javascript"
src="/sfFacebookConnectPlugin/js/animation/animation.js"></script>
</head>
<body>
<?php echo $sf_content ?>
<?php echo include_facebook_connect_script() ?>
</body>
</html>
Pour passer de l'un à l'autre automatiquement, il suffit d'ajouter le code suivant dans le fichier
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
actions.class.php :
public function preExecute()
{
if (sfFacebook::isInsideFacebook())
{
$this->setLayout('layout_fbml');
}
else
{
$this->setLayout('layout_connect');
}
}
Il y a une petite différence entre une balise FBML et XFBML qui ne se trouve pas dans le
layout : les balises FBML peuvent être fermées, contrairement aux balises XFBML. Il suffit
donc de toujours remplacer les tags de la forme :
[html]
<fb:profile-pic uid="12345" size="normal" width="400" />
par :
[html]
<fb:profile-pic uid="12345" size="normal" width="400"></fb:profile-pic>
Bien sûr, il faut aussi configurer l'onglet Facebook Connect de l'application dans les paramètres
développeur de l'application de Facebook, même si le but final n'est que de faire du FBML.
Cependant, l'énorme avantage de faire ceci est de pouvoir tester l'application localement. Si
l'application Facebook utilise des balises FBML, ce qui est quasiment inévitable, la seule façon de
visualiser le résultat final consisterait logiquement à mettre le code en ligne afin de le tester
directement dans Facebook !
Heureusement grâce à Facebook Connect, les balises XFBML peuvent être rendues en dehors du
domaine facebook.com. Et, comme les portions de code précédentes l'ont montré, la seule
différence entre XFBML et FBML concerne le layout.
Cette solution permet donc de visualiser les balises FBML en local, à condition bien sûr d'être
connecté à Internet. De plus, avec un environnement de développement visible sur Internet, tel
qu'un serveur web ou un simple ordinateur avec un port 80 ouvert par exemple, même les
parties de l'application qui dépendent du système d'authentification de Facebook fonctionneront
en dehors du domaine facebook.com. C'est en effet grâce à Facebook Connect une fois de plus.
L'application peut donc être entièrement testée avant d'être mise en ligne sur Facebook.
L'Application de Démonstration "Hello You"
Avec le code suivant dans la vue du module home, l'application "Hello You" est terminée :
<?php $sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession(); ?>
Hello <fb:name uid="<?php echo $sfGuardUser ? $sfGuardUser->getProfile()>getFacebookUid() : '' ?>"></fb:name>
sfFacebookConnectPlugin convertit automatiquement le visiteur Facebook en un utilisateur
sfGuard. Cela permet une intégration très facile avec du code symfony existant qui repose sur
sfGuardPlugin.
Facebook Connect
Comment Facebook Connect fonctionne et Différentes Stratégies d'Intégration
Pour faire simple, Facebook Connect partage sa session avec celle du site. Cette opération est
réalisée au travers de la copie du cookie d'authentification de Facebook vers le site web en
ouvrant successivement une IFrame dans le site qui pointe vers une page Facebook. Cette
même page Facebook ouvre elle aussi une IFrame qui pointe vers le site.
Pour ce faire, Facebook Connect a besoin d'avoir accès au site ce qui empêche d'utiliser ou de
tester l'authentification Facebook Connect en local. Le point d'entrée sur le site est le fichier
xd_receiver.htm que sfFacebookConnectPlugin installe automatiquement. Bien sûr il ne faut
pas oublier d'utiliser la commande symfony plugin:publish-assets pour rendre le fichier
accessible publiquement.
Lorsque cette opération est terminée, la librairie officielle de Facebook permet d'utiliser la
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
session Facebook. Le plugin sfFacebookConnectPlugin crée en plus un utilisateur sfGuard lié à
cette session Facebook, qui s'intègre donc sans souci avec le site web existant. C'est pour cette
raison que le plugin redirige automatiquement l'utilisateur vers l'action
sfFacebookConnectAuth/signIn par défaut, une fois le bouton Facebook Connect cliqué et la
connexion validée.
Le plugin cherche d'abord si un utilisateur existe avec le même UID Facebook ou bien le même
hash d'email (voir la section "Connecter les Utilisateurs Existants avec leur Compte Facebook" à
la fin de ce chapitre). Si aucun n'est trouvé, un utilisateur vierge est alors créé.
Une autre stratégie d'intégration classique consiste à ne pas créer l'utilisateur directement, mais
de le rediriger d'abord vers un formulaire d'enregistrement spécifique. De là, il est
éventuellement possible d'utiliser la session Facebook afin de pré-remplir des informations, par
exemple, en ajoutant la fonction suivante dans le formulaire d'enregistrement :
public function setDefaultsFromFacebookSession()
{
if ($fb_uid = sfFacebook::getAnyFacebookUid())
{
$ret = sfFacebook::getFacebookApi()->users_getInfo(
array(
$fb_uid
),
array(
'first_name',
'last_name',
)
);
if ($ret && count($ret)>0)
{
if (array_key_exists('first_name', $ret[0]))
{
$this->setDefault('first_name',$ret[0]['first_name']);
}
if (array_key_exists('last_name', $ret[0]))
{
$this->setDefault('last_name',$ret[0]['last_name']);
}
}
}
Pour utiliser cette deuxième stratégie, souvent recommandée, il suffit de spécifier deux
paramètres dans le fichier app.yml. Le premier paramètre, redirect_after_connect indique
qu'il faille rediriger après la connexion Facebook Connect tandis que le second,
redirect_after_connect_url précise la route à utiliser pour réaliser cette redirection.
# default values
all:
facebook:
redirect_after_connect: true
redirect_after_connect_url: '@register_with_facebook'
Le Filtre Facebook Connect
Une chose importante à savoir, c'est que les utilisateurs de Facebook sont très souvent
connectés à Facebook lorsqu'ils sont sur Internet, c'est un avantage indéniable. Par conséquent,
le filtre sfFacebookConnectRememberMeFilter peut se révéler très utile.
Si un utilisateur utilise le site web alors qu'il est déjà connecté à Facebook, le filtre
sfFacebookConnectRememberMeFilter va automatiquement le connecter sur le site comme le
ferait le filtre classique "Remember me" de symfony.
$sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession();
if ($sfGuardUser)
{
$this->getContext()->getUser()->signIn($sfGuardUser, true);
}
Malheureusement, il subsiste un inconvénient majeur à ce filtre : les utilisateurs ne peuvent plus
se déconnecter du site, car tant qu'ils sont connectés sur Facebook, ils seront automatiquement
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
reconnectés sur le site. Cette fonctionnalité est donc à manier avec parcimonie et précaution.
Implémentation Propre afin d'Eviter l'Erreur Fatale d'IE
Un des pires bugs que l'on puisse rencontrer sur un site Internet est l'erreur "Operation aborted"
sur IE, qui fait tout simplement planter le rendu du site... côté client !
C'est en effet dû à la mauvaise qualité du moteur de rendu d'IE6 et d'IE7 qui peuvent tous deux
planter si des éléments DOM sont ajoutés à l'élément body depuis un script qui n'est pas
directement fils de l'élément body.
Malheureusement, c'est souvent le cas si du JavaScript Facebook est appelé sans faire attention.
Il faut donc prendre garde et bien l'insérer directement dans le body à la fin du document. C'est
d'autant plus facile à respecter avec symfony grâce notamment aux slots. Un slot dédié au
script Facebook Connect est utilisé dans la template si nécessaire et est inclus à la fin du layout,
juste avant la balise de fermeture </body>.
// in a template that uses a XFBML tag or a Facebook Connect button
slot('fb_connect');
include_facebook_connect_script();
end_slot();
// just before </body> in the layout to avoid problems in IE
if (has_slot('fb_connect'))
{
include_slot('fb_connect');
}
Bonnes Pratiques pour les Applications Facebook
Grâce au plugin sfFacebookConnectPlugin, l'intégration avec le plugin sfGuardPlugin est
simplifiée et le choix entre FBML, IFrame ou un site Facebook Connect peut attendre la dernière
minute.
Afin d'aller plus loin et de créer une véritable application utilisant plus de fonctionnalités
Facebook, il convient de donner quelques pratiques importantes qui profitent de toute la
puissance de symfony.
Configurer Plusieurs Serveurs Facebook Connect de Test
Un point important de la philosophie symfony concerne le débogage rapide et efficace de
l'application. Développer sur Facebook peut rendre la tâche particulièrement difficile, car
beaucoup de fonctionnalités nécessitent une connexion Internet (pour communiquer avec le
serveur Facebook), ainsi qu'un port 80 ouvert pour échanger les cookies d'authentification.
De plus, il existe une contrainte supplémentaire : une application Facebook Connect ne peut
être reliée qu'à un seul hôte. C'est un véritable problème si l'application est développée sur une
machine, puis testée sur une autre, mise en pré-production sur une troisième et utilisée
finalement sur une quatrième.
Dans ce cas la solution la plus simple consiste à créer une application par serveur dans les
paramètres développeur de Facebook, et de créer un environnement symfony pour chacune
d'elles. C'est trivial à réaliser dans symfony puisqu'il suffit d'un simple copier-coller du fichier
frontend_dev.php en son équivalent frontend_preprod.php. Il ne reste alors qu'à éditer le
nouveau fichier en remplaçant l'environnement dev par un nouvel environnement preprod.
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend',
'preprod', true);
L'étape suivante consiste à modifier le fichier app.yml afin de configurer les différentes
applications Facebook correspondantes aux différents environnements.
prod:
facebook:
api_key: xxx
api_secret: xxx
api_id: xxx
dev:
facebook:
api_key: xxx
api_secret: xxx
api_id: xxx
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
preprod:
facebook:
api_key: xxx
api_secret: xxx
api_id: xxx
Désormais, chaque application est testable sur chaque serveur distinct en utilisant le point
d'entrée frontend_xxx.php correspondant.
Utiliser le Système de Log de symfony pour Déboguer le FBML
Interchanger facilement le layout permet à la fois de tester et de développer une application
FBML quasiment entièrement en dehors de Facebook. Cependant, le test final dans Facebook
peut malgré tout résulter en un message d'erreur particulièrement obscur.
En effet, le principal problème lorsqu'il s'agit de visualiser le FBML directement dans Facebook
vient du fait que les erreurs 500 sont remplacées par un message d'erreur complètement inutile.
De plus, la web debug toolbar, à laquelle les développeurs symfony sont rapidement accrocs, ne
peut être utilisée correctement dans une application FBML. Heureusement l'excellent système de
log de symfony est là pour nous sauver. Le plugin sfFacebookConnectPlugin loggue déjà
automatiquement les actions les plus importantes et il est facile de rajouter des lignes dans le
fichier partout dans l'application.
if (sfConfig::get('sf_logging_enabled'))
{
sfContext::getInstance()->getLogger()->info($message);
}
Eviter les Mauvaises Redirections Facebook avec un Proxy
Un bug étrange de Facebook est qu'une fois Facebook Connect configuré pour une application,
le serveur hébergeant l'application est considéré comme page d'accueil par défaut de
l'application. Bien qu'il soit possible de préciser une page d'accueil, elle doit figurer dans le
domaine du serveur hébergeur. Cela peut paraître gênant si c'est une application Facebook dont
la page d'accueil se trouve dans le domaine apps.facebook.com ! Aucune autre solution n'existe
que de se rendre et configurer la page d'accueil vers une action symfony très simple qui redirige
à un endroit désiré. Le code suivant redirige vers la page d'accueil de Facebook :
public function executeRedirect(sfWebRequest $request)
{
return $this>redirect('http://apps.facebook.com'.sfConfig::get('app_facebook_app_url'));
}
Utiliser le Helper fb_url_for() dans les Applications Facebook
Pour garder une application agnostique et utilisable jusqu'à la dernière minute autant en FBML
dans Facebook qu'en XFBML dans une IFrame, un problème important persiste : le routage.
Pour une application FBML, les liens dans l'application doivent pointer vers /appname/symfony-route ;
pour une application IFrame, il est important de passer l'information de session Facebook
d'une page à une autre.
Le plugin sfFacebookConnectPlugin fournit pour cela un helper dédié qui permet de faire
exactement les deux automatiquement : fb_url_for().
Rediriger dans une Application Facebook
Les développeurs symfony s'habituent rapidement à rediriger après une requête post réussie.
C'est en effet une bonne pratique de développement web qui empêche entre autre le double
post.
Rediriger dans une application FBML, cependant, ne fonctionne pas comme attendu. A la place,
une balise FBML spécifique est nécessaire afin d'informer Facebook de faire la redirection.
Toujours dans un ultime but précis de rester agnostique, une méthode statique spéciale,
redirect(), existe dans la classe sfFacebook, qui peut être utilisée par exemple dans l'action
de sauvegarde d'un formulaire.
if ($form->isValid())
{
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
$form->save();
return sfFacebook::redirect($url);
}
Connecter des Utilisateurs Existants avec leur Compte Facebook
L'un des principaux buts de Facebook Connect consiste à faciliter le processus d'enregistrement
pour les nouveaux utilisateurs. Cependant, une autre utilisation intéressante concerne aussi la
connexion des utilisateurs existants à partir de leur compte Facebook. Cette fonctionnalité est
utile soit pour obtenir plus d'informations sur eux, ou bien communiquer dans leur feed mais
aussi pour leur proposer une authentification en un seul clic. Cette tâche est réalisable de deux
manières différentes.
Inciter les utilisateurs sfGuard existants à cliquer sur le bouton "Connect with Facebook".
L'action sfFacebookConnectAuth/signIn ne créera pas un nouvel utilisateur sfGuard si elle
détecte un utilisateur déjà authentifié, mais reliera en revanche cet utilisateur avec l'UID de la
session Facebook reconnue.
Utiliser le système de reconnaissance d'email de Facebook.
Lorsqu'un nouvel utilisateur utilise Facebook Connect sur un site, Facebook est capable de
fournir un hash spécifique de ses emails, qui peuvent ensuite être comparés aux hashes des
emails existants en base. L'objectif est ainsi de reconnaître un utilisateur existant.
Mais, vraisemblablement, pour des raisons de sécurité, il est impossible d'obtenir les hashes des
emails d'un utilisateur si ces derniers n'ont pas été soumis préalablement à l'API de Facebook.
C'est pourquoi il est utile d'enregistrer les hashes d'email de tous les nouveaux utilisateurs
régulièrement, afin de les reconnaître ultérieurement.
C'est exactement le besoin que remplit la tâche registerUsers, qui a été migrée sur symfony
1.2 par Damien Alexandre. Dans l'idéal, cette tâche devrait être exécutée au moins toutes les
nuits afin d'enregistrer les nouvelles créations de comptes, ou bien, juste après la création d'un
nouveau compte avec la méthode registerUsers de sfFacebookConnect.
[php]
sfFacebookConnect::registerUsers(array($sfGuardUser));
Aller Plus Loin
J'espère que cet article a rempli son objectif ; aider et inciter les développeurs à démarrer le
développement d'une application Facebook sous symfony, et expliquer comment bénéficier de
toute la puissance de symfony tout au long de ce développement.
Néanmoins, le plugin sfFacebookConnectPlugin ne remplace pas l'API originale de Facebook, et
pour apprendre à utiliser toutes les fonctionnalités du développement sur la plate-forme
Facebook, il faudra visiter son site.
Pour conclure, je tiens à remercier toute la communauté symfony pour sa qualité et sa
générosité, et particulièrement tous ceux qui ont déjà contribué au plugin
sfFacebookConnectPlugin au travers de leurs commentaires et patches : Damien Alexandre,
Thomas Parisot, Maxime Picaud, Alban Creton et désolé pour ceux que j'aurais pu oublier. Bien
sûr si vous pensez qu'il manque quelque chose dans le plugin, il est toujours possible de
contribuer aussi !
« Windows et symfony
Tirer Profit de la Ligne de Commande »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Open-Source Products
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
Services
The More with symfony book | Développer pour Facebook | symfony | Web PHP Framework
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/12-Developing-for-Facebook[31/12/2010 02:35:04]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Tirer Profit de la Ligne de Commande
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Geoffrey Bachelet
Symfony 1.1 a introduit un système d'exécution de tâches en ligne de
commande moderne, puissant et flexible en remplacement de l'ancien
système basé sur pake. De version en version, le système de tâches de
symfony s'est enrichi afin d'être ce qu'il est aujourd'hui.
De nombreux développeurs ne perçoivent pas toute la valeur ajoutée
des tâches. Bien souvent, ces développeurs ne réalisent pas aussi la
puissance de la ligne de commande. Ce chapitre plonge le lecteur dans
l'univers des tâches automatiques, de leur usage le plus simple au plus
avancé, en démontrant à la fois combien cet outil aide le développeur
au quotidien, et de quelle manière en tirer le mieux profit.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Introduction
Une tâche est une partie du code qui s'exécute depuis une interface en ligne de commande en
utilisant le script php symfony présent à la racine du projet. N'importe quel développeur symfony
a déjà utilisé les tâches de symfony en exécutant par exemple la si connue tâche cache:clear
dans un shell. Cette tâche est aussi bien connue pour sa forme raccourcie cc.
$ php symfony cc
Symfony fournit nativement un large choix de jeux de tâches automatiques pour une grande
variété d'usages. Il est possible d'obtenir une liste complète de toutes les tâches disponibles en
exécutant le script symfony sans lui fournir la moindre option ou argument.
$ php symfony
La sortie générée dans la console ressemblera à celle ci-après. Le contenu a été tronqué :
Usage:
symfony [options] task_name [arguments]
Options:
--help
--quiet
--trace
--version
--color
--xml
-H
-q
-t
-V
Display this help message.
Do not log messages to standard output.
Turn on invoke/execute tracing, enable full backtrace.
Display the program version.
Forces ANSI color output.
To output help as XML
Available tasks:
:help
:list
app
:routes
cache
:clear
Displays help for a task (h)
Lists tasks
Displays current routes for an application
Clears the cache (cc, clear-cache)
Le lecteur aura certainement remarqué que les tâches sont groupées. Les groupes de tâches
sont appelés des espaces de nom. Les noms des tâches sont généralement composés d'un
espace de nom et d'un nom, séparés par un caractère ":". Certaines commandes spéciales telles
que help et list dérogent à la règle car elles sont exemptes d'espace de nom.
Ce schéma de nommage permet de catégoriser facilement les tâches les unes par rapport aux
autres, et il s'agira plus tard de choisir un espace de nom pertinent pour chaque nouvelle tâche
développée.
Ecrire ses Propres Tâches
Débuter avec l'écriture de tâches symfony n'est qu'une question de minutes. En effet, les seules
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
Chapter Content
Introduction
Ecrire ses Propres Tâches
Le Système d'Options
Les Options
Les Arguments
Les Arguments et Options par Défaut
Options Spéciales
Accéder à la Base de Données
Envoyer des Emails
Déléguer la Génération du Contenu
Utiliser le Plugin de Décoration de
Swift Mailer
Utiliser une Librairie Externe de
Template
Obtenir le Meilleur des Deux Mondes
Générer des URLs
Accéder au Système
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
choses à faire sont de créer une classe de tâche, puis de nommer la tâche avec un nom et un
espace de nom, et enfin de lui ajouter un peu de logique. C'est tout ce dont il est nécessaire afin
d'exécuter une première commande personnalisée. Le code ci-dessous déclare un exemple de
tâche simple Hello World! dans un fichier lib/task/sayHelloTask.class.php.
// lib/task/sayHelloTask.class.php
class sayHelloTask extends sfBaseTask
{
public function configure()
{
$this->namespace = 'say';
$this->name
= 'hello';
}
public function execute($arguments = array(), $options = array())
{
echo 'Hello, World!';
}
}
Il ne reste alors plus qu'à exécuter cette nouvelle tâche à l'aide de la commande suivante.
$ php symfony say:hello
Le seul but de cette tâche est d'imprimer la chaîne Hello, World! dans la console. C'est déjà un
bon point de départ ! Bien sûr, les tâches ne servent pas seulement à afficher du contenu dans
la console directement avec les fonctions echo ou print.
Etendre la classe abstraite sfBaseTask permet ainsi au développeur de bénéficier d'autres
méthodes pratiques, telles que log() qui remplit exactement le même besoin, à savoir afficher
du contenu.
d'Internationalisation - I18N
Remanier les Tâches
Exécuter une Tâche dans une
Autre
Manipuler le Système de Fichiers
Utiliser des Squelettes pour
Générer des Fichiers
Utiliser une Option Dry-Run
Ecrire des Tests Unitaires
Methodes Helper : Logging
Méthodes Helper : Interaction avec
l'Utilisateur
Bonus : Utiliser les Tâches avec
une Crontab
Bonus : Utiliser STDIN
Conclusion
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
public function execute($arguments = array(), $options = array())
{
$this->log('Hello, World!');
}
Un même appel à une tâche peut conduire à différents contenus en sortie, c'est pourquoi il
convient généralement d'utiliser la méthode logSection().
public function execute($arguments = array(), $options = array())
{
$this->logSection('say', 'Hello, World!');
}
A ce stade du chapitre, le lecteur aura probablement remarqué la présence des deux arguments
transmis à la méthode execute(), $arguments et $options. Ces deux variables servent à
contenir tous les arguments et options passés à la tâche à l'exécution. Les notions d'arguments
et d'options seront décrites en détail plus loin. Pour l'instant, il s'agit d'ajouter un peu plus
d'interactivité à la tâche en permettant à l'utilisateur de spécifier à qui il souhaite dire bonjour.
public function configure()
{
$this->addArgument('who', sfCommandArgument::OPTIONAL, 'Who to say hello to?',
'World');
}
public function execute($arguments = array(), $options = array())
{
$this->logSection('say', 'Hello, '.$arguments['who'].'!');
}
La commande suivante :
$ php symfony say:hello Geoffrey
Devrait ainsi produire le résultat ci-dessous.
>> say
Hello, Geoffrey!
C'est facile n'est-ce pas ? Par la même occasion, il convient d'ajouter quelques métadonnées
supplémentaires à la commande décrivant par exemple la fonction qu'elle remplit. Pour ce faire,
il suffit de définir une valeur pour les deux propriétés briedDescription et description.
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
and more...
Search
powered by google
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
public function configure()
{
$this->namespace
= 'say';
$this->name
= 'hello';
$this->briefDescription
= 'Simple hello world';
$this->detailedDescription = <<<EOF
The [say:hello|INFO] task is an implementation of the classical
Hello World example using symfony's task system.
[./symfony say:hello|INFO]
Use this task to greet yourself, or somebody else using
the [--who|COMMENT] argument.
EOF;
$this->addArgument('who', sfCommandArgument::OPTIONAL, 'Who to say hello to?',
'World');
}
Comme le montre cet exemple, il est possible d'utiliser un jeu basique de balises pour décorer la
description. La commande symfony help permet de contrôler le rendu des métadonnées de la
tâche:
$ php symfony help say:hello
Le Système d'Options
Dans les tâches symfony, les options sont organisées en deux ensembles distincts : les options
et les arguments.
Les Options
Les options sont les données transmises à l'aide de traits d'union. Elles peuvent être ajoutées à
la ligne de commande dans un ordre totalement arbitraire.
Les options acceptent ou non une valeur selon qu'elles agissent comme des valeurs booléennes
ou non. Très souvent, les options possèdent une forme courte et longue. La forme la plus
longue est généralement invoquée en spécifiant deux traits d'union tandis que sa forme courte
en requiert seulement un.
Il existe aussi des options récurrentes dans le système de tâches de symfony. C'est le cas par
exemple des options d'aide (--help ou -h), de verbosité (--quiet ou -q) ou bien de version (-version ou -V).
Les options sont définies à l'aide de la classe sfCommandOption et stockées dans une classe
sfCommandOptionSet.
Les Arguments
Les arguments constituent une suite de données ajoutées à la ligne de commande. Ils doivent
obligatoirement être spécifiés dans l'ordre dans lequel ils ont été définis, et doivent être
entourés de guillemets dans le cas où leur valeur contient un espace (les espaces peuvent être
échappés).
Il existe deux types d'arguments : les obligatoires et les facultatifs. Tous les arguments déclarés
comme étant facultatifs doivent accueillir une valeur par défaut.
Les arguments sont évidemment définis à l'aide d'une classe sfCommandArgument et stockés
dans un objet sfCommandArgumentSet.
Les Arguments et Options par Défaut
Chaque tâche symfony accueille un jeu d'options et d'arguments par défaut :
--help (-H) affiche un message d'aide ;
--quiet (-q) n'affiche aucun message sur la sortie standard ;
--trace (-t) active la trace d'exécution en incluant la pile d'exceptions complète ;
--version (-V) affiche la version du programme ;
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
--color force une sortie avec les couleurs ANSI.
Options Spéciales
Le système de tâches de symfony est capable de reconnaître deux options très spéciales,
application et env.
L'option application est nécessaire lorsqu'il s'agit d'accéder à une instance de la classe
sfApplicationConfiguration plutôt qu'une instance de sfProjectConfiguration. C'est le cas,
par exemple, lorsque le développeur souhaite générer des URLs depuis le système de routage.
Or, ce dernier est généralement associé à une instance d'une application spécifique.
Lorsqu'une option application est passée à la tâche, symfony la détecte automatiquement et
crée l'objet sfApplicationConfiguration correspondant au lieu de l'objet
sfProjectConfiguration par défaut. Il est intéressant de noter qu'il est possible de définir un
jeu de valeurs par défaut pour cette option, ce qui permet ainsi de s'éviter de passer une
application à la main à chaque exécution de la tâche.
L'option env contrôle bien évidemment l'environnement dans lequel la tâche est exécutée. Si
aucun environnement n'est passé, c'est l'environnement de test qui est sélectionné par défaut.
De la même manière qu'avec l'option application, il est possible de définir une valeur par
défaut pour l'option env qui sera ensuite utilisée par symfony.
Comme les options application et env ne sont pas incluses par défaut dans le jeu d'options,
elles doivent être ajoutées manuellement dans la classe de la tâche.
public function configure()
{
$this->addOptions(array(
new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The
application name', 'frontend'),
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The
environment', 'dev'),
));
}
Dans cet exemple, l'application frontend sera utilisée automatiquement, et à moins qu'un autre
environnement ne soit spécifié, la tâche s'exécutera dans le contexte de l'environnement dev.
Accéder à la Base de Données
Avoir accès à la base de données depuis l'intérieur d'une tâche symfony implique de disposer
d'une instance de la classe sfDatabaseManager.
public function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager($this->configuration);
}
L'objet de connexion de l'ORM peut également être accédé directement comme le montre
l'exemple de code ci-dessous.
public function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager($this->configuration);
$connection = $databaseManager->getDatabase()->getConnection();
}
Mais qu'en est-il des projets pour lesquels plusieurs connexions sont définies dans le fichier
databases.yml ? Il convient par exemple d'ajouter une option connection à la tâche pour
satisfaire ce besoin.
public function configure()
{
$this->addOption('connection', sfCommandOption::PARAMETER_REQUIRED, 'The connection
name', 'doctrine');
}
public function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager($this->configuration);
$connection = $databaseManager->getDatabase(isset($options['connection']) ?
$options['connection'] : null)->getConnection();
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
Comme d'habitude, une valeur par défaut peut être définie pour cette option. Tous les objets de
modèle de la base de données sont à présent manipulables comme s'ils se trouvaient dans un
contexte d'application symfony traditionnelle.
Attention lorsqu'il s'agit de manipuler des objets de modèle d'ORM en masse dans les tâches.
En effet, les deux ORMs Propel et Doctrine souffrent d'un bug très connu de PHP relatif aux
références cycliques et au ramasse miettes (garbage collector). Ce bug provoque une fuite de
mémoire et affecte toutes les versions strictement inférieures à la 5.3.
Envoyer des Emails
L'un des usages les plus répandus des tâches concerne l'envoi d'emails. En effet, cette tâche
n'était pas si aisée avant symfony 1.3. Heureusement, les choses ont changé et symfony
s'accompagne à présent d'une intégration complète de la librairie open-source PHP Swift Mailer.
Pourquoi ne pas en profiter pour la mettre en oeuvre dès à présent.
Le système de tâches de symfony expose un objet d'envoi d'email, le mailer, par l'intermédiaire
de la méthode sfCommandApplicationTask::getMailer(). De cette manière, l'accès à cet objet
simplifie l'envoi d'emails.
public function execute($arguments = array(), $options = array())
{
$mailer = $this->getMailer();
$mailer->composeAndSend($from, $recipient, $subject, $messageBody);
}
Comme la configuration du gestionnaire d'envoi d'emails est lue depuis la configuration de
l'application, la tâche doit obligatoirement accueillir une option application.
Si la stratégie de spool est configurée pour gérer l'envoi des emails dans un projet, alors ces
derniers ne seront envoyés qu'après l'exécution de la tâche project:send-emails.
Dans la plupart des cas, le contenu du message ne se trouvera pas par magie dans la variable
$messageBody, et devra donc être généré. Il n'existe pas de solution miracle pour générer le
contenu destiné à alimenter des emails. En revanche, les développeurs ont la possibilité de
s'appuyer sur quelques astuces en vue de se faciliter la vie.
Déléguer la Génération du Contenu
La génération d'un contenu d'email peut être déléguée simplement à une méthode protégée de
la classe. Cette dernière se charge de générer puis de retourner ce contenu pour l'email à
expédier.
public function execute($arguments = array(), $options = array())
{
$this->getMailer()->composeAndsend($from, $recipient, $subject, $this>getMessageBody());
}
protected function getMessageBody()
{
return 'Hello, World';
}
Utiliser le Plugin de Décoration de Swift Mailer
Swift Mailer délivre un plugin intitulé Decorator. Il s'agit d'un moteur de templating basique à la
fois simple et efficace qui accepte des chaînes de remplacement clé / valeurs. Ces dernières sont
applicables spécifiquement à chaque destinataire quels que soient les emails à expédier.
Le lecteur est inviter à consulter la documentation officielle de Swift Mailer pour plus
d'informations à ce sujet.
Utiliser une Librairie Externe de Template
Intégrer une librairie tierce de rendu de template est relativement facile. Il suffit par exemple de
s'appuyer sur le nouveau composant de templating intégré au projet Symfony Components.
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
Pour ce faire, les fichiers sources du composant doivent être téléchargés et déposés quelque part
dans le projet. Le répertoire lib/vendor/templating est un excellent candidat pour cela. Enfin,
il ne reste plus qu'à ajouter le code suivant à la tâche.
protected function getMessageBody($template, $vars = array())
{
$engine = $this->getTemplateEngine();
return $engine->render($template, $vars);
}
protected function getTemplateEngine()
{
if (is_null($this->templateEngine))
{
$loader = new
sfTemplateLoaderFilesystem(sfConfig::get('sf_app_dir').'/templates/emails/%s.php');
$this->templateEngine = new sfTemplateEngine($loader);
}
return $this->templateEngine;
}
Obtenir le Meilleur des Deux Mondes
Il existe d'autres issues supplémentaires à découvrir pour obtenir un système parfait. Le plugin
Decorator de Swift Mailer est particulièrement efficace lorsqu'il s'agit de gérer des
remplacements par expéditeur.
En effet, ce plugin permet de définir des remplacements pour chacun des destinataires afin que
Swift Mailer puisse remplacer les jetons par les bonnes valeurs en s'appuyant sur le destinataire
du mail à expédier. Le code ci-dessous explique comment intégrer ce fonctionnement à l'aide du
composant de templating.
public function execute($arguments = array(), $options = array())
{
$message = Swift_Message::newInstance();
// fetches a list of users
foreach($users as $user)
{
$replacements[$user->getEmail()] = array(
'{username}'
=> $user->getEmail(),
'{specific_data}' => $user->getSomeUserSpecificData(),
);
$message->addTo($user->getEmail());
}
$this->registerDecorator($replacements);
$message
->setSubject('User specific data for {username}!')
->setBody($this->getMessageBody('user_specific_data'));
$this->getMailer()->send($message);
}
protected function registerDecorator($replacements)
{
$this->getMailer()->registerPlugin(new
Swift_Plugins_DecoratorPlugin($replacements));
}
protected function getMessageBody($template, $vars = array())
{
$engine = $this->getTemplateEngine();
return $engine->render($template, $vars);
}
protected function getTemplateEngine($replacements = array())
{
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
if (is_null($this->templateEngine))
{
$loader = new
sfTemplateLoaderFilesystem(sfConfig::get('sf_app_template_dir').'/emails/%s.php');
$this->templateEngine = new sfTemplateEngine($loader);
}
return $this->templateEngine;
}
Le fichier apps/frontend/templates/emails/user_specific_data.php embarque le contenu
suivant.
Hi {username}!
We just wanted to let you know your specific data:
{specific_data}
Et c'est tout ! A présent, l'application bénéficie d'un moteur de templates entièrement
fonctionnel et dédié à la génération de contenus d'email.
Générer des URLs
Composer des emails implique généralement de générer des URLs basées sur la configuration du
routage. Heureusement, la génération des URLs a été simplifiée dans symfony 1.3 depuis qu'il
est possible d'accéder au routage de l'application courante à l'intérieur de la tâche. Cette
dernière expose en effet la méthode sfCommandApplicationTask::getRouting() prévue à cet
effet.
public function execute($arguments = array(), $options = array())
{
$routing = $this->getRouting();
}
Dans la mesure où le routage est dépendant de l'application, il convient de s'assurer que
l'application dispose d'une configuration d'application disponible ; autrement il sera impossible
de générer des URLs en utilisant le routage.
Consultez la section concernant les Options Spéciales pour savoir comment définir
automatiquement une configuration d'application dans la tâche.
Maintenant que la tâche possède une instance du routage, la génération des URLs s'en voit
simplifiée grâce à la méthode generate().
public function execute($arguments = array(), $options = array())
{
$url = $this->getRouting()->generate('default', array('module' => 'foo', 'action'
=> 'bar'));
}
Le premier argument est le nom de la route et le second un tableau des paramètres pour celleci. A ce stade, symfony génère une URL relative, et c'est malheureusement ce dont on n'a pas
besoin.
En effet, la génération des URLs absolues depuis une tâche ne fonctionnera pas dans la mesure
où il n'existe pas d'objet sfWebRequest sur qui compter pour récupérer l'hôte HTTP.
Une manière triviale pour résoudre ce problème consiste à définir l'hôte HTTP en dur dans le
fichier de configuration factories.yml.
all:
routing:
class: sfPatternRouting
param:
generate_shortest_url:
true
extra_parameters_as_query_string: true
context:
host: example.org
Il s'agit ici de remarquer le paramètre de configuration context_host. C'est à partir de celui-ci
que l'objet de routage est capable de générer une URL absolue.
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
public function execute($arguments = array(), $options = array())
{
$url = $this->getRouting()->generate('my_route', array(), true);
}
Accéder au Système d'Internationalisation - I18N
Toutes les factories ne sont pas aussi facilement accessibles que le gestionnaire d'envoi d'email
ou le routage. Lorsqu'il s'agit d'utiliser l'une d'entre elles, il n'est finalement pas si compliqué de
les instancier à la main directement dans la tâche.
Par exemple, si le développeur souhaite internationaliser les tâches, alors il devra accéder au
système d'i18n de symfony. Pour ce faire, il convient de s'aider de la classe
sfFactoryConfigHandler.
protected function getI18N($culture = 'en')
{
if (!$this->i18n)
{
$config = sfFactoryConfigHandler::getConfiguration($this->configuration>getConfigPaths('config/factories.yml'));
$class = $config['i18n']['class'];
$this->i18n = new $class($this->configuration, null, $config['i18n']['param']);
}
$this->i18n->setCulture($culture);
return $this->i18n;
}
Que se passe-t-il ici ? Tout d'abord, le code emploie une technique simple de manipulation du
cache dans le but d'éviter la reconstruction de l'objet i18n à chaque appel. Ensuite, l'objet
sfFactoryConfigHandler se charge de retrouver la configuration du composant afin de
l'instancier avant de terminer par la définition de la culture. Désormais, la tâche est capable
d'accéder à l'internationalisation comme le montre l'exemple suivant.
public function execute($arguments = array(), $options = array())
{
$this->log($this->getI18N('fr')->__('some translated text!'));
}
Bien sûr, passer à chaque fois la culture à la méthode n'est pas très pratique, surtout quand il
s'agit de changer fréquemment la culture dans la tâche. La section suivante explique comment
arranger cela.
Remanier les Tâches
La génération des contenus d'emails, l'expédition des ces derniers et la génération d'URLs sont
des fonctionnalités communes à d'autres besoins. Par conséquent, il s'avère judicieux de créer
une classe de base dans laquelle stocker ces fonctionnalités afin de les rendre disponibles aux
classes dérivées. L'implémentation technique est quant à elle triviale puisqu'il suffit de créer une
nouvelle classe de base dans un fichier lib/task/sfBaseEmailTask.class.php du projet et de
lui inclure le contenu suivant.
// lib/task/sfBaseEmailTask.class.php
class sfBaseEmailTask extends sfBaseTask
{
protected function registerDecorator($replacements)
{
$this->getMailer()->registerPlugin(new
Swift_Plugins_DecoratorPlugin($replacements));
}
protected function getMessageBody($template, $vars = array())
{
$engine = $this->getTemplateEngine();
return $engine->render($template, $vars);
}
protected function getTemplateEngine($replacements = array())
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
{
if (is_null($this->templateEngine))
{
$loader = new
sfTemplateLoaderFilesystem(sfConfig::get('sf_app_template_dir').'/templates/emails/%s.p
;
$this->templateEngine = new sfTemplateEngine($loader);
}
return $this->templateEngine;
}
}
Tant qu'à faire, il est aussi pertinent d'automatiser la configuration des options de la tâche en
ajoutant ces méthodes à la classe sfBaseEmailTask.
public function configure()
{
$this->addOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The
application', 'frontend');
}
protected function generateUrl($route, $params = array())
{
return $this->getRouting()->generate($route, $params, true);
}
Ce code utilise la méthode configure() pour ajouter des options communes à toutes les tâches
dérivées. Malheureusement, toutes les classes dérivées de sfBaseEmailTask devront appeler
parent::configure() dans leur propre méthode configure(). Cependant, il s'agit d'une gêne
infime au regard de la véritable valeur ajoutée du code.
Il convient à présent de remanier le code d'accès à l'objet I18N de la section précédente.
public function configure()
{
$this->addOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The
application', 'frontend');
$this->addOption('culture', null, sfCommandOption::PARAMETER_REQUIRED, 'The
culture', 'en');
}
protected function getI18N()
{
if (!$this->i18n)
{
$config = sfFactoryConfigHandler::getConfiguration($this->configuration>getConfigPaths('config/factories.yml'));
$class = $config['i18n']['class'];
$this->i18n = new $class($this->configuration, null, $config['i18n']['param']);
$this->i18n->setCulture($this->commandManager->getOptionValue('culture'));
}
return $this->i18n;
}
protected function changeCulture($culture)
{
$this->getI18N()->setCulture($culture);
}
protected function process(sfCommandManager $commandManager, $options)
{
parent::process($commandManager, $options);
$this->commandManager = $commandManager;
}
Il existe un nouveau problème à résoudre à ce stade. En effet, il est impossible d'accéder aux
valeurs des arguments et des options en dehors du périmètre de la méthode execute().
Pour corriger cela, la méthode process() doit être surchargée afin de rattacher le gestionnaire
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
d'options à la classe. Le gestionnaire d'options gère, comme son nom l'indique, les arguments et
les options pour la classe courante. Par exemple, les valeurs des options peuvent être accédées
à partir de la méthode getOptionValue().
Exécuter une Tâche dans une Autre
Une manière alternative de factoriser les tâches consiste à embarquer une tâche dans une
autre. Cette pratique est d'autant plus facilitée grâce aux méthodes
sfCommandApplicationTask::createTask() et sfCommandApplicationTask::runTask().
La méthode createTask() instanciera la classe à la place du développeur en lui passant le nom
de la tâche, de la même manière que la ligne de commande. En retour, cette méthode renverra
une instance de la classe désirée prête à être exécutée.
$task = $this->createTask('cache:clear');
$task->run();
Mais finalement, pour les plus paresseux, la tâche runTask() se charge de faire tout le travail
en une seule passe.
$this->runTask('cache:clear');
Il est bien sûr possible de passer des arguments et des options à la tâche en respectant l'ordre
suivant.
$this->runTask('plugin:install', array('sfGuardPlugin'), array('install_deps' =>
true));
Embarquer des tâches est aussi très utile pour composer des tâches plus puissantes à partir de
tâches simples. Par exemple, la combinaison de plusieurs tâches dans une seule project:clean
permettrait ainsi d'exécuter un ensemble d'opérations juste après le déploiement de l'application
sur le serveur de production.
$tasks = array(
'cache:clear',
'project:permissions',
'log:rotate',
'plugin:publish-assets',
'doctrine:build-model',
'doctrine:build-forms',
'doctrine:build-filters',
'project:optimize',
'project:enable',
);
foreach($tasks as $task)
{
$this->run($task);
}
Manipuler le Système de Fichiers
Symfony est livré par défaut avec une abstraction simple du système de fichiers
(sfFilesystem) qui autorise l'exécution d'opérations simples sur les fichiers et les répertoires.
Elle est accessible à l'intérieur de chaque tâche à l'aide de $this->getFilesystem(). Cette
abstraction expose les méthodes suivantes :
sfFilesystem::copy() copie un fichier ;
sfFilesystem::mkdirs() crée une arborescence de répertoires récursivement ;
sfFilesystem::touch() crée un fichier ;
sfFilesystem::remove() supprime un fichier ou un répertoire ;
sfFilesystem::chmod() change les permissions d'un fichier ou d'un répertoire ;
sfFilesystem::rename() renomme un fichier ou un répertoire ;
sfFilesystem::symlink() crée un lien symbolique ;
sfFilesystem::relativeSymlink() crée un lien symbolique relatif à un répertoire ;
sfFilesystem::mirror() duplique une arborescence complète ;
sfFilesystem::execute() exécute une ligne de commande shell arbitraire.
Elle expose également une méthode très pratique étudiée dans la prochaine section de ce
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
chapitre : replaceTokens().
Utiliser des Squelettes pour Générer des Fichiers
Un autre usage courant des tâches consiste à générer des fichiers. La génération de fichiers
peut être réalisée à partir de squelettes et la méthode mentionnée juste avant :
sfFilesystem::replaceTokens(). Comme son nom l'évoque, cette méthode remplace des
jetons à l'intérieur d'un jeu de fichiers. Concrètement, il suffit de lui fournir un tableau de
fichiers ainsi qu'une liste de jetons afin qu'elle puisse remplacer toutes les occurrences de
chaque jeton par sa valeur respective dans chaque fichier.
Pour mieux comprendre tout l'intérêt de cette méthode, le prochain exemple réécrira
partiellement une tâche existante : generate:module. Par souci de clarté et de sobriété, il s'agit
seulement de regarder de plus près une partie de la méthode execute() de cette tâche, en
supposant qu'elle a été correctement configurée avec les options nécessaires. La validation est
quant à elle totalement ignorée.
Avant de démarrer l'écriture de la tâche, il est nécessaire de créer un squelette pour les
répertoires et les fichiers que la tâche devra générer, puis de les stocker quelque part. Par
exemple dans data/skeleton/ :
data/skeleton/
module/
actions/
actions.class.php
templates/
Le squelette du fichier actions.class.php pourrait ressembler à celui ci-dessous :
class %moduleName%Actions extends %baseActionsClass%
{
}
La première étape de cette tâche consiste à dupliquer l'arborescence de fichiers au bon endroit.
$moduleDir = sfConfig::get('sf_app_module_dir').$options['module'];
$finder
= sfFinder::type('any');
$this->getFilesystem()->mirror(sfConfig::get('sf_data_dir').'/skeleton/module',
$moduleDir, $finder);
A présent, il convient de remplacer les jetons du fichier actions.class.php :
$tokens = array(
'moduleName'
=> $options['module'],
'baseActionsClass' => $options['base-class'],
);
$finder = sfFinder::type('file');
$this->getFilesystem()->replaceTokens($finder->in($moduleDir), '%', '%', $tokens);
Et c'est finalement tout ce dont a besoin le corps de la commande pour générer le nouveau
module en utilisant le remplacement de jetons.
La tâche native actuelle generate:module recherche dans data/skeleton/ d'autres
squelettes alternatifs à utiliser au lieu de ceux par défaut. Donc faites attention !
Utiliser une Option Dry-Run
Il arrive souvent que l'on veuille prévisualiser le résultat final de l'exécution d'une tâche avant
même de l'avoir réellement lancée. Les lignes suivantes donnent quelques pistes et astuces sur
la manière de procéder.
Tout d'abord, il convient d'utiliser un nom standard, tel que dry-run, pour la nouvelle option.
Tout le monde sera ainsi capable de reconnaître de quoi il s'agit. Avant symfony 1.3, la classe
sfCommandApplication ajoutait une option dry-run par défaut. Cependant, celle-ci est
désormais supprimée et doit être ajoutée à la main. Cette option trouve typiquement sa place
dans une classe de base, comme cela a été expliqué plus haut.
$this->addOption(new sfCommandOption('dry-run', null, sfCommandOption::PARAMETER_NONE,
'Executes a dry run');
Ensuite, la tâche doit être appelée de la sorte :
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
./symfony my:task --dry-run
L'option dry-run indique que la tâche ne devrait faire aucun changement.
Ne devrait faire aucun changement sont les mots-clés importants dont il faut se souvenir.
Lorsque la tâche est en cours d'exécution en mode dry-run, elle doit laisser l'environnement
exactement dans le même état initial, y compris (mais ce n'est pas limité) :
La base de données : ne pas insérer, ni ne mettre à jour, ni ne supprimer des
enregistrements des tables. Une bonne pratique consiste à exécuter ce type d'opérations
à l'intérieur d'une transaction qui sera annulée à la fin.
Le système de fichiers : ne pas créer, ni ne modifier, ni ne supprimer des fichiers sur le
système de fichiers.
L'envoi d'emails : ne pas envoyer les emails, ou bien les expédier à une seule et même
adresse de maintenance.
Le code suivant illustre le fonctionnement du mode dry-run avec une base de données.
$connection->beginTransaction();
// modify your database
if ($options['dry-run'])
{
$connection->rollBack();
}
else
{
$connection->commit();
}
Ecrire des Tests Unitaires
Dans la mesure où les tâches peuvent accomplir une variété de buts différents, les tester
unitairement n'est pas une mince affaire non plus. En l'état, il n'existe pas de manière commune
et uniforme de tester des tâches, mais il y'a pourtant quelques principes à suivre pour aider à
rendre les classes de tests plus facilement testables.
Tout d'abord, il est important de penser les tâches comme des contrôleurs. A cette occasion, il
est bon de rappeler la règle d'or à propos des contrôleurs.
Des contrôleurs légers et fins mais des modèles lourds et chargés. C'est tout ! Par conséquent, il
convient de déplacer toute la logique métier à l'intérieur des classes de modèle. De cette
manière, ce seront les classes de modèle qui devront être testées en priorité sur les classes de
tâches, ce qui facilite les choses.
Lorsqu'il n'est plus possible de déplacer davantage de logique métier à l'intérieur des modèles,
une bonne pratique consiste à découper la méthode execute() en petites unités de code
testable, chacune résidant dans sa propre méthode accessible (comprendre publiquement).
Découper le code a plusieurs avantages :
1. La méthode execute() de la tâche en devient plus lisible ;
2. La tâche est quant à elle plus facilement testable ;
3. La tâche est enfin plus ouverte à d'éventuelles extensions.
Il ne faut pas hésiter à être créatif et à se construire son propre petit environnement de tests
pour ses besoins. Et si finalement, dans le cas où il n'existe aucun moyen de tester une tâche
fraîchement développée, c'est qu'il existe deux issues différentes.
La première c'est que certainement la tâche a été mal écrite tandis que la seconde consiste à
demander son opinion à quelqu'un d'autre. Bien évidemment, il est aussi recommandé de ne pas
hésiter à se plonger soi-même dans le code de quelqu'un d'autre afin d'étudier comment les
choses ont été testées. C'est le cas par exemple des tâches de symfony et des générateurs qui
sont relativement bien testés.
Methodes Helper : Logging
Le système de tâches de symfony essaie autant que possible de rendre la vie du développeur
plus facile, en lui fournissant de nombreuses méthodes "helper" pratiques. Ces dernières sont
responsables de la réalisation d'opérations communes telles que l'enregistrement de logs et les
interactions avec l'utilisateur.
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
L'une d'elles peut facilement enregistrer des messages vers STDOUT en utilisant les méthodes de
la famille log :
log() accepte un tableau de messages ;
logSection() est un peu plus élaborée puisqu'elle permet de formater les messages à
l'aide d'un préfixe (premier argument) et un type de message (quatrième argument).
Quand il s'agit d'enregistrer quelque chose de trop long, comme un nom de fichier, la
méthode logSection() tronquera le message, ce qui peut s'avérer contraignant. Le
troisième argument permet de définir la longueur maximale autorisée du message ;
logBlock() est le style de log utilisé pour les exceptions. Une fois de plus il est possible
de passer un style de formatage particulier.
Les formats de logs disponibles sont ERROR, INFO, COMMENT et QUESTION, et il ne faut surtout
pas se priver de les essayer tous afin d'étudier à quoi ils servent.
Exemple d'utilisation :
$this->logSection('file+', $aVeryLongFileName, $this->strlen($aVeryLongFileName));
$this->logBlock('Congratulations! You ran the task successfuly!', 'INFO');
Méthodes Helper : Interaction avec l'Utilisateur
Il existe trois autres helpers qui sont fournis pour faciliter les interactions avec l'utilisateur :
ask() imprime simplement une question et retourne l'entrée saisie par l'utilisateur ;
askConfirmation() est similaire à ask() à la différence qu'une confirmation est
demandée à l'utilisateur en plus, incluant les réponses y (yes, oui) et n (no, non) en
guise d'aide à la saisie ;
askAndValidate() est une méthode très utile qui imprime une question et valide la saisie
de l'utilisateur à l'aide d'un validateur sfValidator passé en second argument. Le
troisième argument est un tableau des options dans lequel peuvent être passées une
valeur par défaut (value), un nombre maximum d'essais (attempts) ainsi qu'un style de
formatage (style).
Par exemple, il est possible de demander à l'utilisateur de saisir son adresse email et de la
valider à la volée.
$email = $this->askAndValidate('What is your email address?', new sfValidatorEmail());
Bonus : Utiliser les Tâches avec une Crontab
La plupart des systèmes UNIX et GNU / Linux supportent la planification de tâches à travers un
mécanisme connu sous le nom de cron. Le cron vérifie un fichier de configuration (un crontab)
pour les commandes à exécuter à un certain temps ou bien à une certaine période. Les tâches
de symfony peuvent facilement être intégrées dans un crontab, et la tâche project:sendemails est le candidat parfait pour un exemple de ce type.
MAILTO="[email protected]"
0 3 * * *
/usr/bin/php /var/www/yourproject/symfony project:send-emails
Cette configuration indique au cron d'exécuter la tâche project:send-emails tous les jours à
trois heures du matin et d'envoyer toutes les sorties possibles (ici les logs, erreurs, etc) à
l'adresse email [email protected].
Pour plus d'informations sur le format du fichier de configuration de la crontab, il suffit de
taper man 5 crontab dans un terminal de commandes.
Il est aussi possible, et ça devrait être le cas ici, de passer des arguments et des options.
MAILTO="[email protected]"
0 3 * * *
/usr/bin/php /var/www/yourproject/symfony project:send-emails -env=prod --application=frontend
La valeur /usr/bin/php est à remplacer par le chemin absolu vers le binaire PHP CLI. Si
vous ne trouvez pas cette information, vous pouvez essayer la commande which php sur les
systèmes Linux ou bien whereis php sur la plupart des autres systèmes UNIX.
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
Bonus : Utiliser STDIN
Dans la mesure où les tâches sont exécutées dans un environnement en ligne de commande,
alors le flux de l'entrée standard (STDIN) peut être atteint. La ligne de commande UNIX permet
aux applications d'interagir entre elles par une variété de moyens, dont l'une d'elles est le pipe,
symbolisé par le caractère |.
Le pipe permet de passer la sortie d'une application (connue sous le nom de STDOUT) à une
entrée standard d'une autre application (connue sous le nom de STDIN). Ces deux valeurs sont
accessibles dans les tâches à travers les deux constantes de PHP STDIN et STDOUT. Il existe
également un troisième flux standard, STDERR, accessible depuis STDERR, et qui vise à porter
les messages d'erreur d'une application.
Qu'est-il possible de faire exactement avec l'entrée standard ? Eh bien, il s'agit d'imaginer qu'il
existe une application en cours d'exécution sur le serveur qui souhaiterait communiquer avec
l'application symfony. Une méthode consiste bien sûr à les faire communiquer à travers HTTP,
mais un moyen plus efficace serait de rediriger sa sortie vers une tâche symfony.
On peut supposer par exemple qu'une application soit capable d'envoyer des données
structurées (par exemple, un tableau PHP sérialisé), décrivant des objets de nom de domaine.
On souhaite ensuite insérer ces derniers en base de données. Par conséquent, il s'agirait d'écrire
la tâche suivante.
while ($content = trim(fgets(STDIN)))
{
if ($data = unserialize($content) !== false)
{
$object = new Object();
$object->fromArray($data);
$object->save();
}
}
Il ne resterait alors plus qu'à l'utiliser de la manière suivante.
/usr/bin/data_provider | ./symfony data:import
La chaîne data_provider consiste en l'application qui fournit les nouveaux objets de nom de
domaine, tandis que data:import est la tâche qui vient tout juste d'être écrite.
Conclusion
Toutes les tâches à accomplir ne sont finalement limitées que par l'imagination du développeur.
Le système de tâches de symfony est à la fois puissant et suffisamment flexible, ce qui permet à
n'importe quel développeur de réaliser presque tout ce dont il a besoin. Ajouter à cela la
puissance d'un shell UNIX, et les tâches ne seront plus qu'un jeu d'enfant.
« Développer pour Facebook
Manipuler le Cache de Configuration de symfony »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Tirer Profit de la Ligne de Commande | symfony | Web PHP Framework
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/13-Leveraging-the-Power-of-the-Command-Line[31/12/2010 02:35:07]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Manipuler le Cache de Configuration de symfony
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Kris Wallsmith
Un de mes principaux intérêts en tant que développeur symfony est de
suivre le travail de la communauté autant que possible quel que soit le
type de projet. Bien que je connaisse sur le bout des doigts le code
source interne de symfony, ce n'est pas nécessairement une obligation
pour tous les développeurs.
Heureusement, symfony fournit des solutions capables d'isoler chaque
composant d'une application, permettant ainsi à n'importe qui
d'effectuer des modifications sans difficulté.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Les Chaînes de Caractères dans les Formulaires
Un excellent exemple pour illustrer ces propos est le framework de
formulaires. Le framework de formulaires est un puissant outil de
symfony qui transforme le rendu et la validation d'un formulaire en objets PHP, dans le but de
donner aux développeurs davantage de contrôle sur leur gestion. Le travail des développeurs en
est ainsi largement simplifié.
En effet, ces derniers peuvent ainsi encapsuler une logique complexe dans une seule classe de
formulaire, étendre cette dernière et la réutiliser à différents endroits du code.
Cependant, pour un intégrateur, cette abstraction de rendu du formulaire peut être quelque peu
troublante. Il suffit d'étudier l'implémentation du formulaire suivant pour s'en convaincre.
La classe qui configure ce formulaire est de la forme suivante :
// lib/form/CommentForm.class.php
class CommentForm extends BaseForm
{
public function configure()
{
$this->setWidget('body', new sfWidgetFormTextarea());
$this->setValidator('body', new sfValidatorString(array('min_length' => 12)));
}
}
Le formulaire est ensuite rendu grâce au template PHP suivant :
<!-- apps/frontend/modules/main/templates/indexSuccess.php -->
<form action="#" method="post">
<ul>
<li>
<?php echo $form['body']->renderLabel() ?>
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
Chapter Content
Les Chaînes de Caractères dans les
Formulaires
Une Solution : le YAML
Filtrer les Variables de Template
Charger le YAML
Le Cache de Configuration
Intégration dans le Code par les
Tests
Les Gestionnaires de Configuration
Personnalisés
Jouer avec les Formulaires
Imbriqués
Qu'en est-il des Performances ?
Bonus : Embarquer le Gestionnaire
de Configuration dans un Plugin
Conclusion
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
<?php echo $form['body'] ?>
<?php echo $form['body']->renderError() ?>
</li>
</ul>
<p><button type="submit">Post your comment now</button></p>
</form>
L'intégrateur a certes la possibilité de modifier le rendu du formulaire. Il peut par exemple
modifier les intitulés par défaut.
<?php echo $form['body']->renderLabel('Please enter your comment') ?>
Une classe CSS peut également être ajoutée lors du rendu des champs.
<?php echo $form['body']->render(array('class' => 'comment')) ?>
Ces modifications sont intuitives et faciles. Mais qu'en est-il s'il doit modifier des messages
d'erreur ?
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
La méthode renderError() n'accepte aucun argument. La seule solution actuelle pour
l'intégrateur consiste à ouvrir la classe relative au formulaire, puis de trouver la méthode
correspondant à la validation afin d'en modifier les paramètres. Dans l'exemple précédent, les
modifications suivantes seraient nécessaires.
// before
$this->setValidator('body', new sfValidatorString(array('min_length' => 12)));
// after
$this->setValidator('body', new sfValidatorString(array('min_length' => 12), array(
'min_length' => 'You haven't written enough',
)));
Où est l'intrus ? Ici c'est une apostrophe dans une chaine de caractères entourée de guillemets
simples qui a été utilisée. Un développeur avisé ne ferait jamais une pareille erreur, mais qu'en
est-il d'un intégrateur qui doit plonger dans une classe de formulaire ? Il ne le fera pas.
La question qui se pose alors est la suivante. Faut-il sérieusement attendre d'un intégrateur de
connaître suffisamment bien le framework de formulaires au point de trouver à quel endroit se
définit un message d'erreur ? Est-ce que quelqu'un d'habitué à modifier des templates doit
connaître la signature d'un constructeur de validateur ?
La réponse à ces questions est clairement non. Les intégrateurs réalisent déjà beaucoup de
travail et il serait complètement déraisonnable de penser que quelqu'un qui n'a pas l'habitude
d'écrire du code puisse apprendre les rouages du framework de formulaires de symfony.
Une Solution : le YAML
Afin de simplifier l'édition de ces chaines de caractères, une couche de configuration en YAML
sera développée dans le but d'améliorer chaque objet de formulaire passé à la vue. Le fichier de
configuration prendra la forme suivante :
# config/forms.yml
CommentForm:
body:
label:
Please enter your comment
attributes:
{ class: comment }
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
errors:
min_length: You haven't written enough
C'est tout de même beaucoup plus simple. La configuration parle d'elle même, et résout le
problème précédent de guillemets. Il s'agit maintenant d'écrire le code nécessaire à l'intégration
de ce YAML.
Filtrer les Variables de Template
La première difficulté consiste à trouver un hameçon dans symfony qui permette de filtrer
chaque variable de formulaire passée au template par le fichier de configuration. La solution
consiste à utiliser l'événement template.filter_parameters qui est appelé par le coeur de
symfony juste avant de rendre un template ou un partiel.
// lib/form/sfFormYamlEnhancer.class.php
class sfFormYamlEnhancer
{
public function connect(sfEventDispatcher $dispatcher)
{
$dispatcher->connect('template.filter_parameters', array($this,
'filterParameters'));
}
public function filterParameters(sfEvent $event, $parameters)
{
foreach ($parameters as $name => $parameter)
{
if ($parameter instanceof sfForm && !$parameter->getOption('is_enhanced'))
{
$this->enhance($parameter);
$parameter->setOption('is_enhanced', true);
}
}
return $parameters;
}
public function enhance(sfForm $form)
{
// ...
}
}
Ce code vérifie si une option is_enhanced existe pour chaque objet de >formulaire avant de
le modifier. Ceci afin d'éviter que les formulaires qui sont chargés depuis un partiel soient
modifiés deux fois.
Cette classe doit maintenant être chargée depuis le fichier de configuration de l'application.
// apps/frontend/config/frontendConfiguration.class.php
class frontendConfiguration extends sfApplicationConfiguration
{
public function initialize()
{
$enhancer = new sfFormYamlEnhancer($this->getConfigCache());
$enhancer->connect($this->dispatcher);
}
}
Désormais les variables de formulaire peuvent être isolées juste avant d'être passées au
template ou au partiel. Tous les outils nécessaires à ce fonctionnement sont en plus disponibles.
La dernière étape consiste enfin à appliquer ce qui a été configuré dans le YAML.
Charger le YAML
La manière la plus simple d'appliquer le YAML à chaque formulaire est de le charger dans un
tableau et d'itérer dessus pour chaque configuration.
public function enhance(sfForm $form)
{
$config = sfYaml::load(sfConfig::get('sf_config_dir').'/forms.yml');
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
foreach ($config as $class => $fieldConfigs)
{
if ($form instanceof $class)
{
foreach ($fieldConfigs as $fieldName => $fieldConfig)
{
if (isset($form[$fieldName]))
{
if (isset($fieldConfig['label']))
{
$form->getWidget($fieldName)->setLabel($fieldConfig['label']);
}
if (isset($fieldConfig['attributes']))
{
$form->getWidget($fieldName)->setAttributes(array_merge(
$form->getWidget($fieldName)->getAttributes(),
$fieldConfig['attributes']
));
}
if (isset($fieldConfig['errors']))
{
foreach ($fieldConfig['errors'] as $errorCode => $errorMessage)
{
$form->getValidator($fieldName)->setMessage($errorCode, $errorMessage);
}
}
}
}
}
}
}
Cependant, cette implémentation a de nombreux défauts. Tout d'abord, le YAML est lu depuis le
système de fichiers et chargé dans l'objet sfYaml à chaque appel. Lire depuis le système de
fichiers de cette manière doit être évité pour des raisons évidentes de performances.
Ensuite, il existe plusieurs niveaux de boucles imbriquées et beaucoup trop de conditions qui
ralentissent inutilement l'exécution de l'application. La solution pour résoudre ces soucis réside
dans la gestion du cache de configuration de symfony.
Le Cache de Configuration
Derrière le cache de configuration se trouve une collection de classes qui optimisent l'utilisation
du YAML en le transformant en code PHP et en le stockant dans le dossier de cache avant
exécution. Ce mécanisme élimine la nécessité de charger le contenu de la configuration dans
sfYaml avant de pouvoir en utiliser les valeurs.
L'étape suivante consiste à implémenter ce système pour la classe de formulaire. Au lieu de
charger le fichier forms.yml dans sfYaml, il s'agit de demander, au système de configuration
une version pré-chargée en objet PHP. Pour ce faire, la classe sfFormYamlEnhancer aura besoin
d'accéder au cache de configuration, et c'est pour cette raison que cet objet sera passé dans le
constructeur.
class sfFormYamlEnhancer
{
protected
$configCache = null;
public function __construct(sfConfigCache $configCache)
{
$this->configCache = $configCache;
$this->configCache->registerConfigHandler('config/forms.yml',
'sfSimpleYamlConfigHandler');
}
// ...
}
Le cache de configuration a besoin de savoir ce qu'il doit faire lorsqu'un fichier de configuration
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
est appelé par l'application. Pour l'instant, il utilise la classe sfSimpleYamlConfigHandler pour
charger le fichier forms.yml. Le YAML est donc analysé puis transformé en un tableau PHP,
juste avant d'être mis en cache. A ce stade, la configuration est en place et prête à être
chargée. Elle peut être appelée de la manière suivante à la place de sfYaml.
public function enhance(sfForm $form)
{
$config = include $this->configCache->checkConfig('config/forms.yml');
// ...
}
C'est déjà beaucoup mieux. Non seulement la contrainte de devoir analyser le YAML à chaque
requête a été éliminée, mais le code fait usage de la fonction native include() de PHP qui
favorise la mise en cache du code.
Développement vs. Environnement de production
L'utilisation de checkConfig() diffère selon que le mode debug est activé ou pas. Dans
l'environnement de production, quand le mode debug est désactivé, cette méthode fonctionne
comme décrit ci-dessous :
Vérification de l'existence d'un fichier caché pour le fichier demandé
S'il existe, retourner le chemin du fichier caché
S'il n'existe pas :
Convertir le fichier de configuration ;
Sauvegarder le code résultant dans le cache ;
Retourner le chemin du nouveau fichier caché.
Cette méthode fonctionne différemment lorsque le mode debug est activé. Les fichiers de
configuration étant modifiés au cours du développement, la méthode checkConfig() compare les
fichiers originaux et ceux mis en cache pour s'assurer d'avoir la dernière version. Ce processus
inclut quelques vérifications :
Vérification d'une version cachée du fichier demandé
Si elle n'existe pas :
Traiter le fichier de configuration ;
Sauvegarder le code résultant dans le cache.
Si elle existe :
Comparer les dernières modifications de la configuration et des fichiers
cachés ;
Si le fichier de configuration a été modifié récemment :
Traiter le fichier de configuration ;
Sauvegarder le code résultant dans le cache.
Retourner le chemin du fichier caché
Intégration dans le Code par les Tests
Avant d'aller plus loin dans les développements, il convient d'écrire quelques tests unitaires pour
valider le fonctionnement de la classe sfFormYamlEnhancer.
// test/unit/form/sfFormYamlEnhancerTest.php
include dirname(__FILE__).'/../../bootstrap/unit.php';
$t = new lime_test(3);
$configuration = $configuration->getApplicationConfiguration(
'frontend', 'test', true, null, $configuration->getEventDispatcher());
sfToolkit::clearDirectory(sfConfig::get('sf_app_cache_dir'));
$enhancer = new sfFormYamlEnhancer($configuration->getConfigCache());
// ->enhance()
$t->diag('->enhance()');
$form = new CommentForm();
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
$form->bind(array('body' => '+1'));
$enhancer->enhance($form);
$t->like($form['body']->renderLabel(), '/Please enter your comment/', '->enhance()
enhances labels');
$t->like($form['body']->render(), '/class="comment"/', '->enhance() enhances
widgets');
$t->like($form['body']->renderError(), '/You haven\'t written enough/', '->enhance()
enhances error messages');
L'exécution de cette suite de tests sur la version actuelle de la classe sfFormYamlEnhancer
réussit et valide la conformité du code.
Le code est désormais prêt à être modifié. Les tests unitaires avertiront le développeur si la
moindre pièce est cassée dans le code.
Les Gestionnaires de Configuration Personnalisés
Dans le code ci-dessous, chaque variable de formulaire passée au template itèrera sur chaque
classe de formulaire configurée dans le fichier forms.yml. Cette méthode fonctionne mais
lorsqu'il s'agit de passer plusieurs objets de formulaire au template, ou bien une longue liste de
formulaires configurés en YAML, un impact sur les performances de l'application se fera
ressentir. C'est donc une excellente opportunité pour écrire un gestionnaire de configuration
personnalisé qui optimisera ces performances.
Pourquoi personnaliser ?
Ecrire un gestionnaire de configuration personnalisé n'est pas des plus aisés. Tous les
développeurs sont sujets à faire des erreurs et la testabilité de ces objets n'est pas chose facile
non plus. Néanmoins, les bénéfices en seront substantiels. L'avantage de créer une logique
personnalisée permet de bénéficier de la flexibilité du YAML et de la faible surcharge du code PHP
natif. En ajoutant un cache d'opcodes (tel que APC ou XCache à tout cela, le gestionnaire de
configuration sera difficile à battre en termes de facilité d'utilisation et de performances.
L'essentiel de la magie de ces gestionnaires se passe en coulisses. Toute la logique est mise en
cache avant d'exécuter n'importe quel gestionnaire de configuration. Par conséquent, le
développeur a tout le loisir de se concentrer sur l'écriture du code nécessaire à la mise en
oeuvre de la configuration YAML de l'application.
Chaque gestionnaire doit implémenter les deux méthodes suivantes :
static public function getConfiguration(array $configFiles)
public function execute($configFiles)
La première méthode statique getConfiguration() reçoit comme paramètre un tableau
contenant le chemin des fichiers. Elle se charge ensuite de les analyser et de regrouper leur
contenu en une seule valeur. Dans la classe sfSimpleYamlConfigHandler utilisée
précédemment, cette méthode contient seulement une ligne.
static public function getConfiguration(array $configFiles)
{
return self::parseYamls($configFiles);
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
}
La classe sfSimpleYamlConfigHandler étend sfYamlConfigHandler qui inclut un certain
nombre de méthodes servant au traitement du fichier de configuration YAML :
::parseYamls($configFiles)
::parseYaml($configFile)
::flattenConfiguration($config)
::flattenConfigurationWithEnvironment($config)
Les deux premières méthodes implémentent le principe de
configuration en cascade de symfony. Les deux suivantes implémentent la sensibilisation à
l'environnement.
La méthode statique getConfiguration() du gestionnaire aura besoin d'une méthode
personnalisée afin de regrouper les configurations des classes dont elle hérite. Par conséquent, il
convient d'écrire une méthode applyInheritance() qui appliquera cette logique.
// lib/config/sfFormYamlEnhancementsConfigHandler.class.php
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler
{
public function execute($configFiles)
{
$config = self::getConfiguration($configFiles);
// compile data
$retval = "<?php\n".
"// auto-generated by %s\n".
"// date: %s\nreturn %s;\n";
$retval = sprintf($retval, __CLASS__, date('Y/m/d H:i:s'), var_export($config,
true));
return $retval;
}
static public function getConfiguration(array $configFiles)
{
return self::applyInheritance(self::parseYamls($configFiles));
}
static public function applyInheritance($config)
{
$classes = array_keys($config);
$merged = array();
foreach ($classes as $class)
{
if (class_exists($class))
{
$merged[$class] = $config[$class];
foreach (array_intersect(class_parents($class), $classes) as $parent)
{
$merged[$class] = sfToolkit::arrayDeepMerge($config[$parent],
$merged[$class]);
}
}
}
return $merged;
}
}
A présent, on dispose d'un tableau dont les valeurs ont été rassemblées en fonction de la classe
héritée. Le besoin de devoir analyser la configuration en entier a été éliminée via un appel à
instanceof pour vérifier le type de chaque objet.
De plus, cette opération est effectuée dans le gestionnaire de configuration et ne sera donc
exécutée qu'une fois avant la mise en cache. Ce tableau peut ainsi être passé à l'objet de
formulaire de la sorte.
class sfFormYamlEnhancer
{
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
protected
$configCache = null;
public function __construct(sfConfigCache $configCache)
{
$this->configCache = $configCache;
$this->configCache->registerConfigHandler('config/forms.yml',
'sfFormYamlEnhancementsConfigHandler');
}
// ...
public function enhance(sfForm $form)
{
$config = include $this->configCache->checkConfig('config/forms.yml');
$class = get_class($form);
if (isset($config[$class]))
{
$fieldConfigs = $config[$class];
}
else if ($overlap = array_intersect(class_parents($class), array_keys($config)))
{
$fieldConfigs = $config[current($overlap)];
}
else
{
return;
}
foreach ($fieldConfigs as $fieldName => $fieldConfig)
{
// ...
}
}
}
Avant de relancer la suite de tests unitaires, il convient d'ajouter quelques lignes pour la
nouvelle logique de classe.
# config/forms.yml
# ...
BaseForm:
body:
errors:
min_length: A base min_length message
required:
A base required message
Il s'agit ici de vérifier que le nouveau message required est appliqué dans le test, et de
confirmer que les enfants du formulaire recevront les améliorations de la classe parente.
$t = new lime_test(5);
// ...
$form = new CommentForm();
$form->bind();
$enhancer->enhance($form);
$t->like($form['body']->renderError(), '/A base required message/', '->enhance()
considers inheritance');
class SpecialCommentForm extends CommentForm { }
$form = new SpecialCommentForm();
$form->bind();
$enhancer->enhance($form);
$t->like($form['body']->renderLabel(), '/Please enter your comment/', '->enhance()
applies parent config');
L'exécution de la nouvelle mise à jour du test confirme que les modifications apportées au
formulaire fonctionnent comme prévu.
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
Jouer avec les Formulaires Imbriqués
Il existe une fonctionnalité importante dans le framework de formulaires de symfony qui n'a pas
encore été discutée jusqu'ici : les formulaires imbriqués. Si une instance de CommentForm est
imbriquée dans un autre formulaire, les améliorations apportées dans le fichier forms.yml ne
fonctionneront plus. Un simple test unitaire suffit pour le démontrer.
$t = new lime_test(6);
// ...
$form = new BaseForm();
$form->embedForm('comment', new CommentForm());
$form->bind();
$enhancer->enhance($form);
$t->like($form['comment']['body']->renderLabel(), '/Please enter your comment/', '>enhance() enhances embedded forms');
Ces quelques lignes prouvent que les formulaires imbriqués ne sont pas gérés.
Pour que le test fonctionne de nouveau, il faut développer une version plus avancée du
gestionnaire de configuration. Il convient de trouver une solution pour implémenter les
spécifications configurées dans le fichier forms.yml d'une manière plus modulaire afin de
prendre en compte les formulaires imbriqués.
Pour ce faire, une version personnalisée doit être écrite pour chaque méthode de chaque classe.
Ces méthodes seront générées par le gestionnaire de configuration personnalisé dans une
nouvelle classe métier.
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler
{
// ...
protected function getEnhancerCode($fields)
{
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
$code = array();
foreach ($fields as $field => $config)
{
$code[] = sprintf('if (isset($fields[%s]))', var_export($field, true));
$code[] = '{';
if (isset($config['label']))
{
$code[] = sprintf(' $fields[%s]->getWidget()->setLabel(%s);',
var_export($config['label'], true));
}
if (isset($config['attributes']))
{
$code[] = ' $fields[%s]->getWidget()->setAttributes(array_merge(';
$code[] = '
$fields[%s]->getWidget()->getAttributes(),';
$code[] = '
'.var_export($config['attributes'], true);
$code[] = ' ));';
}
if (isset($config['errors']))
{
$code[] = sprintf(' if ($error = $fields[%s]->getError())',
var_export($field, true));
$code[] = ' {';
$code[] = '
$error->getValidator()->setMessages(array_merge(';
$code[] = '
$error->getValidator()->getMessages(),';
$code[] = '
'.var_export($config['errors'], true);
$code[] = '
));';
$code[] = ' }';
}
$code[] = '}';
}
return implode(PHP_EOL.'
', $code);
}
}
Il est important de remarquer ici que le tableau de configuration est vérifié pour certaines clés
lors de la génération du code plutôt qu'à l'exécution afin de bénéficier d'un léger gain de
performances.
De manière générale, la logique qui vérifie les conditions de la configuration devrait être
exécutée dans le gestionnaire de configuration, et non dans le code généré. La logique qui
vérifie les conditions d'exécution, comme la nature de l'objet de formulaire, doit être appelée
au moment de l'exécution du code.
Le code généré est ensuite placé dans une définition de classe sauvegardée dans le répertoire
de cache.
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler
{
public function execute($configFiles)
{
$forms = self::getConfiguration($configFiles);
$code = array();
$code[] = '<?php';
$code[] = '// auto-generated by '.__CLASS__;
$code[] = '// date: '.date('Y/m/d H:is');
$code[] = 'class sfFormYamlEnhancementsWorker';
$code[] = '{';
$code[] = ' static public $enhancable = '.var_export(array_keys($forms),
true).';';
foreach ($forms as $class => $fields)
{
$code[] = ' static public function enhance'.$class.'(sfFormFieldSchema
$fields)';
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
$code[] = '
$code[] = '
$code[] = '
{';
'.$this->getEnhancerCode($fields);
}';
}
$code[] = '}';
return implode(PHP_EOL, $code);
}
// ...
}
La classe sfFormYamlEnhancer reportera la classe métier générée afin de gérer le traitement
des objets de formulaire, mais elle doit maintenant prendre en compte la récursivité des
formulaires imbriqués.
Pour ce faire, il s'agit de traiter le schéma des champs de formulaire (sur lequel on peut itérer
récursivement) et les objets de formulaire (y compris les formulaires imbriqués) en parallèle.
class sfFormYamlEnhancer
{
// ...
public function enhance(sfForm $form)
{
require_once $this->configCache->checkConfig('config/forms.yml');
$this->doEnhance($form->getFormFieldSchema(), $form);
}
protected function doEnhance(sfFormFieldSchema $fieldSchema, sfForm $form)
{
if ($enhancer = $this->getEnhancer(get_class($form)))
{
call_user_func($enhancer, $fieldSchema);
}
foreach ($form->getEmbeddedForms() as $name => $form)
{
if (isset($fieldSchema[$name]))
{
$this->doEnhance($fieldSchema[$name], $form);
}
}
}
public function getEnhancer($class)
{
if (in_array($class, sfFormYamlEnhancementsWorker::$enhancable))
{
return array('sfFormYamlEnhancementsWorker', 'enhance'.$class);
}
else if ($overlap = array_intersect(class_parents($class),
sfFormYamlEnhancementsWorker::$enhancable))
{
return array('sfFormYamlEnhancementsWorker', 'enhance'.current($overlap));
}
}
}
Une fois imbriqués, les champs d'un objet de formulaire ne devraient pas être modifiés. Les
formulaires imbriqués sont déclarés dans le formulaire parent afin de faciliter le traitement,
mais ils n'ont pas d'incidence sur le rendu de ce dernier.
A ce stade les formulaires imbriqués sont enfin gérés et les tests devraient s'exécuter sans
aucun souci comme le montre la capture d'écran ci-dessous.
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
Qu'en est-il des Performances ?
Afin de s'assurer que tout le temps passé jusqu'à présent n'a pas été dépensé inutilement, une
suite de tests de performance peut être exécutée. Quelques classes supplémentaires peuvent
être ajoutées au fichier forms.yml grâce à une boucle PHP afin de rendre les résultats plus
intéressants.
# <?php for ($i = 0; $i < 100; $i++): ?> #
Form<?php echo $i ?>: ~
# <?php endfor; ?> #
C'est le morceau de code ci-dessous qui a pour rôle de générer toutes ces classes.
mkdir($dir = sfConfig::get('sf_lib_dir').'/form/test_fixtures');
for ($i = 0; $i < 100; $i++)
{
file_put_contents($dir.'/Form'.$i.'.class.php', '<?php class Form'.$i.' extends
BaseForm { }');
}
Le benchmark est enfin prêt à être exécuter. Pour obtenir les résultats ci-dessous, la commande
Apache suivante a été exécutée sur un Macbook à plusieurs reprises jusqu'à obtenir un écart
standard de moins de 2 ms.
$ ab -t 60 -n 20 http://localhost/config_cache/web/index.php
Le premier benchmark de base ci-dessous exécute l'application par défaut sans les améliorations
apportées. Il convient tout d'abord de commenter l'appel de sfFormYamlEnhancer dans le fichier
frontendConfiguration, puis de relancer le test.
Connection Times (ms)
min mean[+/-sd] median
Connect:
0
0
0.0
0
Processing:
62
63
1.5
63
Waiting:
62
63
1.5
63
Total:
62
63
1.5
63
max
0
69
69
69
A présent, il s'agit de copier la première version de sfFormYamlEnhancer::enhance() qui
appelait directement sfYaml avant de relancer l'exécution du benchmark.
Connection Times (ms)
min mean[+/-sd] median
Connect:
0
0
0.0
0
Processing:
87
88
1.6
88
Waiting:
87
88
1.6
88
Total:
87
88
1.7
88
max
0
93
93
94
Ces tests montrent un ralentissement de 25 ms en moyenne à chaque requête, soit une
augmentation du temps d'exécution de près de 40%. Maintenant, il s'agit de modifier ces
changements afin d'appeler la méthode enhance() pour que le gestionnaire de configuration
personnalisé soit appelé.
Connection Times (ms)
min mean[+/-sd] median
Connect:
0
0
0.0
0
max
0
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
Processing:
Waiting:
Total:
62
62
62
63
63
64
1.6
1.6
1.6
63
63
63
70
70
70
On constate ici que le temps de traitement par défaut a été restauré en utilisant le gestionnaire
de configuration par défaut.
Bonus : Embarquer le Gestionnaire de Configuration dans un Plugin
Maintenant que cet excellent système d'amélioration des objets de formulaire via une
configuration YAML est développé, pourquoi ne pas l'embarquer dans un plugin, puis le partager
avec la communauté.
Cela peut paraître intimidant pour ceux qui n'ont jamais publié de plugin mais les quelques
lignes qui suivent dissiperont ces craintes. Le plugin aura la structure suivante.
sfFormYamlEnhancementsPlugin/
config/
sfFormYamlEnhancementsPluginConfiguration.class.php
lib/
config/
sfFormYamlEnhancementsConfigHandler.class.php
form/
sfFormYamlEnhancer.class.php
test/
unit/
form/
sfFormYamlEnhancerTest.php
Quelques modifications sont nécessaires afin de faciliter le processus d'installation du plugin. La
création et la connexion de l'objet optimisé doivent être encapsulées dans la classe de
configuration du plugin.
class sfFormYamlEnhancementsPluginConfiguration extends sfPluginConfiguration
{
public function initialize()
{
if ($this->configuration instanceof sfApplicationConfiguration)
{
$enhancer = new sfFormYamlEnhancer($this->configuration->getConfigCache());
$enhancer->connect($this->dispatcher);
}
}
}
Le script de test doit aussi être mis à jour afin de prendre en compte le chemin relatif vers le
script d'amorçage du projet.
include dirname(__FILE__).'/../../../../../test/bootstrap/unit.php';
// ...
Enfin, le plugin doit être activé dans la classe ProjectConfiguration.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
$this->enablePlugins('sfFormYamlEnhancementsPlugin');
}
}
Pour exécuter les tests depuis le plugin, il suffit de connecter ces derniers depuis la classe de
configuration ProjectConfiguration.
class ProjectConfiguration extends sfProjectConfiguration
{
// ...
public function setupPlugins()
{
$this->pluginConfigurations['sfFormYamlEnhancementsPlugin']->connectTests();
}
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
}
Les tests doivent maintenant s'exécuter correctement lorsqu'ils sont appelés à l'aide des
commandes test:*.
Toutes les classes sont maintenant rangées dans la structure du plugin bien qu'un autre
problème subsiste. Le script de test cherche toujours ces fichiers au niveau de l'arborescence du
projet. Il faut donc isoler le code dans la classe spécialisée qui appelle la configuration du cache
afin de surcharger la méthode dans le script de tests, et utiliser le fichier forms.yml.
class sfFormYamlEnhancer
{
// ...
public function enhance(sfForm $form)
{
$this->loadWorker();
$this->doEnhance($form->getFormFieldSchema(), $form);
}
public function loadWorker()
{
require_once $this->configCache->checkConfig('config/forms.yml');
}
// ...
}
La méthode loadWorker() peut alors être surchargée afin d'appeler le gestionnaire de
configuration personnalisé. La classe CommentForm doit aussi être déplacée dans le script de test
et le fichier forms.yml dans la structure test/fixtures du plugin.
include dirname(__FILE__).'/../../../../../test/bootstrap/unit.php';
$t = new lime_test(6);
class sfFormYamlEnhancerTest extends sfFormYamlEnhancer
{
public function loadWorker()
{
if (!class_exists('sfFormYamlEnhancementsWorker', false))
{
$configHandler = new sfFormYamlEnhancementsConfigHandler();
$code = $configHandler>execute(array(dirname(__FILE__).'/../../fixtures/forms.yml'));
$file = tempnam(sys_get_temp_dir(), 'sfFormYamlEnhancementsWorker');
file_put_contents($file, $code);
require $file;
}
}
}
class CommentForm extends BaseForm
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Manipuler le Cache de Configuration de symfony | symfony | Web PHP Framework
{
public function configure()
{
$this->setWidget('body', new sfWidgetFormTextarea());
$this->setValidator('body', new sfValidatorString(array('min_length' => 12)));
}
}
$configuration = $configuration->getApplicationConfiguration(
'frontend', 'test', true, null, $configuration->getEventDispatcher());
$enhancer = new sfFormYamlEnhancerTest($configuration->getConfigCache());
// ...
Enfin, la création du package du plugin est facilitée grâce au plugin sfTaskExtraPlugin qui
délivre une tâche plugin:package. Après exécution de cette dernière et quelques questions
posées dans la console, le plugin sera enfin prêt.
$ php symfony plugin:package sfFormYamlEnhancementsPlugin
Le code de cet article a été publié dans un plugin, et est disponible en téléchargement sur le
site de symfony :
http://symfony-project.org/plugins/sfFormYamlEnhancementsPlugin
Ce plugin inclut tout ce qui a été abordé dans ce chapitre et bien plus encore. Il fournit un
support pour les fichiers widgets.yml et validators.yml ainsi qu'une intégration avec la
tâche i18n:extract afin de fournir une internationalisation plus aisée des formulaires.
Conclusion
Ce chapitre a permis de se rendre compte, grâce aux benchmarks exécutés, que la gestion du
cache de configuration de symfony rend possible l'utilisation de fichiers YAML tout en préservant
un impact limité sur les performances.
« Tirer Profit de la Ligne de Commande
Travailler avec la Communauté symfony »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/14-Playing-with-Symfony-Config-Cache[31/12/2010 02:35:11]
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Travailler avec la Communauté symfony
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Par Stefan Koopmanschap
Choisir de travailler avec un projet open-source peut être motivé par
différentes raisons : la gratuité ou l'accès au code source par exemple.
Mais la raison principale réside bien souvent dans sa communauté.
Dans le monde de l'open-source, il existe autant de communautés que
de projets open-source. Concernant symfony, sa communauté est à ce
jour très ouverte et conviviale. Mais comment bénéficier au mieux de
cette communauté ? Et comment chacun peut y apporter sa propre
contribution ?
Ce chapitre présente la communauté symfony et les différents moyens
d'y collaborer. Les entreprises comme les développeurs trouveront ainsi
leur propre façon de participer.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Profiter au Mieux de la Communauté
Il existe diverses façons de profiter de la communauté symfony. Le simple fait d'utiliser le
framework est un bénéfice en soit, car même si, à l'origine, il a été développé et porté par
Sensio, symfony ne serait pas là où il en est aujourd'hui sans la forte implication de sa
communauté.
Le Support
Tous les développeurs, et plus particulièrement les débutants, se sont un jour ou l'autre déjà
retrouvés bloqués sans savoir comment résoudre un problème. Heureusement, symfony possède
une communauté active et accueillante qui se fera un plaisir de répondre à toutes les questions
que vous pourriez vous poser.
Avant de Poser une Question
Avant de poser une question sur les différents moyens mis à votre disposition, prenez le temps
de chercher une réponse par vous même. Vous pouvez bien sûr effectuer des recherches à l'aide
de Google, mais il est recommandé de concentrer vos recherches sur les différentes listes de
diffusion de symfony comme les archives de symfony-users.
Poser une Question
Cela peut paraître trivial mais il est important de savoir comment poser une question.
Réfléchissez bien à ce que vous êtes sur le point de demander. Vérifiez tout d'abord que la
réponse ne se trouve pas déjà dans la documentation officielle. Voici quelques conseils qui vous
aideront à obtenir des réponses plus pertinentes :
Réfléchissez à votre question. Assurez-vous de la formuler clairement. Expliquez ce que
vous faites (ou bien essayez de le faire) et ce que vous n'arrivez pas à faire. N'oubliez pas
d'indiquer les éventuelles erreurs que vous obtenez.
Expliquez vos tentatives en indiquant les éléments sur lesquels vous vous êtes appuyés et
les pistes dont vous disposez.
Les Listes de Diffusion
Plusieurs Groupes Google existent autour de symfony. Ces groupes sont le meilleur moyen
d'entrer en contact avec les utilisateurs et les développeurs de symfony. Si vous êtes utilisateur
de symfony, le groupe symfony users est le premier endroit pour rechercher de l'aide. Cette
liste de diffusion regroupe à la fois des utilisateurs de symfony, mais aussi des débutants et la
plupart des membres de la core team du framework. Par conséquent, il y aura toujours
quelqu'un capable de répondre à votre question. Il existe aussi d'autres groupes destinés à
d'autres sujets :
symfony-devs pour les discussions concernant le développement du framework (pas de
support !) ;
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
Chapter Content
Profiter au Mieux de la
Communauté
Le Support
Corrections et Nouvelles
Fonctionnalités
Plugins
Conférences et Événements
Réputation
Participer à la Communauté
Le Forum et les Listes de Diffusion
IRC
Contribuer au Code
Documenter
Présentations
Organiser un Evénement
Devenir Actif Localement
Intégrer la Core Team
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
symfony-docs pour les discussions concernant la documentation de symfony ;
symfony-community pour les sujets traitant des initiatives de la communauté.
Gardez bien à l'esprit que la liste de diffusion est un mode de communication indirect et bien
moins rapide que ne peut l'être IRC par exemple. Par conséquent, vous devrez attendre
quelques heures parfois plusieurs jours avant d'obtenir une réponse appropriée. Il est donc
important d'être réactif aux questions que pourrait susciter la vôtre. De manière générale, il
convient de rester patient en permanence.
Contrairement à IRC, vous devrez accompagner votre demande d'un maximum d'informations.
Indiquez votre configuration, l'ORM que vous utilisez, votre système d'exploitation, les solutions
que vous avez essayées et ce qui n'a pas fonctionné. N'hésitez pas à ajouter du code d'exemple
car plus vous expliciterez le contexte, et les réponses seront pertinentes.
IRC
Par nature, IRC est la façon la plus rapide d'obtenir une réponse. Symfony possède son propre
canal - #symfony - sur le réseau Freenode. Bien qu'une centaine de personnes puissent être
connectées simultanément, il n'en demeure pas moins qu'elles sont certainement au travail. Par
conséquent, vous devrez probablement faire face à un peu de patience avant d'obtenir une
réponse de leur part.
IRC se prête mal à l'affichage de gros blocs de code. Si le cas se présente durant une
discussion, des services comme pastebin.com vous permettent de formater votre code sur une
page web. Vous pouvez ainsi communiquer l'URL de cette page sur IRC. En réalité, poster un
bloc de code sur IRC revient généralement à s'attirer les foudres des autres participants, ce qui
ne jouera pas en votre faveur.
Une fois votre question posée sur le canal IRC, prêtez attention à toutes les réponses que vous
obtenez. Soyez réactif à d'éventuelles questions complémentaires. Certaines personnes
remettront votre approche du problème en question. Parfois elles auront raison, et d'autres fois
elles auront une vision erronée de votre problème.
Dans tous les cas, répondez à toutes les questions que l'on pourrait vous poser afin d'aider les
gens à se faire une idée de votre problématique et de son contexte. Si des personnes font de
mauvaises suppositions c'est qu'ils n'ont généralement pas assez de détails. Ne vous sentez en
aucun cas offensé car les participants sont là pour vous aider.
En cas d'affluence, et dans un soucis de clarté, veillez à préfixer vos réponses avec le nom de
votre interlocuteur.
Corrections et Nouvelles Fonctionnalités
L'ensemble du code de symfony est le produit de la communauté. Bien que Sensio et Fabien y
aient consacré beaucoup de leur temps, leur travail n'en demeure pas moins une production
communautaire.
En effet, en choisissant de rendre symfony open-source, ils ont prouvé leur attachement à la
communauté ! De même que les nombreux autres utilisateurs qui ont développé de nouvelles
fonctionnalités ou bien corrigé des bogues. En somme lorsque vous travaillez avec symfony (et
cela vaut aussi pour tous les autres projets open-source), soyez conscient que c'est grâce aux
efforts de la communauté.
Plugins
Symfony possède un système de plugins qui facilitent l'ajout de plugins externes aux projets
symfony. Ce système de plugins est construit autour du framework PEAR, ce qui fait de lui un
outil très flexible de-facto. En plus des plugins internes au framework, il existe un certain
nombre d'autres greffons développés et maintenus par la communauté.
Ces derniers sont disponibles sur le site des plugins et classés par catégories. N'hésitez pas à
effectuer des recherches parmi eux en les triant à l'aide des filtres de catégories, ORM et
versions de symfony supportées. Vous pouvez également saisir des mots clefs pour réduire votre
recherche. Grâce à la communauté, un grand nombre de fonctionnalités communes aux
applications web actuelles sont librement disponibles.
Conférences et Événements
A côté de toutes ces interactions numériques, vous pouvez aussi prendre le temps de rencontrer
les membres de la communauté à l'occasion de conférences et d'évènements. La plupart des
conférences PHP accueillent généralement des membres de la communauté symfony, qu'ils
soient spectateurs ou participants. Vous pourrez ainsi apprendre du travail de vos paires. Il
existe aussi des évènements dédiés à symfony tels que le Symfony Live, le SymfonyDay et le
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
Par Où Commencer ?
Autres Communautés
Conclusion
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
powered by google
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
SymfonyCamp. Toutes ces manifestations sont soutenues par une entreprise mais la majorité du
travail est réalisée par la communauté.
En participant à ce genre d'évènements, vous en apprendrez davantage sur symfony et vous
nouerez des liens avec des membres reconnus de la communauté. Ils pourront peut-être vous
prêter main forte plus tard en cas de coup dur. Si vous avez l'opportunité de participer à l'un de
ces évènements, n'hésitez surtout pas car cela en vaut la peine.
En plus des conférences « officielles », il existe un nombre croissant de groupes d'utilisateurs
symfony de par le monde. Ces groupes sont généralement indépendants et ne bénéficient pas
de l'aide d'une entreprise. Il s'agit en réalité de simples rassemblements d'utilisateurs. Il est très
facile d'y participer puisqu'il suffit généralement de s'y rendre. Ces réunions permettent de vous
constituer un réseau de contacts qui vous aideront sur des problèmes liés à symfony. Ces
groupes vous permettront également de trouver du travail ou bien de recruter de nouveaux
développeurs en cas de besoin.
Réputation
Participer à la communauté, rencontrer ses membres, communiquer, et aussi devenir un
membre actif, voilà ce qui vous permettra de construire votre propre réputation. Au début, ce
travail peut paraître inutile, hormis pour son propre ego, mais il peut aussi s'avérer très
intéressant. Par exemple, lorsqu'il s'agit de rechercher un nouvel emploi. En effet, avec une
bonne réputation, les offres seront plus nombreuses et plus intéressantes.
De la même manière, si vous êtes à la recherche de développeurs, une bonne réputation vous
permettra d'intéresser de nombreux candidats. I se pourrait même que votre offre intéresse
quelques grands noms de la communauté.
Participer à la Communauté
Toutes les communautés sont fondées sur un principe d'échange. S'il n'y avait personne pour
offrir quelque chose à la communauté, il n'y aurait rien à en retirer en retour non plus. De ce
fait, si vous avez bénéficié de la communauté, vous pouvez aussi lui offrir quelque chose en
retour. Voyons comment vous pouvez aider la communauté à se renforcer et à s'aggrandir.
Le Forum et les Listes de Diffusion
Comme expliqué plus haut, le forum et les listes de diffusion sont des excellents candidats pour
obtenir de l'aide. Vous y trouverez des réponses à vos questions, des suggestions pour résoudre
vos problèmes et des retours d'expérience sur des problématiques récurrentes.
Même si vous débutez avec symfony, l'expérience que accumulerez avec le temps vous
permettra de répondre à votre tour à des questions d'autres utilisateurs. Et plus vous acquerrez
d'expérience, plus vous serrez en mesure de répondre à des questions complexes. Le simple fait
de suggérer certaines pistes à des utilisateurs peut également les aider dans leurs propres
recherches. Étant déjà abonné à ces listes, il est très facile d'aider les autres.
IRC
Au même titre que les listes de diffusion, si vous êtes connectés sur le canal IRC de symfony,
vous pouvez de temps à autres répondre à des questions. Il est nul besoin de rester en
permanence devant votre client IRC, car la plupart des participants ne le font pas. Par contre,
dès qu'ils ont besoin de faire une pause dans leurs travaux, ils consultent le canal, regardent les
discussions en cours, donnent des coups de pouce (ou des solutions) à des problèmes ou bien
encore discutent d'autres sujets.
Être présent sur le canal peut aussi permettre aux autres participants de vous contacter en
spécifiant votre pseudonyme. La plupart des clients IRC vous notifieront cette prise de contact et
vous pourrez y répondre. Cela vous permet d'être plus accessible au cas où des membres de la
communauté se poseraient une question dont ils savent que vous pourrez y répondre. Ainsi,
même en ne faisant rien, vous aiderez des personnes en vous rendant simplement disponible.
Contribuer au Code
La plupart des utilisateurs de symfony sont des développeurs. Par conséquent, contribuer
directement au code du framework reste pour eux la façon la moins compliquée de s'investir
dans la communauté. La section suivante explique comment y parvenir.
Proposer des Patches
Il peut bien évidement arriver que vous trouviez un bogue dans symfony. Vous pouvez aussi
avoir besoin de réaliser une fonctionnalité qui n'est pas implémentée dans le framework.
Comme il n'est pas recommandé de modifier sa copie du framework car cela vous poserait des
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
problèmes à chaque nouvelle mise à jour, il vous est plutôt recommandé de contacter
directement les développeurs de symfony afin de leur exposer votre problème et votre solution.
Tout d'abord, vous devez modifier votre copie de symfony afin de corriger le bogue ou d'ajouter
un nouveau comportement. Ensuite, vous devez générer un différentiel des fichiers modifiés. Par
exemple, avec Subversion, cela se réalise grâce à cette commande :
$ svn diff > my_feature_or_bug_fix.patch
Vous devez utiliser cette commande à la racine de votre copie de travail pour que tous les
changements soient correctement inclus dans votre patch.
Enfin, rendez-vous sur le bugtracker de symfony, et après la phase d'identification, créez un
nouveau ticket. Remplissez le plus de champs possible afin de faciliter la reproduction de votre
bogue. Vous pouvez aussi détailler les parties de symfony qui sont affectées par vos
modifications.
Dans le champ "Ticket Properties", choisissez la version de symfony pour laquelle vous avez créé
le patch. Quand cela est possible, sélectionnez le composant de symfony que le patch modifie.
S'il y en a plusieurs, sélectionnez celui qui est le plus concerné.
Veuillez aussi à préfixer le contenu du champ "Short Summary" par [PATCH], puis cochez la case
indiquant qu'il y a un patch à attacher à ce ticket, le cas échéant.
Contribuer aux Plugins
Améliorer le framework n'est pas à la porté de tous. Mais tous les développeurs qui utilisent
symfony implémentent des fonctionnalités propres à leurs projets. Certaines de ces
fonctionnalités sont trop spécifiques pour intéresser d'autres développeurs, mais la plupart le
peuvent. Vous savez certainement que les « bonnes pratiques » recommandent de mettre la
logique de l'application dans des plugins afin de faciliter la réutilisation du code ultérieurement,
pour vous et/ou pour votre entreprise. Vous pouvez alors faire le choix de rendre ces plugins
open-source et de les mettre à la disposition des autres utilisateurs de symfony.
Développer un plugin symfony est une tâche simple. il suffit en effet de commencer par lire la
documentation concernant la création de plugins. Le site de symfony met à votre disposition un
ensemble d'outils qui vous permettent de publier vos créations via le canal PEAR des plugins
symfony et d'héberger vos sources sur le dépôt Subversion des plugins. Cette solution
avantageuse vous permet de ne pas avoir à configurer vous même votre serveur Subversion,
votre serveur PEAR et documenter l'ensemble.
De plus, si vous ajoutez votre plugin au système de plugins de symfony, idevient
instantanément disponible à l'ensemble de la communauté, sans plus de configuration de votre
part. Mais après tout, vous êtes libre de faire comme bon vous semble.
Documenter
La documentation est l'un des points forts de symfony. Elle fut initialement rédigée par la Core
Team mais la communauté y a aussi beaucoup contribué. Il existe également des travaux
conjoints entre la communauté et la Core Team comme le tutoriel Jobeet par exemple.
Dans tous les cas, une bonne documentation doit à la fois aider les nouveaux utilisateurs et
servir continuellement de référence aux développeurs plus expérimentés. Les sections suivantes
exposent les différentes façons de contribuer à cette abondante documentation.
Rédiger des Billets de Blog
Partager votre expérience et vos connaissances au sujet de symfony apporte beaucoup plus à la
communauté. Particulièrement lorsque vous traitez de sujets complexes. Bien référencés, ces
billets aideront d'autres développeurs à résoudre leur propres problématiques en prenant
exemple sur votre expérience.
Par conséquent, ne vous limitez pas à rédiger des billets présentant symfony dans ses grandes
lignes. Vous pouvez partager votre expérience, les problèmes que vous avez rencontrés ou
encore présenter les toutes dernières fonctionnalités de la nouvelle version de symfony.
Tous ceux qui écrivent des articles sur symfony sont invités à laisser l'adresse de leur blog dans
la liste des bloggeurs symfony. Tout le contenu de ces blogs est mis en avant sur la page
symfony de la communauté. Pour voir figurer vos propres billets, vous devez créer un flus RSS
contenant exclusivement du contenu relatif à symfony. Merci ne pas ajouter de flux autre que
votre blog (pas de flux twitter par exemple).
Écrire des Articles
Les développeurs les plus prolifiques peuvent creuser encore plus profond. Partout dans le
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
monde, de nombreux magazines traitant de PHP proposent de publier des articles. Ces articles
doivent être idéalement plus avancés, mieux organisés et de bien meilleure qualité qu'un simple
billet de blog, mais ils ont l'avantage d'être lus par plus de monde.
Ces magazines ont chacun leur propre façon de gérer les publications externes. Par conséquent,
reportez-vous au magazine ou sur son site web pour connaître les modalités de publication.
Les magazines internet comme les groupes d'utilisateurs PHP ou symfony, les sites de
développement web, etc... peuvent aussi être intéressés par vos articles.
Traduire la Documentation
Généralement les développeurs PHP sont à l'aise avec l'anglais. Cependant, pour beaucoup,
l'anglais n'est pas leur langue natale, ce qui rend parfois difficile la lecture de la documentation
technique. La communauté symfony encourage la traduction de sa documentation en donnant
les droits d'écriture sur le dépôt de la documentation à tous les traducteurs et en publiant ces
traductions sur le site officiel de symfony.
Ces traductions sont coordonnées sur la liste de diffusion symfony docs. Si vous êtes disposés à
traduire des articles, prenez le temps d'y faire un bref passage afin d'éviter les traductions en
double ou ce genre d'erreurs.
Ajouter du Contenu au Wiki
Le wiki est la façon la plus libre de rédiger de la documentation sur n'importe quel sujet.
Symfony possède son propre wiki où ses utilisateurs peuvent ajouter librement de la
documentation. Nous vous encourageons à poster vos propres oeuvres éditoriales, mais il est
également possible de parfaire les articles existants en les corrigeant ou en les améliorant. Il
existe aussi de vieux articles dont les contenus sont aujourd'hui obsolètes. Faire le ménage dans
ces articles est aussi une bonne façon de faciliter la recherche d'autres utilisateurs.
Si vous souhaitez vous faire une idée du genre d'articles que peut contenir le wiki ou si vous
avez besoin d'inspiration pour écrire vos propres articles, vous pouvez accéder à la page
d'accueil du wiki et consulter son contenu.
Présentations
Écrire des articles est un bon moyen de partager votre savoir et votre expérience. De plus, par
internet ils deviennent disponibles à tous et figurent dans les réponses des moteurs de
recherches. Cependant, il existe d'autres façons de partager votre expérience. Vous pouvez par
exemple réaliser des présentations à l'occasion d'événements.
aux conférences PHP / symfony ;
dans des réunions locales de développeurs ;
dans votre entreprise (pour vos collègues ou vos décideurs).
Suivant le lieu et l'audience, vous aurez sûrement à adapter votre présentation. Alors que les
décideurs ne seront pas intéressés par les détails techniques, les participants d'une conférence
sur symfony attendront plus qu'une présentation sommaire du framework. De ce fait, prenez le
temps de choisir convenablement votre sujet et préparer votre présentation. Faites relire vos
diapositives et si possible faites des essais devant des personnes qui pourront vous critiquer de
manière constructive.
Vous pouvez toujours trouver de l'aide pour votre présentation sur la liste de diffusion de la
communauté symfony, où de nombreuses personnes sont déjà intervenues lors de conférences.
De même, si vous ne savez pas comment réaliser votre présentation, abonnez-vous à cette liste
de diffusion afin de recevoir les appels à participation de nombreuses conférences et / ou
obtenir des contacts avec des groupes ou des associations locales.
Organiser un Evénement
En plus des présentations aux conférences déjà existantes, vous pouvez organiser vos propres
événements. Peu importe votre ambition et votre cible. Vous pouvez même organiser des
événements au sein d'autres événements.
Prenons comme exemple le « symfony update meeting » qui s'est tenu lors de la conférence
PHPNW en 2008. Tout a commencé sur twitter et sur IRC. Plusieurs utilisateurs de symfony
avaient des questions sur ce que serait symfony 1.2. Le jour de la conférence, durant l'une des
pauses entre deux sessions, une dizaine de personnes se sont rassemblées dans une pièce
fournie par l'organisation et ont eu un exposé sur symfony 1.2.
Cet événement fut court et pour peu de personnes, mais celles-ci repartirent toutes avec une
bonne idée de ce que serait, à l'époque, la nouvelle version de symfony.
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
Un autre bon exemple est l'organisation de conférences pour la communauté comme les
SymfonyCamp ou le SymfonyDay Cologne. Ces deux conférences symfony ont été organisées
par des entreprises utilisant symfony qui veulent collaborer avec la communauté. Toutes ces
conférences ont remporté un vif succès, notamment grâce à des intervenants passionnants.
Devenir Actif Localement
Comme il l'a été expliqué plus haut, tout le monde n'est pas capable de comprendre la
documentation ou bien les articles écris en anglais. La communication par internet peut aussi
avoir ses limites. Par conséquent, vous pouvez également vous investir localement pour
symfony.
Les meilleurs exemples sont les associations d'utilisateurs symfony. Depuis quelques années, de
nombreuses initiatives de ce genre ont vu le jour et la plupart ont déjà organisé des
rassemblements, le plus souvent informels et gratuits.
La liste de diffusion de la communauté symfony est un bon endroit pour trouver des groupes ou
associations dans votre région ou, à défaut, créer votre propre groupe. Vous trouverez sur cette
liste des membres et des organisateurs d'autres groupes qui seront capables de vous apporter
leur aide pour monter votre propre association.
Outre de véritables rassemblements locaux, vous pouvez aussi promouvoir symfony dans votre
langue sur internet. Lancer un portail symfony est un bon exemple. C'est le cas du site Spanish
symfony portal, qui informe les visiteurs des nouveaux articles en espagnol sur symfony. Ce site
met aussi à la disposition des visiteurs une importante documentation en espagnol qui permet
aux développeurs espagnols d'apprendre symfony, et de se tenir au courant de ses évolutions.
Intégrer la Core Team
La Core Team fait aussi partie de la communauté symfony. Tous les membres de la Core Team
ont commencé comme simples utilisateurs du framework. Ils se sont ensuite vus intégrés la
Core Team en raison de leur forte implication dans la communauté. Symfony est une
méritocratie, ce qui signifie que si vous faites preuve de talent et de compétences, vous aurez
peut être la chance d'intégrer cette Core Team au même titre que n'importe lequel de ses
membres.
Prenez l'exemple de la nomination de Bernhard Schussek. Bernhard a rejoint la Core Team après
son remarquable travail sur la seconde version du framework de tests unitaires Lime et ses
nombreuses propositions de patches.
Par Où Commencer ?
Maintenant que vous savez comment bénéficier de la communauté mais aussi comment y
contribuer, vous découvrirez dans les lignes suivantes les points de départ qui vous permettront,
selon vos possibilités et vos envies, de vous impliquer dans la communauté symfony.
La Liste de Diffusion de la Communauté symfony
La liste de diffusion de la communauté symfony offre aux membres un moyen de discuter des
initiatives de la communauté et de les rejoindre. Elle leur permet également d'échanger sur tous
les autres sujets ayant trait à la communauté.
Si vous souhaitez rejoindre l'une de ces initiatives, répondez simplement à la discussion relative
à ce projet. Si vous avez une idée qui peut servir la communauté symfony, n'hésitez pas à la
soumettre sur cette liste. De la même façon, vous pouvez y poser toutes vos questions
concernant la communauté ou les différentes façons d'interagir avec ses membres.
La page "How to contribute to symfony"
Depuis un certain temps maintenant, le wiki du site symfony possède un page spéciale intitulée
How to contribute to symfony. Cette page liste de manière exhaustive les différentes façons de
vous impliquer afin d'aider symfony et sa communauté, quelles que soient vos compétences.
C'est bien entendu un point de passage obligatoire pour toute personnes voulant s'impliquer
dans la communauté symfony.
Autres Communautés
Grâce au travail de nombreuses personnes, un certain nombre de projets ont vu le jour
concernant symfony et ses utilisateurs. Deux projets méritent tout particulièrement de les
mentionner.
Symfonians
Symfonians est un site web communautaire qui liste les développeurs et les entreprises qui
utilisent symfony au quotidien, ainsi que leurs projets symfony respectifs. Il permet aussi aux
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
The More with symfony book | Travailler avec la Communauté symfony | symfony | Web PHP Framework
entreprises de publier des offres d'emploi ou de stages.
Vous pouvez bien sûr entrer en contact avec les autres développeurs et les entreprises, et vous
disposez également d'un annuaire des applications symfony. Ce dernier est une importante
galerie qui présente l'ensemble des possibilités offertes par symfony. La diversité de ces
applications rend leur exploration très intéressante et donne une bonne vision de ce que l'on
peut accomplir avec ce framework.
Comme ce site est communautaire, vous pouvez y ouvrir un compte et créer votre profil, mais
aussi celui de votre entreprise et commencer à ajouter les applications que vous avez réalisées
ainsi que des offres d'emploi.
Le Groupe LinkedIn symfony
En tant que développeur PHP, vous avez certainement eu vent du site Internet LinkedIn sur
lequel avez probablement un profil détaillé. Pour ceux qui ne connaissent pas encore LinkedIn, il
s'agit d'un site sur lequel vous avez la possibilité de construire votre propre réseau social
professionnel et entrer en contact avec ses membres. LinkedIn offre également la possibilité de
créer des groupes de discussion, de publier des actualités et des offres d'emploi.
Symfony possède son propre groupe (identification nécessaire) et en devenant membre, vous
pourrez discuter de sujets relatifs à symfony, suivre les actualités du framework et aussi publier
/ consulter des offres d'emploi relatives à symfony.
Conclusion
Désormais, vous devriez avoir une bonne idée de ce que vous pouvez attendre de la
communauté symfony et de ce qu'elle peut attendre de vous. Gardez bien à l'esprit que tout
projet open-source repose sur la mobilisation de sa communauté. Ce soutien peut prendre un
grand nombre de formes, à commencer par les réponses aux questions des débutants, la
proposition de patches, en passant par le développement des plugins et la promotion du projet.
Alors, qu'attendez-vous pour nous rejoindre ?
« Manipuler le Cache de Configuration de symfony
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/15-Working-with-the-Symfony-Community[31/12/2010 02:35:16]
The More with symfony book | Appendix B - License | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Appendix B - License
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Attribution-Share Alike 3.0 Unported License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF
THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE").
THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE
LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER
THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU
ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A
CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED
HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the Work and other preexisting works, such as a translation, adaptation, derivative work, arrangement of music or
other alterations of a literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be recast,
transformed, or adapted including in any form recognizably derived from the original, except
that a work that constitutes a Collection will not be considered an Adaptation for the purpose
of this License. For the avoidance of doubt, where the Work is a musical work, performance
or phonogram, the synchronization of the Work in timed-relation with a moving image
("synching") will be considered an Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as encyclopedias and
anthologies, or performances, phonograms or broadcasts, or other works or subject matter
other than works listed in Section 1(f) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in which the Work is included
in its entirety in unmodified form along with one or more other contributions, each
constituting separate and independent works in themselves, which together are assembled
into a collective whole. A work that constitutes a Collection will not be considered an
Adaptation (as defined below) for the purposes of this License.
c. "Creative Commons Compatible License" means a license that is listed at
http://creativecommons.org/compatiblelicenses that has been approved by Creative
Commons as being essentially equivalent to this License, including, at a minimum, because
that license: (i) contains terms that have the same purpose, meaning and effect as the
License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of
works made available under that license under this License or a Creative Commons
jurisdiction license with the same License Elements as this License.
d. "Distribute" means to make available to the public the original and copies of the Work or
Adaptation, as appropriate, through sale or other transfer of ownership.
e. "License Elements" means the following high-level license attributes as selected by
Licensor and indicated in the title of this License: Attribution, ShareAlike.
f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under
the terms of this License.
g. "Original Author" means, in the case of a literary or artistic work, the individual,
individuals, entity or entities who created the Work or if no individual or entity can be
identified, the publisher; and in addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or
otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a
phonogram the producer being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the organization that
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-License[31/12/2010 02:35:21]
Chapter Content
Attribution-Share Alike 3.0
Unported License
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
- Français)
(Maîtrise de & Doctrine
The More with symfony book | Appendix B - License | symfony | Web PHP Framework
transmits the broadcast.
h. "Work" means the literary and/or artistic work offered under the terms of this License
including without limitation any production in the literary, scientific and artistic domain,
whatever may be the mode or form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a
dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a
musical composition with or without words; a cinematographic work to which are assimilated
works expressed by a process analogous to cinematography; a work of drawing, painting,
architecture, sculpture, engraving or lithography; a photographic work to which are
assimilated works expressed by a process analogous to photography; a work of applied art;
an illustration, map, plan, sketch or three-dimensional work relative to geography,
topography, architecture or science; a performance; a broadcast; a phonogram; a compilation
of data to the extent it is protected as a copyrightable work; or a work performed by a
variety or circus performer to the extent it is not otherwise considered a literary or artistic
work.
i. "You" means an individual or entity exercising rights under this License who has not
previously violated the terms of this License with respect to the Work, or who has received
express permission from the Licensor to exercise rights under this License despite a previous
violation.
j. "Publicly Perform" means to perform public recitations of the Work and to communicate
to the public those public recitations, by any means or process, including by wire or wireless
means or public digital performances; to make available to the public Works in such a way
that members of the public may access these Works from a place and at a place individually
chosen by them; to perform the Work to the public by any means or process and the
communication to the public of the performances of the Work, including by public digital
performance; to broadcast and rebroadcast the Work by any means including signs, sounds
or images.
k. "Reproduce" means to make copies of the Work by any means including without
limitation by sound or visual recordings and the right of fixation and reproducing fixations of
the Work, including storage of a protected performance or phonogram in digital form or other
electronic medium.
2. Fair Dealing Rights
Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or
rights arising from limitations or exceptions that are provided for in connection with the
copyright protection under copyright law or other applicable laws.
3. License Grant
Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide,
royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to
Reproduce the Work as incorporated in the Collections;
b. to create and Reproduce Adaptations provided that any such Adaptation, including any
translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise
identify that changes were made to the original Work. For example, a translation could be
marked "The original work was translated from English to Spanish," or a modification could
indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to
collect royalties through any statutory or compulsory licensing scheme cannot be waived, the
Licensor reserves the exclusive right to collect such royalties for any exercise by You of the
rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to
collect royalties through any statutory or compulsory licensing scheme can be waived, the
Licensor waives the exclusive right to collect such royalties for any exercise by You of the
rights granted under this License; and,
iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-License[31/12/2010 02:35:21]
and more...
Search
powered by google
The More with symfony book | Appendix B - License | symfony | Web PHP Framework
individually or, in the event that the Licensor is a member of a collecting society that
administers voluntary licensing schemes, via that society, from any exercise by You of the
rights granted under this License.
The above rights may be exercised in all media and formats whether now known or hereafter
devised. The above rights include the right to make such modifications as are technically
necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights
not expressly granted by Licensor are hereby reserved.
4. Restrictions
The license granted in Section 3 above is expressly made subject to and limited by the
following restrictions:
a. You may Distribute or Publicly Perform the Work only under the terms of this License. You
must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every
copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of the recipient of the Work
to exercise the rights granted to that recipient under the terms of the License. You may not
sublicense the Work. You must keep intact all notices that refer to this License and to the
disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When
You Distribute or Publicly Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the Work from You to exercise
the rights granted to that recipient under the terms of the License. This Section 4(a) applies
to the Work as incorporated in a Collection, but this does not require the Collection apart
from the Work itself to be made subject to the terms of this License. If You create a
Collection, upon notice from any Licensor You must, to the extent practicable, remove from
the Collection any credit as required by Section 4(c), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from
the Adaptation any credit as required by Section 4(c), as requested.
b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this
License; (ii) a later version of this License with the same License Elements as this License;
(iii) a Creative Commons jurisdiction license (either this or a later license version) that
contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv)
a Creative Commons Compatible License. If you license the Adaptation under one of the
licenses mentioned in (iv), you must comply with the terms of that license. If you license the
Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable
License"), you must comply with the terms of the Applicable License generally and the
following provisions: (I) You must include a copy of, or the URI for, the Applicable License
with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer
or impose any terms on the Adaptation that restrict the terms of the Applicable License or the
ability of the recipient of the Adaptation to exercise the rights granted to that recipient under
the terms of the Applicable License; (III) You must keep intact all notices that refer to the
Applicable License and to the disclaimer of warranties with every copy of the Work as
included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or
Publicly Perform the Adaptation, You may not impose any effective technological measures on
the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise
the rights granted to that recipient under the terms of the Applicable License. This Section
4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the
Collection apart from the Adaptation itself to be made subject to the terms of the Applicable
License.
c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must,
unless a request has been made pursuant to Section 4(a), keep intact all copyright notices
for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name
of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author
and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity,
journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or
by other reasonable means, the name of such party or parties; (ii) the title of the Work if
supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to
be associated with the Work, unless such URI does not refer to the copyright notice or
licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an
Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French
translation of the Work by Original Author," or "Screenplay based on original Work by Original
Author"). The credit required by this Section 4(c) may be implemented in any reasonable
manner; provided, however, that in the case of a Adaptation or Collection, at a minimum
such credit will appear, if a credit for all contributing authors of the Adaptation or Collection
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-License[31/12/2010 02:35:21]
The More with symfony book | Appendix B - License | symfony | Web PHP Framework
appears, then as part of these credits and in a manner at least as prominent as the credits for
the other contributing authors. For the avoidance of doubt, You may only use the credit
required by this Section for the purpose of attribution in the manner set out above and, by
exercising Your rights under this License, You may not implicitly or explicitly assert or imply
any connection with, sponsorship or endorsement by the Original Author, Licensor and/or
Attribution Parties, as appropriate, of You or Your use of the Work, without the separate,
express prior written permission of the Original Author, Licensor and/or Attribution Parties.
d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by
applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as
part of any Adaptations or Collections, You must not distort, mutilate, modify or take other
derogatory action in relation to the Work which would be prejudicial to the Original Author's
honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any
exercise of the right granted in Section 3(b) of this License (the right to make Adaptations)
would be deemed to be a distortion, mutilation, modification or other derogatory action
prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the applicable national law, to
enable You to reasonably exercise Your right under Section 3(b) of this License (right to make
Adaptations) but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER
DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE
LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate automatically upon any breach
by You of the terms of this License. Individuals or entities who have received Adaptations or
Collections from You under this License, however, will not have their licenses terminated
provided such individuals or entities remain in full compliance with those licenses. Sections 1,
2, 5, 6, 7, and 8 will survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is perpetual (for the
duration of the applicable copyright in the Work). Notwithstanding the above, Licensor
reserves the right to release the Work under different license terms or to stop distributing the
Work at any time; provided, however that any such election will not serve to withdraw this
License (or any other license that has been, or is required to be, granted under the terms of
this License), and this License will continue in full force and effect unless terminated as stated
above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to
the recipient a license to the Work on the same terms and conditions as the license granted
to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient
a license to the original Work on the same terms and conditions as the license granted to You
under this License.
c. If any provision of this License is invalid or unenforceable under applicable law, it shall not
affect the validity or enforceability of the remainder of the terms of this License, and without
further action by the parties to this agreement, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
d. No term or provision of this License shall be deemed waived and no breach consented to
unless such waiver or consent shall be in writing and signed by the party to be charged with
such waiver or consent.
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-License[31/12/2010 02:35:21]
The More with symfony book | Appendix B - License | symfony | Web PHP Framework
e. This License constitutes the entire agreement between the parties with respect to the Work
licensed here. There are no understandings, agreements or representations with respect to
the Work not specified here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be modified without the
mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this License were drafted
utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic
Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO
Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the
Universal Copyright Convention (as revised on July 24, 1971). These rights and subject
matter take effect in the relevant jurisdiction in which the License terms are sought to be
enforced according to the corresponding provisions of the implementation of those treaty
provisions in the applicable national law. If the standard suite of rights granted under
applicable copyright law includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this License is not intended to
restrict the license of any rights under applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty whatsoever in
connection with the Work. Creative Commons will not be liable to You or any party on any legal
theory for any damages whatsoever, including without limitation any general, special, incidental
or consequential damages arising in connection to this license. Notwithstanding the foregoing two
(2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the Work is licensed under the
CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative
Commons" or any related trademark or logo of Creative Commons without the prior written
consent of Creative Commons. Any permitted use will be in compliance with Creative Commons'
then-current trademark usage guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt, this trademark restriction
does not form part of the License.
Creative Commons may be contacted at http://creativecommons.org/.
Annexe A - Exemple de Script d'Installation Personnalisé »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-License[31/12/2010 02:35:21]
The More with symfony book | Annexe A - Code JavaScript du Widget sfWidgetFormGMapAddress | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Annexe A - Code JavaScript du Widget sfWidgetFormGMapAddress
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Le code ci-dessous correspond au JavaScript nécessaire au bon
fonctionnement du widget sfWidgetFormGMapAddress :
function sfGmapWidgetWidget(options){
// this global attributes
this.lng
= null;
this.lat
= null;
this.address = null;
this.map
= null;
this.geocoder = null;
this.options = options;
this.init();
}
sfGmapWidgetWidget.prototype = new Object();
sfGmapWidgetWidget.prototype.init = function() {
if(!GBrowserIsCompatible())
{
return;
}
// retrieve dom element
this.lng
= jQuery("#" + this.options.longitude);
this.lat
= jQuery("#" + this.options.latitude);
this.address = jQuery("#" + this.options.address);
this.lookup
= jQuery("#" + this.options.lookup);
// create the google geocoder object
this.geocoder = new GClientGeocoder();
// create the map
this.map = new GMap2(jQuery("#" + this.options.map).get(0));
this.map.setCenter(new GLatLng(this.lat.val(),
this.lng.val()), 13);
this.map.setUIToDefault();
// cross reference object
this.map.sfGmapWidgetWidget = this;
this.geocoder.sfGmapWidgetWidget = this;
this.lookup.get(0).sfGmapWidgetWidget = this;
// add the default location
var point = new GLatLng(this.lat.val(), this.lng.val());
var marker = new GMarker(point);
this.map.setCenter(point, 15);
this.map.addOverlay(marker);
// bind the move action on the map
GEvent.addListener(this.map, "move", function() {
var center = this.getCenter();
this.sfGmapWidgetWidget.lng.val(center.lng());
this.sfGmapWidgetWidget.lat.val(center.lat());
});
// bind the click action on the map
GEvent.addListener(this.map, "click", function(overlay,
latlng) {
if (latlng != null) {
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-sfWidgetFormGMapAddress[31/12/2010 02:35:24]
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
The More with symfony book | Annexe A - Code JavaScript du Widget sfWidgetFormGMapAddress | symfony | Web PHP Framework
sfGmapWidgetWidget.activeWidget = this.sfGmapWidgetWidget;
this.sfGmapWidgetWidget.geocoder.getLocations(
latlng,
sfGmapWidgetWidget.reverseLookupCallback
);
}
});
// bind the click action on the lookup field
this.lookup.bind('click', function(){
sfGmapWidgetWidget.activeWidget = this.sfGmapWidgetWidget;
this.sfGmapWidgetWidget.geocoder.getLatLng(
this.sfGmapWidgetWidget.address.val(),
sfGmapWidgetWidget.lookupCallback
);
return false;
})
}
sfGmapWidgetWidget.activeWidget = null;
sfGmapWidgetWidget.lookupCallback = function(point)
{
// get the widget and clear the state variable
var widget = sfGmapWidgetWidget.activeWidget;
sfGmapWidgetWidget.activeWidget = null;
if (!point) {
alert("address not found");
return;
}
widget.map.clearOverlays();
widget.map.setCenter(point, 15);
var marker = new GMarker(point);
widget.map.addOverlay(marker);
}
sfGmapWidgetWidget.reverseLookupCallback = function(response)
{
// get the widget and clear the state variable
var widget = sfGmapWidgetWidget.activeWidget;
sfGmapWidgetWidget.activeWidget = null;
widget.map.clearOverlays();
if (!response || response.Status.code != 200) {
alert('no address found');
return;
}
// get information location and init variables
var place = response.Placemark[0];
var point = new
GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
var marker = new GMarker(point);
// add marker and center the map
widget.map.setCenter(point, 15);
widget.map.addOverlay(marker);
// update values
widget.address.val(place.address);
widget.lat.val(place.Point.coordinates[1]);
widget.lng.val(place.Point.coordinates[0]);
}
Annexe A - Exemple de Script d'Installation Personnalisé »
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-sfWidgetFormGMapAddress[31/12/2010 02:35:24]
powered by google
The More with symfony book | Annexe A - Code JavaScript du Widget sfWidgetFormGMapAddress | symfony | Web PHP Framework
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/A-sfWidgetFormGMapAddress[31/12/2010 02:35:24]
The More with symfony book | Annexe A - Exemple de Script d'Installation Personnalisé | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
Annexe A - Exemple de Script d'Installation Personnalisé
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Le code PHP ci-dessous est un script d'installation personnalisé utilisé
dans le Chapitre 06 :
<?php
$this->logSection('install', 'default to sqlite');
$this->runTask('configure:database',
sprintf("'sqlite:%s/database.db'",
sfConfig::get('sf_data_dir')));
$this->logSection('install', 'create an application');
$this->runTask('generate:app', 'frontend');
$this->setConfiguration($this->createConfiguration('frontend',
'dev'));
$this->logSection('install', 'publish assets');
$this->runTask('plugin:publish-assets');
if (file_exists($dir =
sfConfig::get('sf_symfony_lib_dir').'/../data'))
{
$this->installDir($dir);
}
$this->logSection('install', 'create the database schema');
file_put_contents(sfConfig::get('sf_config_dir').'/doctrine/schem
, <<<EOF
Product:
columns:
name:
{ type: string(255), notnull: true }
price:
{ type: decimal, notnull: true }
ProductPhoto:
columns:
product_id:
{ type: integer }
filename:
{ type: string(255) }
caption:
{ type: string(255), notnull: true }
relations:
Product:
alias:
Product
foreignType: many
foreignAlias: Photos
onDelete:
cascade
EOF
);
$this->logSection('install', 'add some fixtures');
file_put_contents(sfConfig::get('sf_data_dir').'/fixtures/fixture
, <<<EOF
Product:
product_1:
name: Product Name
price: 25.95
EOF
);
$this->logSection('install', 'build the model');
$this->runTask('doctrine:build', '--all --and-load --noconfirmation');
$this->logSection('install', 'create a simple CRUD module');
http://www.symfony-project.org/more-with-symfony/1_4/fr/B-Custom-Installer-Example[31/12/2010 02:35:26]
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
and more...
Search
The More with symfony book | Annexe A - Exemple de Script d'Installation Personnalisé | symfony | Web PHP Framework
$this->runTask('doctrine:generate-module', 'frontend product
Product --non-verbose-templates');
$this->logSection('install', 'fix sqlite database permissions');
chmod(sfConfig::get('sf_data_dir'), 0777);
chmod(sfConfig::get('sf_data_dir').'/database.db', 0777);
powered by google
« Appendix B - License
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/B-Custom-Installer-Example[31/12/2010 02:35:26]
The More with symfony book | A propos des Auteurs | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
A propos des Auteurs
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Fabien Potencier
Fabien Potencier a découvert le web en 1994, au temps où la connexion
à Internet était synonyme de bruits stridents du modem. Développeur
passioné, il a immédiatement commencé à créer des sites web avec
Perl. Avec l'arrivée de PHP5, il a décidé de se concentrer sur PHP et a
créé le projet du framework symfony en 2004 afin d'aider son entreprise
à tirer profit de la puissance de PHP pour ses clients.
Fabien est un serial-entrepreneur qui a créé en autres, Sensio, une
entreprise de services et de consulting spécialisée dans les technologies
du web et marketing sur Internet, en 1998.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Fabien est également le créateur de plusieurs autres projets opensource, un écrivain, un bloggeur, un maître de conférences
international, et un père comblé de deux enfants.
Son site Web : http://fabien.potencier.org/
Sur Twitter : http://twitter.com/fabpot
Jonathan H. Wage
Jonathan Wage a travaillé sur une multitude de technologies web depuis près de 10 ans. Il a été
à la fois le développeur principal et l'architecte d'applications chez CentreSource. Aujourd'hui,
vous le trouverez chez Sensio Labs, les créateurs du framework MVC symfony. Ses principales
responsabilités reposent sur la formation des autres développeurs et le travail sur les projets
open-source symfony et Doctrine. Il contribue au développement de symfony et est le chef du
projet Doctrine.
Son site Web : http://www.jwage.com/
Sur Twitter : http://twitter.com/jwage
Geoffrey Bachelet
Geoffrey Bachelet est un développeur autodidacte depuis le début des années 2000. Curieux des
nouvelles technologies et toujours à la recherche de nouvelles choses à apprendre, il a essayé
de nombreuses technologies liées au web, dont (entre autres) de nombreux frameworks tels que
Zend Framework, Ruby on Rails et merb. Il a finalement rejoint Sensio Labs où il s'est développé
une grande compréhension du framework PHP symfony. Il est aussi un membre fondateur de
l'association française des utilisateurs de symfony, l'AFSY, et est impatient de s'investir
davantage dans la communauté symfony en écrivant et en participant à des conférences.
Son site Web : http://mirmodynamics.com/
Sur Twitter : http://twitter.com/ubermuda
Kris Wallsmith
Kris Wallsmith est un développeur web freelance de Portland dans l'Oregon. Il passe son temps
dans la Silicon Forrest à diriger la création de startups, à jouer au football et à élever ses deux
enfants. Kris est également maître de conférences international, membre de la Core Team de
symfony et Release Manager des versions 1.3 et 1.4.
Son site Web : http://kriswallsmith.net
Sur Twitter : http://twitter.com/kriswallsmith
Chapter Content
Fabien Potencier
Jonathan H. Wage
Geoffrey Bachelet
Kris Wallsmith
Hugo Hamon
Thomas Rabaix
Stefan Koopmanschap
Fabrice Bernhard
Ryan Weaver
Laurent Bonnet
Hugo Hamon
Hugo Hamon est un autodidacte en technologies du web depuis la fin de l'année 2000 et
travaille aujourd'hui chez Sensio Labs en tant que responsable du pôle formations. Passionné par
PHP, il utilise ce langage depuis plus de 8 ans et a créé le site web français de tutoriels
http://www.symfony-project.org/more-with-symfony/1_4/fr/authors[31/12/2010 02:35:29]
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
The More with symfony book | A propos des Auteurs | symfony | Web PHP Framework
Apprendre-PHP.com. Hugo est impliqué dans deux associations : l'AFUP en tant que membre et
l'AFSY, le groupe d'utilisateurs de symfony, en tant que fondateur et Président. Il a également
co-écrit le livre Mieux Programmer en PHP avec symfony 1.2 et Doctrine aux Editions Eyrolles
ainsi que divers articles pour le magazine PHP Solutions.
Son site Web : http://www.hugohamon.com
Sur Twitter : http://twitter.com/hhamon
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
(Maîtrise de & Doctrine
- Français)
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
Thomas Rabaix
Thomas est un fan de symfony depuis le début. Il travaille actuellement comme travailleur
indépendant avec PHP et symfony. Il est à la fois développeur, consultant et formateur, mais il
est également très actif au sein de la communauté symfony.
Thomas aide également les entreprises sur les sujets d'assurance qualité et les questions
d'architecture sur des projets symfony stimulants.
Son site Web : http://rabaix.net
Stefan Koopmanschap
Stefan Koopmanschap est à la fois développeur, consultant et formateur. Investi dans le projet
symfony en tant qu'utilisateur et évangéliste depuis la fin de l'année 2006, il est devenu le
Responsable de la Communauté du projet à la fin de l'été 2009. Dans le passé, Stefan a
également participé à d'autres projets open-source tels que Zend Framework et phpBB.
En dehors de son amour pour la communauté, Stefan garde un oeil attentif sur les bonnes
pratiques et améliore les compétences des développeurs désireux d'apprendre davantage. Il est
maître de conférences régulier et secrétaire du groupe phpBenelux.
A part PHP, Stefan a un amour inconditionnel pour la musique, la lecture et par dessus tout pour
sa femme Marjolein et ses deux enfants Tomas et Yara.
Son site Web : http://www.leftontheweb.com/
Sur Twitter : http://twitter.com/skoop
Fabrice Bernhard
Fabrice Bernhard a découvert les joies du développement PHP en 1996. Son tout premier site a
fort heureusement disparu à tout jamais dans les abîmes d'Internet, mais il a aussitôt attrapé le
virus. Après avoir étudié les mathématiques et l'informatique à Paris et Zurick, il est devenu
entrepreneur en 2007 avec Allomatch.com, un site de recensement des évènements sportifs en
France. Ensuite, il a co-fondé Theodo en 2008, une entreprise de consulting orientée sur le
développement, spécialisée dans les projets web de qualité et dans les solutions open-source
avec une forte préférence pour symfony.
Son site Web : http://www.allomatch.com/
Son blog : http://www.theodo.fr/blog
Ryan Weaver
Ryan Weaver est lead programmer à Iostudio, LLC à Nashville dans le Tennesee, et est un
fervent supporter de symfony et d'autres technologies open-source. Il est membre du groupe
Nashville OSS et co-organisateur du groupe d'utilisateurs de symfony de Nashvile. Ryan aime
aussi courir, lire et voyager avec sa petite amie Leanna.
En tant que bloggeur, Ryan aime partager des solutions et enseigner des méthodes utiles pour
aider les autres développeurs. Il est convaincu qu'un framework et sa documentation doivent
être accessibles par tout le monde.
Son site Web : http://www.thatsquality.com/
Sur Twitter : http://twitter.com/weaverryan
Laurent Bonnet
Laurent est architecte de plate-forme web chez Microsoft qui se concentre sur les
infrastructures, le développement et le design de fermes de serveurs à haute disponibilité.
Son nom de code Microsoft est lorenbo, et a été créé en janvier 1992, lorsqu'il a rejoint
l'entreprise en tant qu'ingénieur logiciel en assistance aux développeurs sur des noyaux
Windows NT. Il rejoint le monde du développement en 1994 pour mettre en place le programme
de direction régionale, où il a recruté des intégrateurs spécialisés pour représenter Microsoft lors
de séminaires et de conférences.
http://www.symfony-project.org/more-with-symfony/1_4/fr/authors[31/12/2010 02:35:29]
and more...
Search
powered by google
The More with symfony book | A propos des Auteurs | symfony | Web PHP Framework
Puis il a sauté dans le train en marche d'Internet au début de l'année 1996 en tant que chef de
projet technique de Visual Studio/Visual InterDev et Internet Explorer.
En 1998 et 1999, il devient le représentant français de Microsoft à l'AFNOR (organisme
représentant la France à l'ISO), où il a participé au processus de standardisation de Java.
Laurent rejoint une position internationale en 2000 pour développer l'infrastructure de Microsoft
Commercial Internet System, une solution de portail d'e-commerce hautement évolutive codéveloppée par CompuServ, visant Telcos et Hosters.
Il rejoint Microsoft France en 2004, pour piloter une nouvelle activité consacrée aux hébergeurs
et aux communautés du web. Il est toujours à ce poste 6 ans plus tard, aidant les grands
hébergeurs en France à tirer parti des produits et solutions basées sur Windows. Plus
récemment, il s'est intéressé aux marchés français à bas-prix.
Laurent est un intervenant régulier des divers évènements internationaux (MS-Days, Tech days,
Hosting Days).
Son web Web : http://blogs.msdn.com/laurenbo, et occasionnellement sur
http://laurenbo.wordpress.com/
Sur Twitter : http://twitter.com/laurenbo
A Propos des Traducteurs »
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/authors[31/12/2010 02:35:29]
The More with symfony book | A Propos des Traducteurs | symfony | Web PHP Framework
About
Installation
Documentation
Plugins
Community
Blog
Development
The More with symfony book
A Propos des Traducteurs
You are currently browsing "The More with symfony book" in French for the 1.4 version - Switch to language:
French (fr)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Hugo Hamon
Hugo Hamon est un autodidacte passionné des technologies web depuis
la fin de l' année 2000, et travaille aujourd'hui chez Sensio Labs en tant
que développeur et responsable de formations. Passioné par PHP, il
pratique le langage depuis 8 ans et c'est avec celui-ci qu'il a bâti le site
français Apprendre-PHP.com. Hugo s'implique aussi dans deux
associations françaises : l'AFUP comme membre de l'organisation et
tout récemment dans l'AFSY, le groupe des utilisateurs symfony, en
tant que membre fondateur et Président. Il est enfin le co-auteur de
l'ouvrage Mieux Programmer en PHP avec symfony 1.2 et Doctrine
publié aux Editions Eyrolles.
About
You are currently reading "The
More with symfony book" which
is licensed under the Creative
Commons Attribution-Share
Alike 3.0 Unported License
license.
Support symfony!
Buy this book
or donate.
Son site Web : http://www.hugohamon.com
Son Twitter : http://twitter.com/hhamon
Guillaume Bretou
Guillaume BRETOU est un ingénieur en informatique qui a étudié à Polytech Paris Sud (ex
IFIPS). Passionné du web et du PHP, il utilise ce langage depuis le début de son cursus
informatique. Il a pu travailler pour divers grands comptes dont la société Arianespace pour
laquelle il a réalisé plusieurs applications avec le framework Symfony. Il travaille aujourd'hui
chez Sensio Labs en tant que développeur symfony.
**Son twitter : http://twitter.com/gbretou*
Xavier Briand
Xavier baigne depuis toujours dans l'univers de l'informatique, et a rapidement choisi Internet
comme terrain de jeu. Après ses études, il s'est essayé au métier de freelance et à la création
d'entreprise avant de rejoindre Sensio Labs. Passionné du web et de la vie, il pratique
alternativement le roller, les arts martiaux, la batterie, la photographie et passe son temps à lire
(fantasy/sci-fi).
Site perso : http://ezrk.net
Son twitter : http://twitter.com/xavierbriand
Nicolas Ricci
Nicolas a attrapé le virus Symfony en 2006, et a contaminé de nombreux autres développeurs
par la suite. Il travaille aujourd'hui comme consultant pour l'AgencePix.
Son site Web : http://www.nrsoft.co.uk
Thomas Rabaix
Thomas est un fan de symfony depuis le début. Il travaille actuellement comme travailleur
indépendant avec PHP et symfony. Il est à la fois développeur, consultant et formateur, mais il
est également très actif au sein de la communauté symfony.
Thomas aide également les entreprises sur les sujets d'assurance qualité et les questions
d'architecture sur des projets symfony stimulants.
Chapter Content
Hugo Hamon
Guillaume Bretou
Xavier Briand
Nicolas Ricci
Thomas Rabaix
Fabrice Bernhard
Son site Web : http://rabaix.net
Fabrice Bernhard
Fabrice Bernhard a découvert les joies du développement PHP en 1996. Son tout premier site a
fort heureusement disparu à tout jamais dans les abîmes d'Internet, mais il a aussitôt attrapé le
virus. Après avoir étudié les mathématiques et l'informatique à Paris et Zurick, il est devenu
entrepreneur en 2007 avec Allomatch.com, un site de recensement des évènements sportifs en
http://www.symfony-project.org/more-with-symfony/1_4/fr/translators[31/12/2010 02:35:32]
Be trained by symfony experts
Jan 24: Paris
(Maîtrise de & Doctrine
- Français)
Feb 21: Paris
(Maîtrise de & Doctrine
- Français)
Mar 21: Paris
- Français)
(Maîtrise de & Doctrine
The More with symfony book | A Propos des Traducteurs | symfony | Web PHP Framework
France. Ensuite, il a co-fondé Theodo en 2008, une entreprise de consulting orientée sur le
développement et spécialisée dans les projets web de qualité et dans les solutions open-source
avec une forte préférence pour symfony.
Apr 18: Paris
(Maîtrise de & Doctrine
- Français)
May 23: Paris
(Maîtrise de & Doctrine
- Français)
Son site Web : http://www.allomatch.com/
and more...
Son blog : http://www.theodo.fr/blog
Search
« A propos des Auteurs
powered by google
Questions & Feedback
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.
Powered by
- Make a donation - "symfony" is a trademark of Fabien Potencier. All rights reserved.
Since 1998, Sensio Labs has been promoting
the Open-Source software movement by
providing quality web application
development, training, consulting.
Sensio Labs also supports several large OpenSource projects.
Open-Source Products
Services
Symfony - MVC framework
Symfony Components
Doctrine - ORM
Swift Mailer - Mailing library
Twig - Template library
Pirum - PEAR channel server
Trainings - Be Trained by experts
Guru - Get a guru for a day
Partners - Specialists around the world
Books - Read Open-Source books
Conferences - The Symfony Live
Conference
> More
http://www.symfony-project.org/more-with-symfony/1_4/fr/translators[31/12/2010 02:35:32]