Development

Documentation/fr_FR/askeet: 3.txt

You must first sign up to be able to contribute.

Documentation/fr_FR/askeet: 3.txt

File 3.txt, 23.5 kB (added by nicocsgamer, 11 years ago)

Calendrier de l’avent troisième jour: plongez dans l’architecture MVC

Line 
1 Calendrier de l’avent troisième jour: plongez dans l’architecture MVC
2 ====================================================================
3
4 Précédemment dans Symfony
5 -------------------------
6
7 Au cours du [deuxième jour] (2.txt) vous avez apprit à construire  un modèle objet basé sur un modèle de données relationnel, et générer une ébauche pour chacun de ces objets. De plus, l’application générée durant le jour précédent est disponible dans l’espace de stockage SVN d’askeet:
8
9     http://svn.askeet.com/
10
11 Les objectifs du troisième jour sont de définir une mise en page plus esthétique, définir la liste des questions de la page d’accueil, afficher le nombre d’intéressé par question, et de remplir la base de données en utilisant des extraits de fichiers textes afin de disposer de données de test. Ce n’est pas grand chose à faire, mais un peu plus à lire et à comprendre.
12
13 Pour lire ce tutorial, vous devriez être familiarisé avec les concepts de projet, d’application, module et d’action dans Symfony comme expliqué dans le [chapitre sur le contrôleur](http://www.symfony-project.com/content/book/page/controller.html) du livre Symfony.
14
15 Le modèle MVC
16 --------------
17
18 Aujourd’hui sera le premier plongeon dans le monde de [l’architecture MVC] [1]. Qu’est ce que c’est ? Simplement que le code utilisé pour générer une page est situé dans différents fichiers en fonction de leur nature.
19
20 Si le code concerne la manipulation des données indépendante d’une page, il devrait être situé dans le **Modèle** (la plupart du temps dans `askeet/lib/model/`). S’il concerne la présentation finale, il devrait être situé dans la **Vue** ; dans Symfony, la couche Vue se base sur les templates (par exemple dans `askeet/apps/frontend/modules/question/templates/`) et les fichiers de configuration. Par la suite, le code dédié à lier tout cela et à traduire la logique du site en bon vieux PHP est situé dans le Contrôleur,  et dans Symfony le contrôleur pour une page spécifique est appelé une action (recherchez les actions dans `askeet/apps/frontend/modules/question/actions/`). Vous pouvez en lire plus à propos de ce modèle dans le chapitre sur [l’implémentation de MVC dans Symfony](http://www.symfony-project.com/content/book/page/mvc.html) du livre Symfony.
21 Alors que la vue de notre application changera peu aujourd’hui, nous allons manipuler beaucoup de fichiers. Cependant ne paniquez pas, puisque l’organisation des fichiers et la séparation du code en différentes couches va bientôt devenir évidente et très utile.
22
23 Changer la mise en page
24 -------------------------
25
26 Dans l’application du [décorateur][2], le contenu du template appelé par une action est intégré dans un template global, ou une mise en page. En d’autres termes, la mise en page contient toutes les parties invariantes de l’interface, elle « décore » le résultat de l’action. Ouvrez la mise en page par défaut (situé dans  `askeet/apps/frontend/templates/layout.php`) et changez-la comme suit :
27
28     [php]
29     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
30     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
31     <head>
32    
33     <?php echo include_http_metas() ?>
34     <?php echo include_metas() ?>
35    
36     <?php echo include_title() ?>
37    
38     <link rel="shortcut icon" href="/favicon.ico" />
39    
40     </head>
41     <body>
42    
43       <div id="header">
44         <ul>
45           <li><?php echo link_to('about', '@homepage') ?></li>
46         </ul>
47         <h1><?php echo link_to(image_tag('askeet_logo.gif', 'alt=askeet'), '@homepage') ?></h1>
48       </div>
49    
50       <div id="content">
51         <div id="content_main">
52           <?php echo $sf_data->getRaw('sf_content') ?>
53           <div class="verticalalign"></div>
54         </div>
55    
56         <div id="content_bar">
57           <!-- Nothing for the moment -->
58           <div class="verticalalign"></div>
59         </div>
60       </div>
61    
62     </body>
63     </html>
64
65 >**Note**: nous avons essayé de garder les balises aussi sémantique que possible, et de déplacer tout le style dans les feuilles de styles CSS. Ces feuilles de styles ne seront pas décrites ici, puisque la syntaxe CSS n’est pas le but de ce tutorial. Cependant elles sont disponibles en téléchargement, dans [l’espace de stockage SVN](http://svn.askeet.com/tags/release_day_3/web/css/).
66 >
67 >Nous avons créé deux feuilles de styles (`main.css` et `layout.css`). >Copiez les dans le répertoire `askeet/web/css/` et éditez votre >`frontend/config/view.yml` pour changer l’auto chargement des feuilles >de styles :
68 >
69 >   stylesheets:    [main, layout]
70 >
71
72 Cette mise en page un peu légère pour le moment, sera refaite plus tard (dans une semaine environ). Les choses importantes dans ce template sont la partie `<head>`, qui est la plupart du temps générée, et la variable `sf_content`, qui contient le résultat des actions.
73
74 Vérifiez que les modifications s’affichent correctement en demandant la page d’accueil – cette fois dans l’environnement de développement :
75
76     http://askeet/frontend_dev.php/
77    
78 ![updated layout](/images/askeet/congratulations_new.gif)
79    
80 Quelques mots sur les environnements
81 ---------------------------------------
82
83 Si vous vous demandez quelle est la différence entre `http://askeet/frontend_dev.php/` et `http://askeet/`, vous devriez jeter un œil sur [le chapitre sur la configuration](http://www.symfony-project.com/content/book/page/configuration.html) du livre Symfony. Pour le moment, vous devez juste savoir qu’elles pointent sur la même application, mais dans des environnements différents. Un environnement est une configuration unique, où quelques fonctionnalités du framework peuvent êtres activées ou désactivées selon le besoin.
84
85 Dans ce cas, l’url `/frontend_dev.php/` pointe sur **l’environnement de développement**, où toute la configuration est analysée à chaque requête, le cache HTML est désactivé, et les outils de débogage sont tous disponibles (y compris une barre d’outils semi-transparente située dans le coin supérieur de la fenêtre). L’url `/` - équivalent à `/index/` - pointe sur **l’environnement de production**, où la configuration est « compilée » et les outils de débogage sont désactivés pour accélérer la vitesse d’accès aux pages.
86
87 Ces deux scripts PHP - `frontend_dev.php` et `index.php` – sont appelés **front controllers**, et toutes les requêtes de l’application sont manipulées par eux. Vous pouvez les trouver dans le répertoire `askeet/web/`. En fait, le fichier `index.php` devrait s’appeler `frontend_prod.php`, mais comme le `frontend` est la première application que vous créez, Symfony déduit que vous voudriez probablement que ce soit votre application par défaut et le renomme en index.php, c’est donc pour cela que vous pouvez voir votre application en environnement de production juste en demandant `/`. Si vous voulez en apprendre plus à propos du front controller et de la couche contrôleur du modèle MVC en général, référez vous au [chapitre sur le contrôleur] (http://www.symfony-project.com/content/book/page/controller.html) dans le livre Symfony.
88 Un bon principe de base est de naviguer dans l’environnement de développement jusqu'à ce que vous soyez satisfait des fonctionnalités sur lesquelles vous travaillez et de passer à l’environnement de production vous vérifiez la vitesse et la beauté des urls.
89
90 >**Note**: rappelez vous de toujours effacer le cache quand vous ajouter des classes ou quand vous changez des fichiers de configurations pour voir le résultat en environnement de production.
91
92
93
94 Redéfinir la page d’accueil
95 -----------------------------
96 Pour le moment, si vous demandez la page d’accueil du nouveau site, il affiche une page de ‘félicitations’. Une meilleur idée serai d’afficher la liste des questions (référencée dans ces documents comme `question/list` et traduit comme : l’action `list` du module `question`). Pour faire cela, ouvrez le fichier de configuration de routage de l’application frontend, situé dans `askeet/apps/frontend/config/routing.yml` et trouvez la section `homepage : `. Changez-la-en :
97
98     homepage:
99       url:   /
100       param: { module: question, action: list }
101
102 Rafraichissez la page d’accueil de l’environnement de développement (`http://askeet/frontend_dev.php/`); elle affiche maintenant la liste des questions.
103
104 >**Note**: si vous êtes une personne curieuse, vous avez peut être cherchez la page contenant le message de ‘félicitation’. Et vous avez surement été surpris de ne pas la trouver dans votre répertoire `askeet`. En fait, le template pour l’action `defaut/index` est situé dans le répertoire data de Symfony et est indépendant du projet. Si vous ne voulez pas en tenir compte, vous pouvez toujours créer un module `défault` dans votre propre projet.
105
106 Les possibilités offertes par le système de routage seront détaillées prochainement, mais si cela vous intéressent, vous pouvez lire le [chapitre sur le routage](http://www.symfony-project.com/content/book/page/routing.html) su livre Symfony.
107
108 Définir des données de test
109 ---------------------------
110
111 La liste affichée par la page d’accueil restera vide, à moins que vous ajoutiez vos propres questions. Quand vous développez une application, c’est une bonne idée d’avoir quelques données de test à votre disposition. Entrer des données de test à la main (ou via l’interface CRUD) peut être une vraie douleur, c’est pourquoi Symfony peut utiliser les fichiers textes pour remplir les bases de données.
112
113 Nous allons créer un fichier de données de test dans le répertoire `askeet/data/fixtures/` (ce répertoires doit être créé). Créez un fichier nommé `test_data.yml` avec le contenu suivant :
114
115     User:
116       anonymous:
117         nickname:   anonymous
118         first_name: Anonymous
119         last_name:  Coward
120
121       fabien:
122         nickname:   fabpot
123         first_name: Fabien
124         last_name:  Potencier
125    
126       francois:
127         nickname:   francoisz
128         first_name: François
129         last_name:  Zaninotto
130    
131     Question:
132       q1:
133         title: What shall I do tonight with my girlfriend?
134         user_id: fabien
135         body:  |
136           We shall meet in front of the Dunkin'Donuts before dinner,
137           and I haven't the slightest idea of what I can do with her.
138           She's not interested in programming, space opera movies nor insects.
139           She's kinda cute, so I really need to find something
140           that will keep her to my side for another evening.
141    
142       q2:
143         title: What can I offer to my step mother?
144         user_id: anonymous
145         body:  |
146           My stepmother has everything a stepmother is usually offered
147           (watch, vacuum cleaner, earrings, del.icio.us account).
148           Her birthday comes next week, I am broke, and I know that
149           if I don't offer her something sweet, my girlfriend
150           won't look at me in the eyes for another month.
151    
152       q3:
153         title: How can I generate traffic to my blog?
154         user_id: francois
155         body:  |
156           I have a very swell blog that talks
157           about my class and mates and pets and favorite movies.
158          
159     Interest:
160       i1: { user_id: fabien, question_id: q1 }
161       i2: { user_id: francois, question_id: q1 }
162       i3: { user_id: francois, question_id: q2 }
163       i4: { user_id: fabien, question_id: q2 }
164
165 Avant tout, ici vous pouvez reconnaitre [YAML] [3]. Si vous n’êtes pas familiarisé avec Symfony, vous pourriez ne pas savoir que le format YAML est le format favori des fichiers de configurations de ce framework. Il n’est pas exclusif – si vous êtes attachés aux fichiers XML ou .ini, il est très facile d’ajouter un gestionnaire de configuration pour permettre à Symfony de les lire. Si vous avez le temps et la patience, vous pouvez en lire plus à propos de YAML et des fichiers de configuration dans le [chapitre de la configuration en pratique](http://www.symfony-project.com/content/book/page/configuration_practice.html) du livre Symfony. A partir de maintenant, si vous n’êtes pas familiarisé avec la syntaxe YAML, vous devriez [commencer tout de suite] [3], puisque ce tutorial l’emploiera intensivement.
166
167 Ok, revenons au fichier de données de test. Il défini des instances d’objets, intitulés avec un nom interne. L’intitulé est d’une bonne utilité pour relier les objets relatifs sans avoir à définir les `id` (qui sont souvent auto incrémentés et qui ne peuvent pas être initialisés). Par exemple, le premier objet créé d’une classe `User`, est intitulé `fabien`. La première `question` est intitulée `q1`. Ceci rend facile de créer un objet de la classe `Interest` en mentionnant l’intitulé des objets relatifs :
168
169     Interest:
170       i1:
171         user_id: fabien
172         question_id: q1
173
174 Le fichier de données donné précédemment utilise la syntaxe courte de YAML pour dire la même chose. Vous pouvez en savoir plus à propos des fichiers de population de données dans le [chapitre sur les fichiers de données](http://www.symfony-project.com/content/book/page/populate.html) dans le livre Symfony.
175
176 >**Note**: ce n’est pas nécessaire de définir les valeurs pour les colonnes `created_at` and `updated_at` puisque Symfony sait comment les remplir par défaut.
177
178 Créer un batch pour peupler la base de données
179 ----------------------------------------------
180
181 La prochaine étape est réellement de peupler la base de données, et nous espérons le faire avec un script PHP qui peut être appelé avec la console – un batch.
182
183 ### Squelette du batch
184
185 Créez un fichier appelé `load_data.php` dans le répertoire `askeet/batch/` avec le contenu suivant :
186
187     [php]
188     <?php
189    
190     define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
191     define('SF_APP',         'frontend');
192     define('SF_ENVIRONMENT', 'dev');
193     define('SF_DEBUG',       true);
194    
195     require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
196    
197     // initialize database manager
198     $databaseManager = new sfDatabaseManager();
199     $databaseManager->initialize();
200    
201     ?>
202
203 Le script ne fait rien, ou presque rien : il définit un chemin, une application et un environnement pour aboutir à une configuration, la charge, et initialise le manager de base de données. Mais c’est déjà beaucoup : cela signifie que tout le code écrit ci-dessous profitera de l’auto chargement des classes, la connexion automatique aux objets Propel, et des classes racines de Symfony.
204
205 >**Note**: si vous avez regardez les front controllers de Symfony (comme `askeet/web/index.php`), vous pourriez trouver ce code extrêmement familier. C’est parce que toutes les requêtes web nécessites l’accès aux mêmes objets et configuration, que le batch.
206
207 ### Importation des données
208
209 Maintenant que la structure du batch est prête, il est temps d’en faire quelque chose. Le batch doit:
210
211 1. lire le fichier YAML
212 2. Créer des instances d’objets Propel
213 2. Créer les enregistrements relatifs dans les tables de la base de données concernée
214
215 Ceci pourrait sembler compliqué, mais avec Symfony, vous pouvez le faire avec deux lignes de code, grâce à l’objet `sfPropelData`. Ajoutez juste le code suivant avant le dernier `?> ` dans le script `askeet/batch/load_data.php` :
216
217     [php]
218     $data = new sfPropelData();
219     $data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');
220
221 C’est tout. Un objet `sfPropelData` est créé, et dit de charger toutes les données d’un dossier spécifique - notre répertoire `fixtures` – dans la base de données définie dans le fichier de configuration `database.yml`.
222
223 >**Note**: la constante `DIRECTORY_SEPARATOR` est ici utilisée pour être compatible avec les plateformes Windows et *nic.
224
225 ### Exécuter le batch
226
227 Enfin, vous pouvez vérifiez si ces quelques lignes de code en valaient la peine. Tapez dans la console:
228
229     $ cd /home/sfprojects/askeet/batch
230     $ php load_data.php
231    
232 Vérifiez les modifications en rechargeant la page d'accueil de développement :
233
234     http://askeet/frontend_dev.php
235    
236 ![loaded data](/images/askeet/fixtures.gif)
237
238 Hourrah, les données sont là.
239
240 >**Note**: Par défaut, l’objet `sfPropelData` supprime toutes les données >avant de charger les nouvelles. Vous pouvez les ajouter aux données >actuelles :
241 >
242 >     [php]
243 >     $data = new sfPropelData();
244 >     $data->setDeleteCurrentData(false);
245 >     $data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');
246
247 Accès aux données dans le modèle
248 --------------------------------
249
250 La page affichée en demandant l’action `list` du module question est le résultat de la fonction `executeList()` (située dans le fichier action `askeet/apps/frontend/modules/question/actions/action.class.php`) passée au template `askeet/apps/frontend/modules/question/templates/listSuccess.php`. Ceci est basé sur la convention de nommage qui est expliquée dans le [chapitre sur le controller](http://www.symfony-project.com/content/book/page/controller.html) dans le livre Symfony. Jetons un œil au code qui est exécuté:
251    
252 actions.class.php:
253
254     [php]
255     public function executeList ()
256     {
257       $this->questions = QuestionPeer::doSelect(new Criteria());
258     }
259
260 listSuccess.php:
261
262     [php]
263     ...
264     <?php foreach ($questions as $question): ?>
265     <tr>
266         <td><?php echo link_to($question->getId(), 'question/show?id='.$question->getId()) ?></td>
267         <td><?php echo $question->getTitle() ?></td>
268         <td><?php echo $question->getBody() ?></td>
269         <td><?php echo $question->getCreatedAt() ?></td>
270         <td><?php echo $question->getUpdatedAt() ?></td>
271       </tr>
272     <?php endforeach; ?>
273
274 Etape par étape, voici ce qui est fait :
275
276 1. L’action demande les enregistrements  de la table `Question` qui satisfont un critère vide - i.e. toutes les questions
277 2. Cette liste d’enregistrements est mise dans un tableau (`$question`) qui est donné au template
278 3. Le template passe en revue toutes les questions données par l’action
279 4. Le template affiche la valeur des colonnes de chaque enregistrement
280
281 Les fonctions `->getId()`,`->getTitle()`,`->getBody()`, etc. ont été créés lors de l’appel de la commande `symfony propel-build-model` (vous vous rappelez [d’hier](2.text) ?) pour rechercher la valeur des champs `id`, `title`, `body`, etc. Ce sont les accesseurs standards, formé en ajoutant le préfix `get` et le nom du champ en casse de chameau – et Propel fournit aussi des mutateurs standards, préfixés de set. La [documentation de Propel][4] décrit les accesseurs créés pour chaque classe.
282 Quand à l’appel mystérieux `QuestionPeer::doSelect(new Criteria())`, c’est aussi une requête standard Propel. La documentation Propel l’expliquera complètement.
283 Ne vous inquiétez pas si vous ne comprenez pas tout le code écrit ci-dessus, il deviendra clair dans quelques jours.
284
285
286 Modifier le template question/list
287 ----------------------------------
288
289 Maintenant que la base de données contient les intéressés pour les questions, il sera facile de récupérer le nombre d’utilisateurs intéressés par une question. Si vous jetez un œil à la classe BaseQuestion.php générée par Propel dans le répertoire `askeet/lib/model/om/`,  vous noterez la fonction `->getInterests()`. Propel a vu la clé étrangère `question_id` dans la définition de la table Interest, et déduit qu'une question a plusieurs intéressés. Ceci fait, il est très facile d’afficher ce que nous voulons en modifiant le template `listSuccess.php`, situé dans `askeet/apps/frontend/modules/question/templates/`. Par la suite, nous remplacerons les horribles table par des jolis div : 
290
291     [php]
292     <?php use_helper('Text') ?>
293    
294     <h1>popular questions</h1>
295    
296     <?php foreach($questions as $question): ?>
297       <div class="question">
298         <div class="interested_block">
299           <div class="interested_mark" id="interested_in_<?php echo $question->getId() ?>">
300             <?php echo count($question->getInterests()) ?>
301           </div>
302         </div>
303      
304         <h2><?php echo link_to($question->getTitle(), 'question/show?id='.$question->getId()) ?></h2>
305      
306         <div class="question_body">
307           <?php echo truncate_text($question->getBody(), 200) ?>
308         </div>
309       </div>
310     <?php endforeach; ?>
311
312 Ici vous reconnaissez la même boucle `foreach` que dans le fichier original `listSuccess.php`. Les fonctions `link_to()` et `truncate_text()` sont des **assistants aux templates** fournit par Symfony. La première crée un lien hypertexte d’une autre action du même module, et la seconde coupe le corps de la question à 200 caractères. L’assistant `link_to()` est chargé automatiquement, mais vous devez déclarer l’utilisation le groupe `Text` des assistants pour utiliser `truncate_text()`.
313
314 Allez, essayer notre nouveau template en rafraichissant la page d’accueil de développement.
315
316     http://askeet/frontend_dev.php/
317    
318 ![better question list](/images/askeet/question_list_day3.gif)
319
320 Le nombre d’utilisateurs intéressés apparait correctement à coté de chaque question. Pour obtenir la présentation de la capture ci dessus, téléchargez la feuille de styles [`main.css`](http://svn.askeet.com/tags/release_day_3/web/css/main.css) et mettez la dans votre répertoire `askeet/web/css/`.
321
322 Nettoyage
323 ---------
324
325 La commande `propel-generate-crud` à crée quelques actions et templates qui ne seront pas nécessaires. Il est temps de les enlever.
326
327 Les actions à supprimer dans `askeet/apps/frontend/modules/question/actions/actions.class.php`:
328
329 * `executeIndex`
330 * `executeEdit`
331 * `executeUpdate`
332 * `executeCreate`
333 * `executeDelete`
334
335 Les templates à supprimer dans `askeet/apps/frontend/modules/question/templates/` :
336
337 * `editSuccess.php`
338
339 A demain
340 --------
341
342 Aujourd’hui était une première grande étape dans le monde du paradigme Model-View-Controller : en manipulant des layout, des templates, des actions et des objets du modèle objet Propel, vous avez accédé à toutes les couches d’une application structurée par MVC. Ne vous inquiétez pas si vous n’avez pas compris tous les liens entre ces couches : cela deviendra plus clair petit à petit.
343
344 Beaucoup de fichiers ont été ouverts aujourd’hui, et si vous voulez savoir comment les fichiers sont organisés dans un projet, référez vous au [chapitre sur la structure de fichier](http://www.symfony-project.com/content/book/page/file_structure.html) du livre Symfony.
345
346 Demain sera un autre jour splendide : nous modifierons les vues, installerons une police de routage plus complexe, modifierons le modèle, et plongerons en profondeur dans la manipulation des données et les liens entre les tables.
347
348 Jusque-là, dormez bien, et sentez vous libre de consulter la source du tutorial d’aujourd’hui (tag `release_day_3`) à:
349
350     http://svn.askeet.com/tags/release_day_3
351
352 [1]: http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur "Model-View-Controller definition at Wikipedia"
353 [2]: http://fr.wikipedia.org/wiki/D%C3%A9corateur_%28motif_de_conception%29     "Decorator pattern definition at Wikipedia"
354 [3]: http://www.yaml.org/                               "YAML"
355 [4]: http://propel.phpdb.org/docs/user_guide/           "Propel documentation"