Development

Documentation/fr_FR/jobeet/6 (diff)

You must first sign up to be able to contribute.

Changes between Version 6 and Version 7 of Documentation/fr_FR/jobeet/6

Show
Ignore:
Author:
sebastien.b (IP: 82.251.154.4)
Timestamp:
03/22/09 14:56:38 (8 years ago)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Documentation/fr_FR/jobeet/6

    v6 v7  
    77 * Traduction : Sébastien B 
    88 * Date de traduction : 12 Mars 2009 
    9  * Date de dernière modification : 12 Mars 2009 
     9 * Date de dernière modification : 
    1010{{{ 
    1111#!html 
    1212</div> 
     13<div style="width:75%"> 
    1314}}} 
    1415= Jour 6: Aller plus loin avec le Modèle = 
    1516 
    16 Hier était un grand jour. Vous avez appris comment créer des URLs propres et comment[[BR]] 
    17 utiliser symfony pour automatiser beaucoup de choses.[[BR]][[BR]] 
    18  
    19 Aujourd'hui, nous allons améliorer Jobeet en optimisant le code ci et là. Vous en[[BR]] 
    20 apprendrez plus sur toutes les fonctions que nous avons déjà présenté au cours[[BR]] 
    21 des jours précédents.[[BR]][[BR]] 
     17Hier était un grand jour. Vous avez appris comment créer des URLs propres et comment 
     18utiliser symfony pour automatiser beaucoup de choses. 
     19 
     20Aujourd'hui, nous allons améliorer Jobeet en optimisant le code ci et là. Vous en 
     21apprendrez plus sur toutes les fonctions que nous avons déjà présenté au cours 
     22des jours précédents. 
    2223 
    2324== __L' objet Propel Criteria__ == 
    2425 
    25 Conditions du Jour 2 :[[BR]] 
     26Conditions du Jour 2 : 
    2627 
    2728  === "Quand un utilisateur arrive sur Jobeet, il doit voir la liste des jobs actifs." === 
    2829 
    29 Mais pour l'instant, tous les jobs sont affichés, qu'ils soient actifs ou non :[[BR]] 
     30Mais pour l'instant, tous les jobs sont affichés, qu'ils soient actifs ou non : 
    3031{{{ 
    3132#!php 
    4142    } 
    4243}}} 
    43 Un job est considéré actif s'il a été posté il y a moins de 30 jours. La méthode[[BR]] 
    44 `doSelect()` utilise un objet `~Criteria~` qui décrit la requête à exécuter sur[[BR]] 
    45 la base de données. Dans le code ci-dessus, aucun argument n'est passé dans[[BR]] 
    46 `Criteria`, ce qui signifie que tous les enregistrements seront retournés.[[BR]] 
     44Un job est considéré actif s'il a été posté il y a moins de 30 jours. La méthode 
     45`doSelect()` utilise un objet `~Criteria~` qui décrit la requête à exécuter sur 
     46la base de données. Dans le code ci-dessus, aucun argument n'est passé dans 
     47`Criteria`, ce qui signifie que tous les enregistrements seront retournés. 
    4748 
    4849Modifions cela pour n'afficher que les jobs actifs : 
    5758    } 
    5859}}} 
    59 La méthode `Criteria::add()` ajoute une clause `WHERE` à la requête SQL générée.[[BR]] 
    60 Ici, criteria retournera les jobs qui datent de moins de 30 jours.[[BR]] 
    61 La méthode `add()` accepte plusieurs types d'opérateurs de comparaison.[[BR]] 
    62 Voici les plus utilisés :[[BR]] 
    63  
    64   * `Criteria::EQUAL`[[BR]] 
    65   * `Criteria::NOT_EQUAL`[[BR]] 
    66   * `Criteria::GREATER_THAN`, `Criteria::GREATER_EQUAL`[[BR]] 
    67   * `Criteria::LESS_THAN`, `Criteria::LESS_EQUAL`[[BR]] 
    68   * `Criteria::LIKE`, `Criteria::NOT_LIKE`[[BR]] 
    69   * `Criteria::CUSTOM`[[BR]] 
    70   * `Criteria::IN`, `Criteria::NOT_IN`[[BR]] 
    71   * `Criteria::ISNULL`, `Criteria::ISNOTNULL`[[BR]] 
    72   * `Criteria::CURRENT_DATE`, `Criteria::CURRENT_TIME`,[[BR]] 
    73     `Criteria::CURRENT_TIMESTAMP`[[BR]] 
     60La méthode `Criteria::add()` ajoute une clause `WHERE` à la requête SQL générée. 
     61Ici, criteria retournera les jobs qui datent de moins de 30 jours. 
     62La méthode `add()` accepte plusieurs types d'opérateurs de comparaison. 
     63Voici les plus utilisés : 
     64 
     65  * `Criteria::EQUAL` 
     66  * `Criteria::NOT_EQUAL` 
     67  * `Criteria::GREATER_THAN`, `Criteria::GREATER_EQUAL` 
     68  * `Criteria::LESS_THAN`, `Criteria::LESS_EQUAL` 
     69  * `Criteria::LIKE`, `Criteria::NOT_LIKE` 
     70  * `Criteria::CUSTOM` 
     71  * `Criteria::IN`, `Criteria::NOT_IN` 
     72  * `Criteria::ISNULL`, `Criteria::ISNOTNULL` 
     73  * `Criteria::CURRENT_DATE`, `Criteria::CURRENT_TIME`, 
     74    `Criteria::CURRENT_TIMESTAMP` 
    7475 
    7576== __Debugging Propel generated SQL__ == 
    7677 
    77 Etant donné que vous n'écrivez pas les requêtes SQL, c'est Propel qui prend en[[BR]] 
    78 charge la génération des requêtes propres au moteur de base de donnée choisi le[[BR]] 
    79 3ème jour. Mais il est parfois utile de connaître le code SQL généré par Propel;.[[BR]] 
    80 Par exemple, pour debugger une requête qui ne fonctionne pas. Dans l'environnement[[BR]] 
    81 de développement, symfony enregistre toutes ces requêtes ( ainsi que beaucoup[[BR]] 
    82 d'autres informations ) dans le répertoire `log/`. There is one log file for every[[BR]] 
    83 combination of an application and an environment. Le fichier qui nous intéresse[[BR]] 
     78Etant donné que vous n'écrivez pas les requêtes SQL, c'est Propel qui prend en 
     79charge la génération des requêtes propres au moteur de base de donnée choisi le 
     803ème jour. Mais il est parfois utile de connaître le code SQL généré par Propel;. 
     81Par exemple, pour debugger une requête qui ne fonctionne pas. Dans l'environnement 
     82de développement, symfony enregistre toutes ces requêtes ( ainsi que beaucoup 
     83d'autres informations ) dans le répertoire `log/`. There is one log file for every 
     84combination of an application and an environment. Le fichier qui nous intéresse 
    8485est `frontend_dev.log` : 
    8586{{{ 
    8990    Dec 6 15:47:12 symfony [debug] {sfPropelLogger} Binding '2008-11-06 15:47:12' at position :p1 w/ PDO type PDO::PARAM_STR 
    9091}}} 
    91 Comme vous pouvez le voir, Propel a généré une clause WHERE pour la colonne[[BR]] 
    92 `created_at` column (`WHERE jobeet_job.CREATED_AT > :p1`).[[BR]] 
    93  
    94 >>'''__NOTE__'''[[BR]] 
    95 >Dans la requête, `:p1` indique que Propel utilise les conditions déclarées.[[BR]] 
    96 >La valeur actuelle de `:p1` ( '`2008-11-06 15:47:12`' dans l'exemple ci-dessus )[[BR]] 
    97 >est passée pendant l'exécution de la requête. L'utilisation de conditions [[BR]] 
     92Comme vous pouvez le voir, Propel a généré une clause WHERE pour la colonne 
     93`created_at` column (`WHERE jobeet_job.CREATED_AT > :p1`). 
     94 
     95>>'''__NOTE__''' 
     96>Dans la requête, `:p1` indique que Propel utilise les conditions déclarées. 
     97>La valeur actuelle de `:p1` ( '`2008-11-06 15:47:12`' dans l'exemple ci-dessus ) 
     98>est passée pendant l'exécution de la requête. L'utilisation de conditions  
    9899>déclarées reduit considérablement le risque d'attaques du type [http://en.wikipedia.org/wiki/Sql_injection SQL injection] 
    99100 
    100 Le travail est facilité mais devoir basculer entre le navigateur, l'IDE et le[[BR]] 
    101 fichier log à chaque fois que l'on veut tester une modification est assez contraignant.[[BR]] 
    102 Heureusement, symfony possède une barre d'outil de débuggage. Toutes les informations[[BR]] 
    103 nécessaire sont disponibles dans votre navigateur :[[BR]] 
     101Le travail est facilité mais devoir basculer entre le navigateur, l'IDE et le 
     102fichier log à chaque fois que l'on veut tester une modification est assez contraignant. 
     103Heureusement, symfony possède une barre d'outil de débuggage. Toutes les informations 
     104nécessaire sont disponibles dans votre navigateur : 
    104105 
    105106[[Image(web_debug_sql.png)]] 
    107108== __Object ~Serialization~__ == 
    108109 
    109 Jusqu'à présent, notre code fonctionne mais il est loin d'être parfait et ne prend[[BR]] 
    110 pas en charge les contraintes évoqués le 2ème jour :[[BR]] 
     110Jusqu'à présent, notre code fonctionne mais il est loin d'être parfait et ne prend 
     111pas en charge les contraintes évoqués le 2ème jour : 
    111112 
    112113  === "Un utilisateur peut activer à nouveau ou augmenter la validité de l'offre d'emploi pour une période de 30 jours supplémentaires..." === 
    113114 
    114 Le code actuel se base sur la valeur de la colonne `created_at` qui stocke la[[BR]] 
    115 date de création ce qui ne nous permet pas de satisfaire la condition ci-dessus.[[BR]][[BR]] 
    116  
    117 Mais si vous vous rappelez le schéma de la base décrit le 3ème jour, nous avons[[BR]] 
    118 aussi défini une colonne `expires_at`. Pour l'instant cette valeur est vide car[[BR]] 
    119 nous ne l'avons pas renseignée dans le fichier du répertoire fixture. Cette valeur[[BR]] 
    120 peut être automatiquement renseignée à plus 30 jours à partir de la date courante[[BR]] 
    121 lors de la création d'un job.[[BR]] 
    122  
    123 Quand vous devez créer une action automatique avant que l'objet Propel soit[[BR]] 
    124 sérialisé dans la base, vous pouvez surpasser la méthode `save()` de la classe[[BR]] 
     115Le code actuel se base sur la valeur de la colonne `created_at` qui stocke la 
     116date de création ce qui ne nous permet pas de satisfaire la condition ci-dessus. 
     117 
     118Mais si vous vous rappelez le schéma de la base décrit le 3ème jour, nous avons 
     119aussi défini une colonne `expires_at`. Pour l'instant cette valeur est vide car 
     120nous ne l'avons pas renseignée dans le fichier du répertoire fixture. Cette valeur 
     121peut être automatiquement renseignée à plus 30 jours à partir de la date courante 
     122lors de la création d'un job. 
     123 
     124Quand vous devez créer une action automatique avant que l'objet Propel soit 
     125sérialisé dans la base, vous pouvez surpasser la méthode `save()` de la classe 
    125126du modèle : 
    126127{{{ 
    143144    } 
    144145}}} 
    145 La méthode `~isNew()~` renvoie `true` quand l'objet n'est pas encore sérialisé[[BR]] 
    146 dans la base et `false` dans la cas contraire.[[BR]][[BR]] 
    147  
    148 A présent, modifions l'action pour récupérer les jobs actifs en utilisant la[[BR]] 
    149 colonne `expires_at` au lieu de la colonne `created_at` : [[BR]] 
     146La méthode `~isNew()~` renvoie `true` quand l'objet n'est pas encore sérialisé 
     147dans la base et `false` dans la cas contraire. 
     148 
     149A présent, modifions l'action pour récupérer les jobs actifs en utilisant la 
     150colonne `expires_at` au lieu de la colonne `created_at` :  
    150151{{{ 
    151152#!php 
    158159    } 
    159160}}} 
    160 La requête sélectionnera seulement les jobs possédant une date `expires_at`.[[BR]][[BR]] 
     161La requête sélectionnera seulement les jobs possédant une date `expires_at`. 
    161162 
    162163== __Aller plus loin avec Fixtures__ == 
    163164 
    164 Si vous actualisez la page d'accueil de Jobeet, vous ne constaterez aucun[[BR]] 
     165Si vous actualisez la page d'accueil de Jobeet, vous ne constaterez aucun 
    165166changement. Modifions le fichier fixture pour ajouter un job qui a expiré : 
    166167{{{ 
    184185        email:        job@example.com 
    185186}}} 
    186 >>'''__NOTE__'''[[BR]] 
    187 >Faites bien attention quand vous faîtes un copier/coller du code. il faut conserver[[BR]] 
    188 >l'indentation. Il doit y avoir deux espaces devant `expired_job`.[[BR]] 
    189  
    190 Comme vous pouvez le constater, il est possible de définir une valeur pour la colonne[[BR]] 
    191 `created_at` même si elle est automatiquement remplie par Propel. La valeur[[BR]] 
    192 définie sera utilisé à la place de la valeur automatique. Rechargez les fixtures[[BR]] 
    193 et actualisez la page d'accueil pour vérifier que l'ancien job n'apparaisse pas :[[BR]] 
     187>>'''__NOTE__''' 
     188>Faites bien attention quand vous faîtes un copier/coller du code. il faut conserver 
     189>l'indentation. Il doit y avoir deux espaces devant `expired_job`. 
     190 
     191Comme vous pouvez le constater, il est possible de définir une valeur pour la colonne 
     192`created_at` même si elle est automatiquement remplie par Propel. La valeur 
     193définie sera utilisé à la place de la valeur automatique. Rechargez les fixtures 
     194et actualisez la page d'accueil pour vérifier que l'ancien job n'apparaisse pas : 
    194195{{{ 
    195196    $ php symfony propel:data-load 
    196197}}} 
    197 Vous pouvez aussi exécuter la requête suivante pour être sûr que la colonne `expires_at`[[BR]] 
    198 soit automatiquement renseignée en fonction de la valeur de la colonne `created_at`[[BR]] 
    199 grâce à la méthode `save()` :[[BR]] 
     198Vous pouvez aussi exécuter la requête suivante pour être sûr que la colonne `expires_at` 
     199soit automatiquement renseignée en fonction de la valeur de la colonne `created_at` 
     200grâce à la méthode `save()` : 
    200201{{{ 
    201202    SELECT `position`, `created_at`, `expires_at` FROM `jobeet_job`; 
    203204== __Configuration Personnalisée__ == 
    204205 
    205 Dans la méthode `JobeetJob::save()`, nous avons figé le nombre de jours qui détermine[[BR]] 
    206 l'expiration d'un job. Il serait préférable que la valeur de 30 jours soit paramétrable.[[BR]] 
    207 symfony utilise le fichier configuration interne `~app.yml~` qui permet de définir[[BR]] 
    208 des paramètres spécifiques à l'application. Ce fichier YAML peut contenir[[BR]] 
     206Dans la méthode `JobeetJob::save()`, nous avons figé le nombre de jours qui détermine 
     207l'expiration d'un job. Il serait préférable que la valeur de 30 jours soit paramétrable. 
     208symfony utilise le fichier configuration interne `~app.yml~` qui permet de définir 
     209des paramètres spécifiques à l'application. Ce fichier YAML peut contenir 
    209210n'importe quel paramètre nécessaire : 
    210211{{{ 
    213214      active_days: 30 
    214215}}} 
    215 Dans l'application, ces paramètres sont disponibles à travers la classe globale[[BR]] 
     216Dans l'application, ces paramètres sont disponibles à travers la classe globale 
    216217`~sfConfig~` : 
    217218{{{ 
    219220    sfConfig::get('app_active_days') 
    220221}}} 
    221 Les paramètres utilisent le préfixe `app_` car la classe `sfConfig` fournit[[BR]] 
    222 également des accès aux paramètres symfony que nous verrons plus tard.[[BR]] 
     222Les paramètres utilisent le préfixe `app_` car la classe `sfConfig` fournit 
     223également des accès aux paramètres symfony que nous verrons plus tard. 
    223224 
    224225Mettez le code à jour pour prendre en compte ce nouveau paramètre : 
    236237    } 
    237238}}} 
    238 Le fichier de configuration `app.yml` est un bon moyen de centraliser les paramètres[[BR]] 
    239 globaux de votre application.[[BR]][[BR]] 
    240  
    241 Pour finir, si vous avez besoin de définir des paramètres étendus, il suffit de[[BR]] 
    242 créer un nouveau fichier `app.yml` dans le répertoire `config` à la racine de[[BR]] 
    243 votre projet symfony.[[BR]][[BR]] 
     239Le fichier de configuration `app.yml` est un bon moyen de centraliser les paramètres 
     240globaux de votre application. 
     241 
     242Pour finir, si vous avez besoin de définir des paramètres étendus, il suffit de 
     243créer un nouveau fichier `app.yml` dans le répertoire `config` à la racine de 
     244votre projet symfony. 
    244245 
    245246== __Refactoring__ == 
    246247 
    247 Bien que notre code fonctionne correctement, il n'est pas encore parfait.[[BR]] 
    248 Etes-vous capable de repérer le problème ?[[BR]][[BR]] 
    249  
    250 Le code `Criteria` n'appartient pas à l'action ( couche Controlleur ), mais à la[[BR]] 
    251 couche Modèle. Dans le modèle ~MVC~, le Modèle définit toute la ~logique métier~,[[BR]] 
    252 et le Controlleur récupère les données du Modèle. Etant donné que le code renvoie[[BR]] 
    253 une collection de jobs, déplaçons le dans la classe `JobeetJobPeer` et créons la[[BR]] 
     248Bien que notre code fonctionne correctement, il n'est pas encore parfait. 
     249Etes-vous capable de repérer le problème ? 
     250 
     251Le code `Criteria` n'appartient pas à l'action ( couche Controlleur ), mais à la 
     252couche Modèle. Dans le modèle ~MVC~, le Modèle définit toute la ~logique métier~, 
     253et le Controlleur récupère les données du Modèle. Etant donné que le code renvoie 
     254une collection de jobs, déplaçons le dans la classe `JobeetJobPeer` et créons la 
    254255méthode `getActiveJobs()` : 
    255256{{{ 
    267268    } 
    268269}}} 
    269 Il est maintenant possible de récupérer les jobs actifs grâce à cette méthode dans[[BR]] 
     270Il est maintenant possible de récupérer les jobs actifs grâce à cette méthode dans 
    270271les actions : 
    271272{{{ 
    276277    } 
    277278}}} 
    278 La refactorisation présente plusieurs avantages :[[BR]][[BR]] 
    279  
    280  * Le code pour récupérer les jobs actifs se trouve dans la Modèle, là ou est sa place[[BR]] 
    281  * Le code du Controlleur est plus lisible[[BR]] 
    282  * La méthode `getActiveJobs()` est réutilisable ( dans une autre action par exemple )[[BR]] 
    283  * Le code du modèle peut être testé indépendemment[[BR]] 
     279La refactorisation présente plusieurs avantages : 
     280 
     281 * Le code pour récupérer les jobs actifs se trouve dans la Modèle, là ou est sa place 
     282 * Le code du Controlleur est plus lisible 
     283 * La méthode `getActiveJobs()` est réutilisable ( dans une autre action par exemple ) 
     284 * Le code du modèle peut être testé indépendemment 
    284285 
    285286Récupérons les jobs grâce à la colonne `expires_at` : 
    295296    } 
    296297}}} 
    297 La méthode `addDescendingOrderByColumn()` ajoute une clauser `ORDER BY` à la requête.[[BR]] 
    298 Il existe aussi la méthode `addAscendingOrderByColumn()`.[[BR]][[BR]] 
     298La méthode `addDescendingOrderByColumn()` ajoute une clauser `ORDER BY` à la requête. 
     299Il existe aussi la méthode `addAscendingOrderByColumn()`. 
    299300 
    300301== __Catégories en page d'accueil__ == 
    301302 
    302 Conditions du 2ème jour :[[BR]][[BR]] 
     303Conditions du 2ème jour : 
    303304 
    304305  === "Les jobs sont affichés par catégorie et par leur date de publication ( les nouveaux jobs en tête de liste." === 
    305306 
    306 Jusqu'à présent, nous n'avons pas pris en compte la catégorie associée aux jobs.[[BR]] 
    307 Afin d'afficher les jobs par catégorie, nous allons d'abord récupérer toutes les[[BR]] 
    308 catégories asscoiées à au moins un job.[[BR]][[BR]] 
     307Jusqu'à présent, nous n'avons pas pris en compte la catégorie associée aux jobs. 
     308Afin d'afficher les jobs par catégorie, nous allons d'abord récupérer toutes les 
     309catégories associées à au moins un job. 
    309310 
    310311Editer la classe `JobeetCategoryPeer` et ajouter la méthode `getWithJobs()` : 
    325326    } 
    326327}}} 
    327 La méthode `Criteria::addJoin()` ajoute une clause `~JOIN~` à la requête. Par défaut,[[BR]] 
    328 la condition join est ajoutée à la clause `WHERE`. Vous pouvez également modifier[[BR]] 
    329 l'opérateur join en ajoutant un troisième argument (`Criteria::LEFT_JOIN`,[[BR]] 
    330 `Criteria::RIGHT_JOIN`, et `Criteria::INNER_JOIN`).[[BR]] 
     328La méthode `Criteria::addJoin()` ajoute une clause `~JOIN~` à la requête. Par défaut, 
     329la condition join est ajoutée à la clause `WHERE`. Vous pouvez également modifier 
     330l'opérateur join en ajoutant un troisième argument (`Criteria::LEFT_JOIN`, 
     331`Criteria::RIGHT_JOIN`, et `Criteria::INNER_JOIN`). 
    331332 
    332333Modifiez l'action `index` en conséquence : 
    339340    } 
    340341}}} 
    341 Dans le template, nous devons rechercher les jobs actifs dans chaque catégorie et[[BR]] 
     342Dans le template, nous devons rechercher les jobs actifs dans chaque catégorie et 
    342343les afficher. 
    343344{{{ 
    375376    </div> 
    376377}}} 
    377 >>'''__NOTE__'''[[BR]] 
    378 >Pour afficher le nom d'une catégorie, nous utilisons `echo $category` dans le template.[[BR]] 
    379 >Ca vous paraît bizarre ? `$category` n'est pas un objet, comment peut-on afficher[[BR]] 
    380 >le nom de la catégorie avec un `echo`. La réponse se trouve au jour 3 quand nous[[BR]] 
    381 >avons défini la méthode magique `__toString()` pour toutes les classes du modèle.[[BR]] 
    382  
    383 Nous devons ajouter la méthode `getActiveJobs()` à la classe `JobeetCategory` qui[[BR]] 
     378>>'''__NOTE__''' 
     379>Pour afficher le nom d'une catégorie, nous utilisons `echo $category` dans le template. 
     380>Ca vous paraît bizarre ? `$category` n'est pas un objet, comment peut-on afficher 
     381>le nom de la catégorie avec un `echo`. La réponse se trouve au jour 3 quand nous 
     382>avons défini la méthode magique `__toString()` pour toutes les classes du modèle. 
     383 
     384Nous devons ajouter la méthode `getActiveJobs()` à la classe `JobeetCategory` qui 
    384385retoune les jobs actifs pour l'objet catégorie : 
    385386{{{ 
    394395    } 
    395396}}} 
    396 Dans l'appel `add()`, nous avons omis le troisième argument car la valeur par[[BR]] 
    397 défaut est `Criteria::EQUAL`.[[BR]][[BR]] 
    398  
    399 La méthode `JobeetCategory::getActiveJobs()` utilise la méthode `JobeetJobPeer::getActiveJobs()`[[BR]] 
    400 pour rechercher les jobs actifs de la catégorie donnée.[[BR]][[BR]] 
    401  
    402 A l'appel de `JobeetJobPeer::getActiveJobs()`, nous voulons restreindre la condition[[BR]] 
    403 autrement qu'en fournissant uniquement une catégorie. Au lieu de passer l'objet[[BR]] 
    404 catégorie, nous avons décidé de passer un objet `Criteria` qui est la meilleure[[BR]] 
    405 solution pour encapsuler une condition générique.[[BR]][[BR]] 
    406  
    407 Pour ce faire, il faut fusionner cet argument `Criteria` avec les criteria de la[[BR]] 
     397Dans l'appel `add()`, nous avons omis le troisième argument car la valeur par 
     398défaut est `Criteria::EQUAL`. 
     399 
     400La méthode `JobeetCategory::getActiveJobs()` utilise la méthode `JobeetJobPeer::getActiveJobs()` 
     401pour rechercher les jobs actifs de la catégorie donnée. 
     402 
     403A l'appel de `JobeetJobPeer::getActiveJobs()`, nous voulons restreindre la condition 
     404autrement qu'en fournissant uniquement une catégorie. Au lieu de passer l'objet 
     405catégorie, nous avons décidé de passer un objet `Criteria` qui est la meilleure 
     406solution pour encapsuler une condition générique. 
     407 
     408Pour ce faire, il faut fusionner cet argument `Criteria` avec les criteria de la 
    408409méthode`getActiveJobs()`. `Criteria` étant un objet, ce sera simple : 
    409410{{{ 
    425426== __Limiter les résultats__ == 
    426427 
    427 Il reste encore une condition à implémenter pour la liste des jobs en page d'accueil :[[BR]] 
     428Il reste encore une condition à implémenter pour la liste des jobs en page d'accueil : 
    428429 
    429430  === "Chaque catégorie doit afficher les 10 premiers jobs et un lien doit permettre d'afficher tous les jobs d'une catégorie choisie." === 
    442443    } 
    443444}}} 
    444 La clause `~LIMIT~` est figée dans le code du Modèle, mais il est préférable de[[BR]] 
    445 pouvoir configurer cette valeur. Modifiez le template pour utiliser le nombre[[BR]] 
     445La clause `~LIMIT~` est figée dans le code du Modèle, mais il est préférable de 
     446pouvoir configurer cette valeur. Modifiez le template pour utiliser le nombre 
    446447maximum de jobs configuré dans `app.yml` : 
    447448{{{ 
    461462== __Fixtures dynamiques__ == 
    462463 
    463 A moins de passer la valeur `max_jobs_on_homepage` à une, vous ne verrez aucune[[BR]] 
    464 différence. Nous devons ajouter des jobs ans le fichier ~fixture~s. Evidemment,[[BR]] 
    465 vous pouvez faire 20, 30, ... copier/coller des jobs existants mais il y a une[[BR]] 
    466 meilleure solution. La duplication n'est pas une bonne méthode, même pour les fichiers[[BR]] 
    467 fixture.[[BR]][[BR]] 
    468  
    469 symfony à la rescousse ! Dans symfony, les fichiers ~YAML~ peuvent contenir du code[[BR]] 
    470 PHP qui sera évalué juste avant l'analyse du fichier. Editez le fichier fixture[[BR]] 
     464A moins de passer la valeur `max_jobs_on_homepage` à une, vous ne verrez aucune 
     465différence. Nous devons ajouter des jobs ans le fichier ~fixture~s. Evidemment, 
     466vous pouvez faire 20, 30, ... copier/coller des jobs existants mais il y a une 
     467meilleure solution. La duplication n'est pas une bonne méthode, même pour les fichiers 
     468fixture. 
     469 
     470symfony à la rescousse ! Dans symfony, les fichiers ~YAML~ peuvent contenir du code 
     471PHP qui sera évalué juste avant l'analyse du fichier. Editez le fichier fixture 
    471472`020_jobs.yml` et ajoutez le code suivant à la fin : 
    472473{{{ 
    490491    <?php endfor; ?> 
    491492}}} 
    492 Attention ! L'analyseur de YAML n'aime pas les erreurs de formatage. Gardez bien[[BR]] 
    493 à l'esprit les conseils suivants si vous ajoutez du code PHP dans un fichier YAML :[[BR]][[BR]] 
    494  
    495  * `<?php ?>` doit toujours commençer une ligne ou être intégré dans une valeur.[[BR]] 
    496  * Si `<?php ?>` fint une ligne, vous devez indiquer clairement une nouvelle ligne ("\n").[[BR]] 
    497  
    498 Rechargez les données fixtures avec `propel:data-load` et vérifiez que seulement[[BR]] 
    499 `10` jobs soient affichés en page d'accueil pour la catégorie `Programming`.[[BR]] 
    500 Dans la capture d'écran suivante, nous avons modifié le nombre maximum de jobs sur[[BR]] 
     493Attention ! L'analyseur de YAML n'aime pas les erreurs de formatage. Gardez bien 
     494à l'esprit les conseils suivants si vous ajoutez du code PHP dans un fichier YAML : 
     495 
     496 * `<?php ?>` doit toujours commençer une ligne ou être intégré dans une valeur. 
     497 * Si `<?php ?>` fint une ligne, vous devez indiquer clairement une nouvelle ligne ("\n"). 
     498 
     499Rechargez les données fixtures avec `propel:data-load` et vérifiez que seulement 
     500`10` jobs soient affichés en page d'accueil pour la catégorie `Programming`. 
     501Dans la capture d'écran suivante, nous avons modifié le nombre maximum de jobs sur 
    5015025 pour avoir une image de taille raisonnable : 
    502503 
    505506== __Sécuriser la page Job__ == 
    506507 
    507 Même si vous connaissez l'URL d'un job qui a expiré, il ne doit plus être possible[[BR]] 
    508 d'y accéder. Essayez l'URL d'un job expiré ( remplaçer l'`id` par l'`id`[[BR]] 
    509 correspondant dans la base de donnée - `SELECT id, token FROM jobeet_job WHERE[[BR]] 
     508Même si vous connaissez l'URL d'un job qui a expiré, il ne doit plus être possible 
     509d'y accéder. Essayez l'URL d'un job expiré ( remplaçer l'`id` par l'`id` 
     510correspondant dans la base de donnée - `SELECT id, token FROM jobeet_job WHERE 
    510511expires_at < NOW()` ) : 
    511512{{{ 
    512513    /frontend_dev.php/job/sensio-labs/paris-france/ID/web-developer-expired 
    513514}}} 
    514 Au lieu d'afficher le job, nous devons rediriger l'utilisateur vers une erreur 404.[[BR]] 
    515 Mais comment faire alors que le job est recherché automatiquement par la route ?[[BR]][[BR]] 
    516  
    517 Par défaut,`~sfPropelRoute~` utilise la méthode standard `doSelectOne()` pour[[BR]] 
    518 rechercher l'objet. Mais il est possible de le modifier en indiquant une[[BR]] 
     515Au lieu d'afficher le job, nous devons rediriger l'utilisateur vers une erreur 404. 
     516Mais comment faire alors que le job est recherché automatiquement par la route ? 
     517 
     518Par défaut,`~sfPropelRoute~` utilise la méthode standard `doSelectOne()` pour 
     519rechercher l'objet. Mais il est possible de le modifier en indiquant une 
    519520`~method_for_criteria~` dans la configuration de la route : 
    520521{{{ 
    548549    } 
    549550}}} 
    550 Maintenant, si vous essayez d'accéder à un job qui a expiré, vous serez redirigé[[BR]] 
     551Maintenant, si vous essayez d'accéder à un job qui a expiré, vous serez redirigé 
    551552vers une page d'erreur 404. 
    552553 
    555556== __Lien vers la page des Catégories__ == 
    556557 
    557 A présent, nous allons ajouter un lien vers la catégorie et créer la page de cette[[BR]] 
    558 catégorie.[[BR]] 
    559  
    560 Une minute. L'heure n'est pas encore écoulée et nous n'avons pas beaucoup travaillé.[[BR]] 
    561 En fait, vous avez tout le temps nécessaire pour mettre en pratique tout ce que[[BR]] 
    562 nous avons déjà appris et implémenter cette fonction par vous-même. Vous pourrez[[BR]] 
    563 vérifier votre travail demain.[[BR]] 
     558A présent, nous allons ajouter un lien vers la catégorie et créer la page de cette 
     559catégorie. 
     560 
     561Une minute. L'heure n'est pas encore écoulée et nous n'avons pas beaucoup travaillé. 
     562En fait, vous avez tout le temps nécessaire pour mettre en pratique tout ce que 
     563nous avons déjà appris et implémenter cette fonction par vous-même. Vous pourrez 
     564vérifier votre travail demain. 
    564565 
    565566== __A demain__ == 
    566567 
    567 Travaillez votre projet Jobeet. N'hésitez pas à abuser de la documentation en ligne[[BR]] 
     568Travaillez votre projet Jobeet. N'hésitez pas à abuser de la documentation en ligne 
    568569[http://www.symfony-project.org/api/1_2/ API documentation] et de toutes les  
    569 ressources [http://www.symfony-project.org/doc/1_2/ documentation] disponibles[[BR]] 
    570 sur le site pour vous aider. On se retrouve demain pour découvrir l'implémentation[[BR]] 
    571 des catégories.[[BR]][[BR]] 
     570ressources [http://www.symfony-project.org/doc/1_2/ documentation] disponibles 
     571sur le site pour vous aider. On se retrouve demain pour découvrir l'implémentation 
     572des catégories. 
    572573 
    573574Bonne chance ! 
     575{{{ 
     576#!html 
     577</div> 
     578}}}