askeet jour 1
Installation de symfony
documentations annexes sur l'installation de symfony- Installation générale/portable de Symfony
- Installer Symfony et Wamp
- Installer Symfony sous IIS
- Installer Symfony sur Mac OSX 10.5 Leopard
Le moyen le plus simple d'installer symfony est d'utiliser le package PEAR. Attention cependant: pour pouvoir utiliser les channels - et ainsi accéder au channel de symfony - vous devez disposer de la version PEAR 1.4.0 ou supérieur (à moins que vous ne soyez sous PHP 5.1.0, qui inclue PEAR 1.4.5):
$ pear config-set http_proxy 192.168.106.20:8080
$ sudo pear upgrade PEAR
Vous pouvez à présent ajouter le channel 'symfony':
$ pear channel-discover pear.symfony-project.com
Vous êtes prêt à installez la dernière version stable de symfony 1.0, avec toutes ses dépendances:
$ sudo pear install symfony/symfony-1.0.19
Vérifiez que symfony est bien installé en utilisant la ligne de commande pour vérifier la version:
$ symfony -V
Vous pouvez lister toutes les options de la commande symfony en tapant simplement
$ symfony
ou
$ symfony -T
Création d'un projet
Dans symfony, des applications qui partagent le même modèle objet sont regroupées dans des projets. Pour le projet askeet, il y aura une application publique et une application de gestion privée : cela fait deux applications. Comme le projet est le conteneur des applications, il doit être créé en premier. Pour cela, tout ce dont vous avez besoin est un répertoire et un appel à la commande symfony init-project :
$ mkdir /var/www/askeet
$ cd /var/wwww/askeet
$ symfony init-project askeet
Vous pouvez maintenant créer l'application 'frontend' avec la commande symfony init-app:
$ symfony init-app frontend
si vous êtes sous Windows, il vaut mieux créer votre projet dans un répertoire dont le chemin d'accès ne contient pas d'espace - à ce titre, le répertoire Documents and Settings est déconseillé.
Configuration web
Configuration du serveur
A présent, il est temps de s'attaquer à la configuration Apache pour rendre l'application accessible par un navigateur. Dans un contexte de développement professionnel, il est préférable de configurer une nouvelle application en Virtual Host, et c'est la solution qui sera décrite ici.
Ouvrez le fichier httpd.conf de votre répertoire Apache/conf/ et ajoutez à la fin :
NameVirtualHost *:80
<VirtualHost *:80>
ServerName askeet
DocumentRoot "/var/www/askeet/web"
DirectoryIndex index.php
Alias /sf /usr/share/php/data/symfony/web/sf
<Directory "/var/www/askeet/web">
AllowOverride All
</Directory>
</VirtualHost>
apache doit être redémarré pour que le nouveau virtual host soit pris en compte
sudo /etc/init.d/apache restart
N.B. l'alias /sf doit pointer vers le répertoire symfony dans le dossier data de PEAR. Si vous ne savez pas de quel répertoire il s'agit, lancez la commande pear config-show. Les applications symfony nécessitent un accès à ce répertoire pour afficher certaines images ou charger des javascripts, notamment pour faire fonctionner la boîte à outils de web debug et les helpers AJAX.
Sous Windows, il faut remplacer la ligne de l'Alias par quelque chose comme:
Alias /sf "C:\php\pear\data\symfony\web\sf"
Déclaration du nom de domaine
Le nom de domaine askeet doit être déclaré localement.
Sous Linux, cette déclaration doit être faite dans le fichier /etc/hosts. Sous Windows XP, le fichier hosts est dans le répertoire C:\WINDOWS\system32\drivers\etc\.
Ajoutez la ligne suivante (pas nécessaire pour les salles de TP):
127.0.0.1 askeet
N.B. vous devez disposer de droits administrateur pour effectuer cette modification.
N.B.bis Si vous ne souhaitez pas ajouter un nouvel host, vous pouvez ajouter un Listen pour servir le site sur un autre port, ce qui vous permettra d'utiliser le domaine localhost par ailleurs.
Test de la nouvelle configuration
Redémarrez Apache, et vérifiez que vous avez à présent accès à la nouvelle application:
http://askeet/

symfony peut utiliser le module mod_rewrite d'Apache pour enlever la mention /index.php/ des url. Si vous ne souhaitez pas l'utiliser ou si votre serveur web ne supporte pas cette fonction, vous pouvez retirer le fichier .htaccess du répertoire web/. Si votre version d'Apache n'est pas compilée avec le mod_rewrite, vérifiez que vous avez la DSO mod_rewrite installée et les lignes suivantes dans votre httpd.conf:
AddModule mod_rewrite.c
LoadModule rewrite_module modules/mod_rewrite.so
Vous pouvez également essayer d'accéder à l'application dans l'environnement de développement. Saisissez l'url suivante :
http://askeet/frontend_dev.php/
La boîte à outils de web debug devrait apparaître dans le coin supérieur droit; si elle contient bien des icônes, votre configuration du Alias sf/ est correcte.

Le projet dévoilé
Que voulez-vous savoir ? C’est une question intéressante. Il y a beaucoup de questions intéressantes comme :
- Que dois-je faire avec ma copine ce soir ?
- Comment générer du trafic sur mon blog ?
- Quel est le meilleur Framework web ?
- Quel est le restaurant le plus abordable de Paris ?
- Qu’en est-il de la vie, de l’univers et du reste ?
Toutes ces questions n’ont pas de réponse unique, et la meilleure n’est qu’une question d’opinion. En fait, les questions qui ont qu’une seule réponse sont souvent les moins intéressantes (comme, combien fait 1+1 ?) mais ce sont les seules résolues sur le net. Ce n’est pas juste !
Découvrez askeet. Un site web dont le but est d’aider les personnes à répondre à leurs questions. Qui veut répondre à ces questions délicates ? Tout le monde. Et tout le monde devrait avoir la possibilité d’évaluer les réponses des autres, ce qui permettrait aux meilleures réponses d’être le plus visible. À mesure que le nombre de questions augmente, il devient impossible de les organiser en catégories et sous catégories, donc le créateur de la question aura la possibilité de les tagger avec les mots de son choix, à la del.icio.us. Bien sur, la popularité des tags doit être prise en compte. Si une personne souhaite suivre les réponses d’une question en particulier, elle pourra s’inscrire au fil RSS de celle-ci. Toutes ces fonctionnalités doivent être claires et légères, donc toutes les interactions qui n’ont pas besoin de nouvelles pages doivent être en AJAX. Par la suite, un back-end est nécessaire pour administrer les questions et réponses reportées comme spam, ou pour encourager une question.
Maintenant vous pourriez poser la question suivante : n’ais-je pas déjà vu un pareil site sur le web ? Bon, si c’est le cas nous sommes grillés mais si vous faites référence à faqts, eHow, Ask Jeeves ou quelque chose de similaire, sans réponses collectives, sans AJAX, sans fil RSS et sans tags ce n’est pas le même site. Ici nous parlons d’une application web 2.0.
Le challenge d’askeet c’est que ce n’est pas qu’un site web, mais également une application téléchargeable que l’on peut installer chez soi ou sur un intranet professionnel et d’y ajouter des fonctionnalités. Le code source va être publié en licence open-source. Vous voulez garder une trace de toutes les astuces que vous avez apprises lorsque vous avez démonté votre voiture ? Vous ne voulez pas développer de FAQ pour votre site web ? Ne cherchez plus, car askeet existe. Enfin, il va exister c’est notre cadeau de noël.
Par où commencer ?
Alors, par où êtes-vous censé commencer une application askeet ? Ca dépend de vous. Vous pouvez écrire des histoires, faire un jeu de planification et trouver un partenaire pour programmer à deux si vous êtes un adepte d’XP, ou rédiger une spécification détaillée de votre site web, accompagnée d’un croquis de tous les objets, états, interactions et plus encore si vous étiez un fan d’UML.
Mais ce tutorial ne traite pas du développement en général, donc nous allons commencer avec un modèle de données relationnel basique, et ajouter les fonctionnalités une à une. Ce dont nous avons besoin c’est une application fonctionnelle à la fin de chaque jour, pas un gigantesque et continuel fouillis de code qui ne fais jamais rien. Dans un monde idéal, nous devrions écrire des jeux de tests pour chaque fonctionnalité que nous ajoutons, mais honnêtement nous n’avons pas le temps pour ca. Cependant un jour sera dédié aux jeux de tests, donc continuez à lire.
Pour ce projet, nous allons utiliser une base MySQL avec le type de table InnoDB pour profiter des contraintes d’intégrités et le support des transactions. Nous aurions pu utiliser une base SQLite pour les premières étapes, pour éviter de mettre en place une vraie base de données. Cela aurai demandé que quelques changements dans le fichier database.yml, nous vous laisserons vous y intéresser en tant qu’exercice.
Modèle de données
Modèle relationnel
De toute évidence, il y aura les tables 'question' et 'answer'. Nous avons besoin d’une table ‘user’, et nous stockerons les intérêts des utilisateurs pour une question dans une table ‘interest’, et l’évaluation, par un utilisateur, d’une réponse dans une table ‘relevancy’.
Les utilisateurs devront s’identifier pour ajouter une question, pour évaluer la pertinence d’une réponse, ou pour se déclarer intéressé par une question. Pour répondre ils ne seront pas obligés de s’identifier, mais une réponse devra toujours être reliée à un utilisateur pour que ceux avec des réponses populaires puissent être distingués. Les réponses données sans identification seront visibles en tant que contribution à un utilisateur générique, appelé ‘Anonymous Coward’. Il est plus facile de comprendre cela avec un diagramme de relation entre les entités.

Notez que nous avons déclaré un champ created_at pour chaque table. Symfony reconnaît de tels champs et initialise la valeur à l’heure actuelle du système quand un enregistrement est créé. C’est la même chose pour le champ updated_at : leurs valeurs est initialisée à l’heure système quand l’enregistrement est mis à jour.
schema.xml
Le modèle relationnel doit être transcrit en un fichier de configuration pour que Symfony le comprenne. C’est l’objectif du fichier schema.xml ou du fichier schema.yml, situé dans le répertoire askeet/config/. Symfony supporte le schéma au format XML et YAML.
Il y a deux moyens d’écrire ce fichier : à la main, et c’est la façon que nous préférons, ou à partir d’une base existante. Regardons la première solution.
Tout d’abord nous avons besoin de supprimer le fichier YAML installé par défaut :
$ svn delete config/schema.yml
La syntaxe du fichier schema.xml, expliquée en détails sur le site Propel website, est relativement simple : c’est un fichier XML, dont chaque balise <table> contient une balise <column>, <foreign-key> et <index>. Une fois que vous savez en écrire une, vous savez toutes les écrire. Ceci est le fichier schema.xml correspondant au modèle relationnel décrit précédemment:
<?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noxsd="true">
<table name="ask_question" phpName="Question">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="title" type="longvarchar" />
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
<column name="updated_at" type="timestamp" />
</table>
<table name="ask_answer" phpName="Answer">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="question_id" type="integer" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
</table>
<table name="ask_user" phpName="User">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="nickname" type="varchar" size="50" />
<column name="first_name" type="varchar" size="100" />
<column name="last_name" type="varchar" size="100" />
<column name="created_at" type="timestamp" />
</table>
<table name="ask_interest" phpName="Interest">
<column name="question_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="created_at" type="timestamp" />
</table>
<table name="ask_relevancy" phpName="Relevancy">
<column name="answer_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_answer">
<reference local="answer_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="score" type="integer" />
<column name="created_at" type="timestamp" />
</table>
</database>
Notez que le nom de la base de données est propel dans ce fichier, quelque soit le nom de la base de données réelle. C’est un paramètre utilisé pour connecter la couche Propel au framework Symfony. Le nom actuel de la base sera défini dans le fichier de configuration databases.yml (voir plus bas).
Il y a un autre moyen de créer un fichier schema.xml si vous avez une base de données existante. Ainsi, si vous êtes familiarisé avec un outil de création de base de données graphique, vous préférerez construire votre schéma depuis votre base MySQL générée. Avant cela, vous devez juste éditer le fichier propel.ini situé dans le répertoire askeet/config/ et saisir les paramètres de connexion à votre base de données :
propel.database.url = mysql://username:password@localhost/databasename
… où username, password, localhost et databasename sont vos paramètres de connexion actuels. Vous pouvez maintenant exécuter la commande propel-build-schema (depuis le répertoire askeet/) pour générer le schema.xml de votre base de données :
$ symfony propel-build-schema
Note: certains outils vous permettent de construire votre base de données graphiquement (par exemple Fabforce's Dbdesigner) et de générer le fichier schema.xml directement (avec DB Designer 4 TO Propel Schema Converter).
Plutôt que de créer un fichier schema.xml, vous pouvez aussi créer un fichier schema.yml utilisant le format YAML:
propel:
_attributes: { noXsd: false, defaultIdMethod: none, package: lib.model }
ask_question:
_attributes: { phpName: Question, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
title: { type: longvarchar }
body: { type: longvarchar }
created_at: ~
updated_at: ~
ask_answer:
_attributes: { phpName: Answer, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
question_id: { type: integer, foreignTable: ask_question, foreignReference: id }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
body: { type: longvarchar }
created_at: ~
ask_user:
_attributes: { phpName: User, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
nickname: { type: varchar(50), required: true, index: true }
first_name: varchar(100)
last_name: varchar(100)
created_at: ~
ask_interest:
_attributes: { phpName: Interest, idMethod: native }
question_id: { type: integer, foreignTable: ask_question, foreignReference: id, primaryKey: true }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id, primaryKey: true }
created_at: ~
ask_relevancy:
_attributes: { phpName: Relevancy, idMethod: native }
answer_id: { type: integer, foreignTable: ask_answer, foreignReference: id, primaryKey: true }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id, primaryKey: true }
score: { type: integer }
created_at: ~
Construction du modèle objet
Pour utiliser le moteur InnoDB, une ligne doit être ajoutée au fichier propel.ini situé dans le répertoire askeet/config/ :
propel.mysql.tableType = InnoDB
Une fois le fichier schema.xml construit, vous pouvez générer un modèle objet basé sur le modèle relationnel. Dans Symfony, l'organisation relationnelle des objets est prise en charge par Propel, mais encapsulée dans la commande Symfony:
$ symfony propel-build-model
Cette commande (vous devez l’exécuter depuis le répertoire racine du projet askeet) va générer les classes correspondantes aux tables définies dans le schéma, ainsi que les accesseurs standards (fonctions ->get() et ->set()). Vous pouvez regarder le code généré dans le répertoire askeet/lib/model/om/. Si vous vous demandez pourquoi y a deux classes par table, référez vous au chapitre sur le modèle du livre Symfony. Ces classes seront réécrites à chaque fois que vous ferez la commande build-model, et cela arrivera souvent dans ce projet. Ainsi si vous avez besoin d'ajouter des méthodes aux objets du modèle, vous devrez modifier ceux situés dans le répertoire askeet/lib/model/ - ces classes héritent de celles de /om.
La base de données
Connexion
Maintenant que Symfony possède un modèle objet de la base de données, il est temps de connecter votre projet à la base de données. Premièrement, vous devez créer une base dans MySQL :
$ mysqladmin -u youruser -p create askeet
Vous pouvez également réaliser cette opération via phpMyAdmin.
Maintenant ouvrez le fichier de configuration askeet/config/databases.yml.
all:
propel:
class: sfPropelDatabase
param:
phptype: mysql
host: localhost
database: askeet
username: youruser
password: yourpasswd
Construction
Si vous n’avez pas écris le fichier schema.xml à la main vous avez surement les tables correspondante dans votre base de données. Vous pouvez donc ignorer ce qui suit.
Pour les fans du clavier, voici une surprise : vous n’avez pas besoin de créer les tables et les colonnes dans MySQL. Vous l’avez déjà fait dans le schema.xml, donc symfony va construire pour vous les commandes SQL :
$ symfony propel-build-sql
Cette commande crée un lib.model.schema.sql dans le répertoire askeet/data/sql/. Utilisez le comme commande SQL dans MySQL:
$ mysql -u youruser -p askeet < data/sql/lib.model.schema.sql
De manière alternative, vous pouvez aussi utiliser l'instruction propel-insert-sql:
$ symfony propel-insert-sql
N.B. la commande suivante regroupe les actionsde création des classes, génération et exécution du SQL$ symfony propel-build-all
Test de l’accès aux données via CRUD
Il est toujours agréable de voir que le travail effectué est utile. Jusqu'à maintenant, votre navigateur n'était d'aucune utilité, et pourtant nous sommes supposés construire une application web ... Créons donc un ensemble de modèles et d'actions Symfony pour manipuler les données de la table 'question'. Cela nous permettra de créer quelques questions et de les afficher.
Dans le répertoire askeet/, tapez:
$ symfony propel-generate-crud frontend question Question
Cela génère une ébauche pour un module question dans l'application frontend, basé sur le modèle objet Propel Question, avec les actions basiques Create Retrieve (ou Read) Update Delete (Ce qui explique l'acronyme CRUD). Ne vous y trompez pas: Une ébauche n'est pas une application finie, mais la structure basique sur laquelle vous pouvez développer de nouveaux éléments, ajouter des règles de gestion et customiser l’apparence.
La liste de toutes les actions créées par un générateur CRUD est:
| Nom Action | Description |
| list | Affiche tous les enregistrements d'une table |
| index | Retourne à la liste |
| show | Affiche tous les enregistrements d'un enregistrement donné |
| edit | Affiche un formulaire pour créer un nouvel nregistrement ou pour éditer un enregistrement existant |
| update | Modifie un enregistrement selon les paramètres indiqués dans la requête, puis retourne à l'affichage |
| delete | Supprime un enregistrement donné de la table |
Dans le répertoire askeet/apps/frontend/modules/, notez le nouveau module question et consultez sa source.
Quelque soit le moment ou vous ajoutez une classe qui doit être chargée automatiquement, n'oubliez pas de vider le cache (pour recharger le cache auto-chargé):
$ symfony cc frontend config
Vous pouvez maintenant le tester en ligne en consultant la page:
http://askeet/question

Allez-y, jouez avec. Ajoutez quelques questions, éditez les, listez les, supprimez les. Si cela fonctionne, c'est que le modèle objet est correct, que la connexion à la base de données est correcte, et que l'agencement entre le modèle relationnel de la base de données et le modèle objet de Symfony sont corrects. C'est un bon test fonctionnel.





