PageRenderTime 68ms CodeModel.GetById 10ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/jobeet/fr/04.markdown

https://github.com/rafaelgou/symfony1-docs
Markdown | 878 lines | 718 code | 160 blank | 0 comment | 0 complexity | 2a588922c6b94de822f6827351f630bc MD5 | raw file
  1Jour 4 : Le contrôleur et la vue
  2================================
  3
  4Hier, nous avons appris comment symfony nous simplifie la gestion des bases de données
  5en faisant abstraction des différences entre les moteurs de base de données et en convertissant les
  6éléments relationnels sous forme de classes orientées objets. Nous avons également vu le principe
  7de fonctionnement de ##ORM## permettant de définir le schéma de la base de données, créer les tables et
  8remplir la base de données avec quelques valeurs initiales.
  9
 10Aujourd'hui, nous allons personnaliser le module `job` que nous avons créé
 11hier. Actuellement, ce module a déjà tout le code utile pour Jobeet :
 12
 13 * Une page listant tous les jobs
 14 * Une page pour créer un nouveau job
 15 * Une page pour mettre à jour une job déjà existant
 16 * Une page pour supprimer un job
 17
 18Bien que le code est prêt à être utilisé tel qu'il est, nous devons modifier les
 19Templates afin que nos pages correspondent à notre maquette. 
 20
 21L'architecture ~MVC~
 22--------------------
 23
 24Si vous avez l'habitude de développer des sites web en PHP sans framework, vous avez
 25probablement utilisé le principe d'un fichier PHP par page HTML. Ces fichiers PHP ayant très
 26certainement une structure proche de : l'initialisation et la configuration globale, le traitement
 27associé à la page demandée, la récupération des données depuis la base de données et enfin
 28la mise en place du code HTML formant la page.
 29
 30Vous pouvez également utiliser un moteur de Template permettant de séparer la logique du HTML.
 31Vous utilisez peut-être une couche d'abstraction permettant de séparer l'interaction du modèle
 32avec celui du traitement des données. Mais la plupart du temps, vous vous retrouvez avec beaucoup de code
 33absolument cauchemardesque à maintenir. Le code a rapidement été mis en place, mais la plupart du temps, ce
 34dernier est de plus en plus difficile à modifier, notamment parce que personne, excepté vous,
 35ne sait comment votre site a été conçu et comment il fonctionne.
 36
 37Comme toujours : à chaque problème, ses solutions. Pour le développement Web, les
 38solutions les plus populaires pour organiser votre code de nos jours est la mise en place
 39d'une [**architecture MVC**](http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur).
 40En résumé, l'architecture MVC définit un cadre d'organisation de votre code en accord
 41avec sa nature. Ce modèle permet une séparation de votre code en
 42**trois couches** :
 43
 44  * La couche **~Modèle~** contenant le traitement logique de vos données (les accès
 45    à la base de données se trouvent dans cette couche). Vous savez déjà que symfony stocke toutes
 46    les classes et tous les fichiers relatifs au Modèle dans le répertoire `lib/model`.
 47
 48  * La **~Vue~** est la couche où interagit l'utilisateur (un moteur de template fait parti de
 49    cette couche). Dans symfony, la couche vue est principalement faite de Templates PHP.
 50    Ces fichiers sont stockés dans les différents dossiers `templates/` comme nous le verrons
 51    plus loin.
 52
 53  * La **~Contrôleur~** est un morceau de code qui appelle le modèle pour obtenir certaines données
 54    qu'il passe à la Vue pour le rendu au client. Quand nous avons installé
 55    symfony le premier jour, nous avons vu que toutes les requêtes étaient gérées par des
 56    contrôleurs frontaux (`index.php` et `frontend_dev.php`). Ces contrôleurs frontaux
 57    délèguent le réel travail à des **actions**. Comme nous l'avons vu hier, ces
 58    actions sont logiquement regroupées dans des **modules**.
 59
 60![MVC](http://www.symfony-project.org/images/jobeet/1_4/04/mvc.png)
 61
 62Aujourd'hui, nous allons utiliser la maquette définie le 2ième jour afin de personnaliser
 63la page d'accueil et la page d'un emploi. Nous allons les rendre dynamique. En chemin, nous allons ajuster
 64un tas de choses dans beaucoup de fichiers différents afin de montrer la structure de répertoire
 65de symfony et la manière de séparer le code entre les couches.
 66
 67La mise en page
 68---------------
 69
 70D'abord, si vous regardez de plus près la maquette, vous remarquerez que la quantité de
 71chaque page vous semble le même. Vous savez déjà que la duplication de code est mauvais,
 72si nous parlons de code HTML ou PHP, donc nous devons trouver un moyen
 73d'empêcher ces éléments communs de la vue d'aboutir à la duplication du code.
 74
 75Une manière de résoudre le problème est de définir une entête et un pied de page et
 76de les inclure dans chaque Template :
 77
 78![Entête et pied de page](http://www.symfony-project.org/images/jobeet/1_4/04/header_footer.png)
 79
 80Mais dans ce cas, les fichiers header et footer ne contiennent pas de code HTML valide. Il doit
 81y avoir un meilleur moyen. Plutôt que de réinventer la roue, nous allons utiliser un autre modèle
 82pour résoudre ce problème : le
 83[modèle décorateur](http://fr.wikipedia.org/wiki/D%C3%A9corateur_(patron_de_conception)).
 84Le modèle décorateur résout le problème d'une manière différente :  le
 85Template est décorée après que le contenu soit mise en page grâce à un template global,
 86appelé **~layout|Layout~** dans symfony :
 87
 88![Layout](http://www.symfony-project.org/images/jobeet/1_4/04/layout.png)
 89
 90La mise en page par défaut d'une application est appelée `layout.php` et se
 91trouve dans le dossier `apps/frontend/templates/`. Ce répertoire contient
 92tous les Templates globaux pour une application.
 93
 94Remplacez le contenu par défaut du layout par le code suivant :
 95
 96    [php]
 97    <!-- apps/frontend/templates/layout.php -->
 98    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 99     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
100    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
101      <head>
102        <title>Jobeet - Your best job board</title>
103        <link rel="shortcut icon" href="/favicon.ico" />
104        <?php include_javascripts() ?>
105        <?php include_stylesheets() ?>
106      </head>
107      <body>
108        <div id="container">
109          <div id="header">
110            <div class="content">
111              <h1><a href="<?php echo url_for('job/index') ?>">
112                <img src="/images/logo.jpg" alt="Jobeet Job Board" />
113              </a></h1>
114
115              <div id="sub_header">
116                <div class="post">
117                  <h2>Ask for people</h2>
118                  <div>
119                    <a href="<?php echo url_for('job/index') ?>">Post a Job</a>
120                  </div>
121                </div>
122
123                <div class="search">
124                  <h2>Ask for a job</h2>
125                  <form action="" method="get">
126                    <input type="text" name="keywords"
127                      id="search_keywords" />
128                    <input type="submit" value="search" />
129                    <div class="help">
130                      Enter some keywords (city, country, position, ...)
131                    </div>
132                  </form>
133                </div>
134              </div>
135            </div>
136          </div>
137
138          <div id="content">
139            <?php if ($sf_user->hasFlash('notice')): ?>
140              <div class="flash_notice">
141                <?php echo $sf_user->getFlash('notice') ?>
142              </div>
143            <?php endif; ?>
144
145            <?php if ($sf_user->hasFlash('error')): ?>
146              <div class="flash_error">
147                <?php echo $sf_user->getFlash('error') ?>
148              </div>
149            <?php endif; ?>
150
151            <div class="content">
152              <?php echo $sf_content ?>
153            </div>
154          </div>
155
156          <div id="footer">
157            <div class="content">
158              <span class="symfony">
159                <img src="/images/jobeet-mini.png" />
160                powered by <a href="http://www.symfony-project.org/">
161                <img src="/images/symfony.gif" alt="symfony framework" />
162                </a>
163              </span>
164              <ul>
165                <li><a href="">About Jobeet</a></li>
166                <li class="feed"><a href="">Full feed</a></li>
167                <li><a href="">Jobeet API</a></li>
168                <li class="last"><a href="">Affiliates</a></li>
169              </ul>
170            </div>
171          </div>
172        </div>
173      </body>
174    </html>
175
176Un ~template|Templates~ symfony est juste un fichier PHP. Dans le template layout, vous
177trouverez des appels à des fonctions PHP et des références à des variables PHP. ~`$sf_content`~ est
178la variable la plus intéressante : elle est définie par le framework lui-même et
179contient le code HTML généré par l'action.
180
181Si vous parcourez le module `job`
182(`http://jobeet.localhost/frontend_dev.php/job`), vous verrez que toutes
183les actions sont décorés par le layout.
184
185Les feuilles de style, les images et les Javascripts
186----------------------------------------------------
187
188Comme ce tutoriel n'est pas sur le design web, nous avons déjà préparé toutes les
189ressources que nous utiliserons pour Jobeet :
190[téléchargez les fichiers image](http://www.symfony-project.org/get/jobeet/images.zip)
191et mettez les dans le dossier `web/images/`;
192[téléchargez les fichiers feuilles de style](http://www.symfony-project.org/get/jobeet/css.zip)
193et mettez les dans le dossier `web/css/`.
194
195>**NOTE**
196>Dans le layout, nous avons inclus un *favicon*. Vous pouvez
197>[télécharger celle de Jobeet](http://www.symfony-project.org/images/jobeet/favicon.ico)
198>et la déposer dans le dossier `web/`.
199
200![Le module job avec le layout et les ressources](http://www.symfony-project.org/images/jobeet/1_4/04/job_layout_assets.png)
201
202>**TIP**
203>Par défaut, la tâche `generate:project` a créé trois dossiers pour les
204>ressources du projet : `web/images/` pour les images, `web/~css|CSS~/` pour
205>les ~feuilles de style|Feuilles de style~ et `web/js/` pour les ~JavaScript~s. Ceci fait partie des
206>nombreuses ~conventions|Conventions~ définies par symfony, mais vous pouvez évidemment les
207>placer dans un autre dossier sous le répertoire `web/`.
208
209Un lecteur avisé aura remarqué que, bien que le fichier `main.css` n'est
210défini nul part dans le layout par défaut, il est nécessairement présent dans le
211code HTML généré. Mais pas pour les autres. Comment est-ce possible ?
212
213La feuille de style a été incluse grâce à la fonction `include_stylesheets()`
214située entre les balises `<head>` du fichier layout. La fonction `include_stylesheets()`
215est appelée un **helper**. Un helper est une fonction, définie par symfony,
216pouvant prendre des paramètres et renvoyant du code HTML. La plupart du temps, les helpers
217permettent de gagner du temps, ils contiennent du code fréquemment utilisé dans les templates. Le
218helper `include_stylesheets()` génère une balise `<link>` spécifique aux feuilles de style.
219
220Mais comment le helper sait quelle feuille de style inclure ?
221
222La couche de la ~Vue~ peut être paramétrée en éditant le fichier de configuration `view.yml`
223de l'application. Voici le fichier par défaut généré lors de l'appel par la tâche
224`generate:app` :
225
226    [yml]
227    # apps/frontend/config/view.yml
228    default:
229      http_metas:
230        content-type: text/html
231
232      metas:
233        #title:        symfony project
234        #description:  symfony project
235        #keywords:     symfony, project
236        #language:     en
237        #robots:       index, follow
238
239      stylesheets:    [main.css]
240
241      javascripts:    []
242
243      has_layout:     true
244      layout:         layout
245
246Le fichier `view.yml` configure les paramètres par défaut pour tous les Templates de
247l'application. Par exemple, l'entrée `stylesheets` définit un tableau de
248fichiers de feuille de style à inclure pour chaque page de l'application (ceci
249grâce au helper `include_stylesheets()`).
250
251>**NOTE**
252>Dans le fichier de configuration par défaut `view.yml`, le fichier référencé est
253>`main.css`, et non pas `css/main.css`. En fait, les deux définitions sont équivalentes.
254>Symfony ~préfixe|Préfixe~ les chemins relatifs avec `/css/`.
255
256Si plusieurs fichiers sont définis, symfony les inclura dans le même ordre que celui
257dans lequel ils ont été définis :
258
259    [yml]
260    stylesheets:    [main.css, jobs.css, job.css]
261
262Vous pouvez également définir l'attribut `media` et omettre le suffixe `.css` :
263
264    [yml]
265    stylesheets:    [main.css, jobs.css, job.css, print: { media: print }]
266
267Cette configuration génèrera le code suivant :
268
269    [php]
270    <link rel="stylesheet" type="text/css" media="screen"
271      href="/css/main.css" />
272    <link rel="stylesheet" type="text/css" media="screen"
273      href="/css/jobs.css" />
274    <link rel="stylesheet" type="text/css" media="screen"
275      href="/css/job.css" />
276    <link rel="stylesheet" type="text/css" media="print"
277      href="/css/print.css" />
278
279>**TIP**
280>Le fichier de configuration `view.yml` définit également le layout utilisé par
281>défaut pour l'application. Par défaut, son nom est `layout`. Par conséquent, symfony
282>met en page chacune de vos pages à partir du fichier `layout.php`. Vous pouvez également
283>désactiver cette mise en page en définissant l'entrée ~`has_layout`~ à `false`.
284
285Il fonctionne comme `jobs.css`, mais le fichier n'est nécessaire que pour la page d'accueil et le
286fichier `job.css` n'est nécessaire que pour la page emploi. Le fichier de configuration `view.yml`
287peut être personnalisés sur la base de chaque module. Changez la clé `stylesheets` du fichier
288`view.yml` de l'application pour contenir uniquement le fichier `main.css` :
289
290    [yml]
291    # apps/frontend/config/view.yml
292    stylesheets:    [main.css]
293
294Pour personnaliser la vue du module `job`, créez un fichier `view.yml` dans le
295répertoire `apps/frontend/modules/job/config/` :
296
297    [yml]
298    # apps/frontend/modules/job/config/view.yml
299    indexSuccess:
300      stylesheets: [jobs.css]
301
302    showSuccess:
303      stylesheets: [job.css]
304
305Sous les sections `indexSuccess` et `showSuccess` (qui sont les noms des Templates
306associés aux actions `index` et `show`, comme nous le verrons plus tard),
307vous pouvez personnaliser les entrées se trouvant sous la section `default` du
308fichier `view.yml` de l'application. Toutes les entrées spécifiques sont fusionnées avec la configuration
309de l'application. Vous pouvez également définir une configuration de toutes les actions d'un
310module avec la section spéciale `all`.
311
312>**SIDEBAR**
313>Principes de configuration dans symfony
314>
315>Pour beaucoup de fichiers de ~configuration|Configuration~ de symfony, un même paramètre peut
316>être défini à différents niveaux :
317>
318>  * La configuration par défaut se trouve dans le framework
319>  * La configuration globale pour le projet (dans le répertoire `config/`)
320>  * La configuration locale pour l'application (dans le répertoire `apps/APP/config/`)
321>  * La configuration locale est restreinte à un module (dans le répertoire
322>    `apps/APP/modules/MODULE/config/`)
323>
324>Lors de l'exécution, le système de configuration fusionne tous les valeurs depuis les
325>différents fichiers si ils existent et met le résultat en cache pour de meilleures performances.
326
327En règle générale, quand quelque chose est configurable via un fichier de configuration,
328la même chose peut être faite avec du code PHP. Au lieu de créer un fichier `view.yml`
329pour le module `job` par exemple, vous pouvez aussi utiliser le helper `use_stylesheet()`
330pour inclure une feuille de style depuis un template :
331
332    [php]
333    <?php use_stylesheet('main.css') ?>
334
335Vous pouvez également utiliser ce helper dans le layout pour inclure une feuille de style globale.
336
337Le choix entre une méthode ou une autre est réellement une question de goût. Le
338fichier `view.yml` permet de définir quelque chose pour toutes les actions d'un module,
339ce qui n'est pas possible depuis un template. Cela dit, la configuration est plus statique.
340A l'inverse, le ~helper|Helpers~ `use_stylesheet()` est plus flexible et
341plus encore, tout se trouve au même endroit : la définition des feuilles de style et le
342code HTML. Pour Jobeet, nous allons utiliser le helper `use_stylesheet()`, nous pouvons donc
343supprimer le fichier `view.yml` que nous venons de créer, et mettre à jour le template `job` avec
344les appels à `use_stylesheet()` :
345
346    [php]
347    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
348    <?php use_stylesheet('jobs.css') ?>
349
350    <!-- apps/frontend/modules/job/templates/showSuccess.php -->
351    <?php use_stylesheet('job.css') ?>
352
353>**NOTE**
354>De la même manière, la configuration JavaScript est faite via l'entrée `javascripts`
355>du fichier de configuration `view.yml` et via le ~helper `use_javascript()`~ permettant
356>d'inclure des fichiers JavaScript dans un Template.
357
358La page d'accueil Job
359---------------------
360
361Comme vu le troisième jour, la page d'accueil est générée par l'action `index`
362du module `job`. L'action `index` fait partie de la couche Contrôleur de la page
363et le template associé, `indexSuccess.php`, fait parti de la couche Vue :
364
365    apps/
366      frontend/
367        modules/
368          job/
369            actions/
370              actions.class.php
371            templates/
372              indexSuccess.php
373
374### L'action
375
376Chaque action est représentée par une méthode de la classe. Pour la page d'accueil job,
377la classe est `jobActions` (le nom du module avec le suffixe `Actions`) et la méthode
378est `executeIndex()` (le nom de l'action avec le préfixe `execute`). Dans notre cas,
379cela renvoie tous les jobs de la base de données :
380
381    [php]
382    // apps/frontend/modules/job/actions/actions.class.php
383    class jobActions extends sfActions
384    {
385      public function executeIndex(sfWebRequest $request)
386      {
387<propel>
388        $this->jobeet_jobs = JobeetJobPeer::doSelect(new Criteria());
389</propel>
390<doctrine>
391        $this->jobeet_jobs = Doctrine::getTable('JobeetJob')
392          ->createQuery('a')
393          ->execute();
394</doctrine>
395      }
396
397      // ...
398    }
399
400<propel>
401Analysons de plus près le code : la méthode `executeIndex()` (le Contrôleur)
402appelle le Modèle `JobeetJobPeer` pour renvoyer tous les jobs (`new Criteria()`).
403Le modèle renvoie un tableau d'objet de type `JobeetJob` que l'on affecte à la propriété
404`jobeet_jobs` de l'objet courant.
405</propel>
406<doctrine>
407Analysons de plus près le code : la méthode `executeIndex()` (le Contrôleur)
408appelle la Table `JobeetJob` pour créer une requête renvoyant tous les jobs.
409La Table renvoie une 'Doctrine_Collection` d'objets de type `JobettJob` que l'on affecte 
410à la propriété `jobeet_jobs` de l'objet courant.
411</doctrine>
412
413Toutes les propriétés des objets sont automatiquement passées au template (la Vue).
414Pour transmettre des données du Contrôleur à la Vue, il vous suffit simplement de créer une
415nouvelle propriété :
416
417    [php]
418    public function executeFooBar(sfWebRequest $request)
419    {
420      $this->foo = 'bar';
421      $this->bar = array('bar', 'baz');
422    }
423
424Cette méthode rendra les variables `$foo` et `$bar` accessibles depuis le template.
425
426### Le Template
427
428Par défaut, le nom du ~template|Templates~ associé à l'action est déduit par symfony
429grâce a une convention (le nom de l'action avec le suffixe `Success`).
430
431Le template `indexSuccess.php` génère une table HTML pour tous les jobs. Voici
432le code  du Template actuel :
433
434    [php]
435    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
436    <?php use_stylesheet('jobs.css') ?>
437
438    <h1>Job List</h1>
439
440    <table>
441      <thead>
442        <tr>
443          <th>Id</th>
444          <th>Category</th>
445          <th>Type</th>
446    <!-- more columns here -->
447          <th>Created at</th>
448          <th>Updated at</th>
449        </tr>
450      </thead>
451      <tbody>
452        <?php foreach ($jobeet_jobs as $jobeet_job): ?>
453        <tr>
454          <td>
455            <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>">
456              <?php echo $jobeet_job->getId() ?>
457            </a>
458          </td>
459          <td><?php echo $jobeet_job->getCategoryId() ?></td>
460          <td><?php echo $jobeet_job->getType() ?></td>
461    <!-- more columns here -->
462          <td><?php echo $jobeet_job->getCreatedAt() ?></td>
463          <td><?php echo $jobeet_job->getUpdatedAt() ?></td>
464        </tr>
465        <?php endforeach; ?>
466      </tbody>
467    </table>
468
469    <a href="<?php echo url_for('job/new') ?>">New</a>
470
471Dans ce code, la boucle `foreach` parcourt la liste d'objets `job` (`$jobeet_jobs`)
472et pour chaque job, chaque valeur de colonne est affichée.
473Souvenez-vous, pour accéder à la valeur d'une colonne, il suffit simplement d'appeller une méthode accesseur.
474dont le nom commence par `get` et suivit du nom de la colonne en ~camelCased|Formattage du code~
475(par exemple, la méthode `getCreatedAt()` permet d'accéder à la colonne `created_at`).
476
477Faisons un peu de tri dans tout ça afin de n'afficher qu'une partie des colonnes :
478
479    [php]
480    <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
481    <?php use_stylesheet('jobs.css') ?>
482
483    <div id="jobs">
484      <table class="jobs">
485        <?php foreach ($jobeet_jobs as $i => $job): ?>
486          <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
487            <td class="location"><?php echo $job->getLocation() ?></td>
488            <td class="position">
489              <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
490                <?php echo $job->getPosition() ?>
491              </a>
492            </td>
493            <td class="company"><?php echo $job->getCompany() ?></td>
494          </tr>
495        <?php endforeach; ?>
496      </table>
497    </div>
498
499![Homepage](http://www.symfony-project.org/images/jobeet/1_4/04/homepage.png)
500
501La fonction `url_for()` utilisée dans ce template est un helper symfony que nous détaillerons
502dans le chapitre de demain.
503
504Le template de la page job
505--------------------------
506
507Personnalisons maintenant le template de la page job. Ouvrez le fichier `showSuccess.php`
508et remplacez son contenu par le code suivant :
509
510    [php]
511    <!-- apps/frontend/modules/job/templates/showSuccess.php -->
512    <?php use_stylesheet('job.css') ?>
513    <?php use_helper('Text') ?>
514
515    <div id="job">
516      <h1><?php echo $job->getCompany() ?></h1>
517      <h2><?php echo $job->getLocation() ?></h2>
518      <h3>
519        <?php echo $job->getPosition() ?>
520        <small> - <?php echo $job->getType() ?></small>
521      </h3>
522
523      <?php if ($job->getLogo()): ?>
524        <div class="logo">
525          <a href="<?php echo $job->getUrl() ?>">
526            <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
527              alt="<?php echo $job->getCompany() ?> logo" />
528          </a>
529        </div>
530      <?php endif; ?>
531
532      <div class="description">
533        <?php echo simple_format_text($job->getDescription()) ?>
534      </div>
535
536      <h4>How to apply?</h4>
537
538      <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
539
540      <div class="meta">
541<propel>
542        <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
543</propel>
544<doctrine>
545        <small>posted on <?php echo $job->getDateTimeObject('created_at')->format('m/d/Y') ?></small>
546</doctrine>
547      </div>
548
549      <div style="padding: 20px 0">
550        <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">
551          Edit
552        </a>
553      </div>
554    </div>
555
556Ce template utilise la variable `$job` passée en paramètre par l'action pour afficher
557les informations sur un job. Comme nous avons renommé la variable utilisée dans le template
558de `$jobeet_job` en `$job`), vous devez également faire modification dans l'action `show`
559(attention, la variable s'y trouve deux fois) :
560
561    [php]
562    // apps/frontend/modules/job/actions/actions.class.php
563    public function executeShow(sfWebRequest $request)
564    {
565<propel>
566      $this->job =
567       ➥ JobeetJobPeer::retrieveByPk($request->getParameter('id'));
568</propel>
569<doctrine>
570      $this->job = Doctrine::getTable('JobeetJob')->
571       ➥ find($request->getParameter('id'));
572</doctrine>
573      $this->forward404Unless($this->job);
574    }
575
576<propel>
577Remarquez que certains ~accesseurs|Acesseurs~ Propel prennent des argumentss. Comme nous avons
578défini la colonne `created_at` de type timestamp, l'accesseur `getCreatedAt()` prend
579en paramètre le format de la date à renvoyer comme premier argument :
580
581    [php]
582    $job->getCreatedAt('m/d/Y');
583</propel>
584<doctrine>
585Notez que les colonnes de date peuvent être converties en instances d'objets PHP DateTime. Comme
586nous avons défini la colonne `created_at` comme un timestamp, vous pouvez convertir l
587valeur de la colonne en un objet DateTime en utilisant la méthode `getDateTimeObject()`
588et ensuite appeler la méthode `format()` dont son premier argument prend un format de
589mise en forme d'une date :
590
591    [php]
592    $job->getDateTimeObject('created_at')->format('m/d/Y');
593</doctrine>
594
595>**NOTE**
596>La description d'un job utilise le helper `simple_format_text()` afin de formater
597>le texte en HTML, en remplaçant notamment les retours chariots par des balises `<br />`.
598>Comme ce helper fait parti du groupe `Text` et que celui-ci n'est pas chargé par défaut,
599>nous le chargeons manuellement en utilisant le ~helper `use_helper()`~.
600
601![La page job](http://www.symfony-project.org/images/jobeet/1_4/04/job.png)
602
603Les ~Slot~s
604-----------
605
606Actuellement, le titre de toutes les pages est défini dans la balise `<title>`
607du layout :
608
609    [php]
610    <title>Jobeet - Your best job board</title>
611
612Mais pour un job, nous aimerions avoir des informations plus utiles telles que le 
613nom de la société et le type d'emploi.
614
615Avec symfony, quand une zone du layout dépend du template à afficher, vous devez utiliser
616un slot :
617
618![Slots](http://www.symfony-project.org/images/jobeet/1_4/04/layout_slots.png)
619
620Ajoutez un slot au layout afin de rendre le titre dynamique :
621
622    [php]
623    // apps/frontend/templates/layout.php
624    <title><?php include_slot('title') ?></title>
625
626Chaque slot est identifié par un nom (ici `title`) et peut être affiché en
627utilisant le helper `~include_slot()~`. Maintenant, au début du template
628`showSuccess.php`, utilisez le helper `slot()` afin de définir le contenu
629du slot pour la page job :
630
631    [php]
632    // apps/frontend/modules/job/templates/showSuccess.php
633    <?php slot(
634      'title',
635      sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()))
636    ?>
637
638Si le titre est complexe à définir, le helper `slot()` peut aussi être utilisé
639dans un block de code :
640
641    [php]
642    // apps/frontend/modules/job/templates/showSuccess.php
643    <?php slot('title') ?>
644      <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?>
645    <?php end_slot(); ?>
646
647Pour certaines pages, comme la page d'accueil, nous avons juste besoin d'un titre
648générique. Plutôt que de répéter le même titre encore et encore dans chaque template,
649nous pouvons définir un titre par défaut dans le layout :
650
651    [php]
652    // apps/frontend/templates/layout.php
653    <title>
654      <?php include_slot('title', 'Jobeet - Your best job board') ?>
655    </title>
656
657Le deuxième argument de la méthode `include_slot()` est la valeur par défaut pour
658le slot si elle n'a pas été défini. Si la valeur par défaut est plus longue ou a
659certaines balises HTML, vous pouvez aussi le définir comme dans le code suivant :
660
661    [php]
662    // apps/frontend/templates/layout.php
663    <title>
664      <?php if (!include_slot('title')): ?>
665        Jobeet - Your best job board
666      <?php endif; ?>
667    </title>
668
669Le helper `include_slot()` renvoie `true` si le slot a été défini. Ainsi,
670lorsque vous spécifiez une valeur pour le slot `title` dans un template, il est utilisé, sinon,
671ce sera le titre par défaut qui sera utilisé.
672
673>**TIP**
674>Nous avons déjà vu quelques helpers commençant par `include_`. Ces helpers
675>renvoient du code HTML et dans la plupart des cas, ils  ont un helper `get_`
676>permettant de renvoyer uniquement le contenu :
677>
678>     [php]
679>     <?php include_slot('title') ?>
680>     <?php echo get_slot('title') ?>
681>
682>     <?php include_stylesheets() ?>
683>     <?php echo get_stylesheets() ?>
684
685L'action de la page job
686-----------------------
687
688La page job est générée grâce à l'action `show`, définie par la méthode
689`executeShow()` du module `job` :
690
691    [php]
692    class jobActions extends sfActions
693    {
694      public function executeShow(sfWebRequest $request)
695      {
696<propel>
697        $this->job =
698         ➥ JobeetJobPeer::retrieveByPk($request->getParameter('id'));
699</propel>
700<doctrine>
701        $this->job = Doctrine::getTable('JobeetJob')->
702         ➥ find($request->getParameter('id'));
703</doctrine>
704        $this->forward404Unless($this->job);
705      }
706
707      // ...
708    }
709
710<propel>
711Comme dans l'action `index`, la classe `JobeetJobPeer` est utilisée pour récupérer
712un job, cette fois en utilisant la méthode `retrieveByPk()`. Le paramètre de cette
713méthode est un identifiant unique d'un job, sa ~clé primaire|Clé primaire~. La section
714suivante explique pourquoi l'instruction `$request->getParameter('id')` renvoie la clé
715primaire d'un job.
716</propel>
717<doctrine>
718Comme dans l'action `index`, la classe de la table `JobeetJob` est utilisée pour récupérer un job,
719cette fois en utilisant la méthode `find()`. Le paramètre de cette méthode est
720l'identifiant unique d'un job, sa ~clé primaire|Clé primaire~. La prochaine
721section explique pourquoi l'instruction `$request->getParameter('id')` renvoie la clé
722primaire d'un job.
723</doctrine>
724
725<propel>
726>**TIP**
727>Le modèle de classe généré contient un grand nombre de méthodes utiles pour interagir avec
728>les objets du projet. Prenez un peu de temps pour parcourir le code situé dans le dossier
729>`lib/om/` et découvrez toute la puissance embarqué dans ces classes.
730</propel>
731
732Si le job n'existe pas dans la base de données, nous voudrions renvoyer l'utilisateur vers
733une page ~404|erreur 404~, c'est ce que fait exactementla méthode `forward404Unless()`.
734Elle prend en premier paramètre un Booléen et, à moins que ce paramètre ne soit à true,
735elle arrête l'exécution normale. Cette méthode génère une exception `sfError404Exception`
736et vous n'avez donc pas besoin de rajouter de `return` après cette
737méthode.
738
739Comme pour toutes ~exceptions|Gestion de l'exception~, la page affichée est différente en fonction de
740l'~environnement|Environnement~ de `prod` ou de `dev` :
741
742![Erreur 404 dans l'environnement dev](http://www.symfony-project.org/images/jobeet/1_4/05/404_dev.png)
743
744![Erreur 404 dans l'environnement prod](http://www.symfony-project.org/images/jobeet/1_4/05/404_prod.png)
745
746>**NOTE**
747>Avant que nous déployons notre site Jobeet sur le serveur de production, vous
748>apprendrez à personnaliser la page 404 par défaut.
749
750-
751
752>**SIDEBAR**
753>La famille des méthodes "~forward|Action de redirection~"
754>
755>L'appel à la méthode `forward404Unless` est équivalent à :
756>
757>     [php]
758>     $this->forward404If(!$this->job);
759>
760>qui est équivalent à :
761>
762>     [php]
763>     if (!$this->job)
764>     {
765>       $this->forward404();
766>     }
767>
768>La méthode `forward404()` elle-même étant juste un raccourci pour :
769>
770>     [php]
771>     $this->forward('default', '404');
772>
773>La méthode `forward()` renvoie vers une autre action de la même application;
774>dans l'exemple précédent, vers l'action `404` du module `default`.
775>Le module `default` fait parti intégrante de symfony et fournit des actions
776>par défaut pour afficher les pages 404, de sécurité et de connexion.
777
778Les requêtes et les réponses
779----------------------------
780
781Quand vous accédez à la page `/job` ou `/job/show/id/1` depuis votre navigateur, vous
782provoquez un ensemble de traitements entre le serveur web et votre ordinateur. Votre navigateur envoie
783une **~requête|Requête HTTP~** et le serveur vous renvoie une **~réponse|Réponse HTTP~**.
784
785Nous avons déjà vu que symfony encapsule les requêtes dans un objet `sfWebRequest`
786(regardez la signature de la méthode `executeShow()`). Et comme symfony est un
787framework Orienté Objet, la réponse est également un objet de classe
788`sfWebResponse`. Vous pouvez récupérer l'objet de la réponse dans une action en appelant
789`$this->getResponse()`.
790
791Ces objets permettent un accès pratique et simple pour obtenir des informations
792sur des fonctions PHP et des variables globales PHP.
793
794>**NOTE**
795>Pourquoi symfony redéfinit-il des fonctions PHP déjà existantes ? Premièrement,
796>parce que celles de symfony sont plus puissantes que leur homologue PHP. Ensuite,
797>parce que quand vous testez une application, il est plus facile de simuler des requêtes
798>ou des réponses grâce à des objets plutôt que d'essayer d'utiliser des variables globales
799>ou travailler avec des fonctions PHP comme `header()` si mystiques.
800
801### La requête
802
803La classe `sfWebRequest` redéfinit les tableaux globaux PHP ~`$_SERVER`~, 
804~`$_COOKIE`~, ~`$_GET`~, ~`$_POST`~, et ~`$_FILES`~ :
805
806 Nom de la méthode    | Équivalent PHP
807 -------------------- | --------------------------------------------------
808 `getMethod()`        | `$_SERVER['REQUEST_METHOD']`
809 `getUri()`           | `$_SERVER['REQUEST_URI']`
810 `getReferer()`       | `$_SERVER['HTTP_REFERER']`
811 `getHost()`          | `$_SERVER['HTTP_HOST']`
812 `getLanguages()`     | `$_SERVER['HTTP_ACCEPT_LANGUAGE']`
813 `getCharsets()`      | `$_SERVER['HTTP_ACCEPT_CHARSET']`
814 `isXmlHttpRequest()` | `$_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest'`
815 `getHttpHeader()`    | `$_SERVER`
816 `getCookie()`        | `$_COOKIE`
817 `isSecure()`         | `$_SERVER['HTTPS']`
818 `getFiles()`         | `$_FILES`
819 `getGetParameter()`  | `$_GET`
820 `getPostParameter()` | `$_POST`
821 `getUrlParameter()`  | `$_SERVER['PATH_INFO']`
822 `getRemoteAddress()` | `$_SERVER['REMOTE_ADDR']`
823
824Nous avons déjà accédé aux paramètres d'une requête en utilisant la méthode
825`getParameter()`. Elle renvoie une valeur depuis la variable globale `$_GET` ou `$_POST`,
826ou depuis la variable `~PATH_INFO~`.
827
828Si vous voulez être sûr qu'un paramètre demandé provienne de l'une de ces variables en
829particulier, vous devez utiliser respectivement la méthode `getGetParameter()`,
830`getPostParameter()` et `getUrlParameter()`.
831
832>**NOTE**
833>Quand vous voulez restreindre une action pour une ~méthode HTTP|Méthode HTTP~ spécifique,
834>par exemple quand vous voulez être sûr qu'un formulaire ait été envoyé via la méthode `POST`,
835>vous pouvez utiliser la méthode `isMethod()` :
836>`$this->forwardUnless($request->isMethod('POST'));`.
837
838### La réponse
839
840La classe `sfWebResponse` redéfinit les méthodes PHP `~header|HTTP Headers~()` et
841`setraw~cookie|Cookies~()` :
842
843 Nom de la méthode             | Équivalent PHP
844 ----------------------------- | ----------------
845 `setCookie()`                 | `setrawcookie()`
846 `setStatusCode()`             | `header()`
847 `setHttpHeader()`             | `header()`
848 `setContentType()`            | `header()`
849 `addVaryHttpHeader()`         | `header()`
850 `addCacheControlHttpHeader()` | `header()`
851
852Évidemment, la classe `sfWebResponse` permet aussi de définir la réponse du serveur web
853(`setContent()`) et de l'envoyer au navigateur (`send()`).
854
855Plus tôt aujourd'hui, nous avons vu comment gérer les feuilles de style et les JavaScripts dans le
856fichier `view.yml` et dans les templates. Finalement, ces deux techniques utilisent les méthodes 
857`addStylesheet()` et `addJavascript()` de l'objet réponse.
858
859>**TIP**
860>Les classes [`sfAction`](http://www.symfony-project.org/api/1_4/sfAction),
861>[`sfRequest`](http://www.symfony-project.org/api/1_4/sfRequest), et
862>[`sfResponse`](http://www.symfony-project.org/api/1_4/sfResponse)
863>fournissent un grand nombre de méthodes très utiles. N'hésitez pas à
864>parcourir [la documentation de l'API](http://www.symfony-project.org/api/1_4/)
865>pour en apprendre plus sur les classes internes de symfony.
866
867Conclusion
868----------
869
870Tout au long de ce chapitre, nous avons décrit quelques uns des modèles de conception utilisés par symfony. Espérons que
871la structure des répertoires du projet ait maintenant plus de sens. Nous avons joué avec les
872Templates en manipulant la mise en page et les fichiers des Templates. Nous avons également rendu
873les pages un peu plus dynamiques grâce aux slots et aux actions.
874
875Au cours du prochain chapitre, nous en apprendrons davantage sur le helper `url_for()` que nous avons aperçu aujourd'hui,
876et le "sous-framework" de routage qui lui est associé.
877
878__ORM__