Author: forresst Date: 2010-01-19 18:14:06 +0100 (Tue, 19 Jan 2010) New Revision: 26890
Added: doc/branches/1.4/jobeet/fr/11.markdown Log: [doc-fr][1.4] Add doc in french, jobeet/11 rev:en/24847 Added: doc/branches/1.4/jobeet/fr/11.markdown =================================================================== --- doc/branches/1.4/jobeet/fr/11.markdown (rev 0) +++ doc/branches/1.4/jobeet/fr/11.markdown 2010-01-19 17:14:06 UTC (rev 26890) @@ -0,0 +1,826 @@ +Jour 11 : Testez votre formulaire +================================= + +Hier, nous avons créé notre premier formulaire avec symfony. Les gens sont maintenant en mesure de +publier un nouvel emploi dans Jobeet mais nous avons manqué de temps pour ajouter quelques tests. + +C'est ce que nous ferons aujourd'hui. En chemin, nous en apprendrons également plus sur le +framework de formulaire. + +>**SIDEBAR** +>Utilisation du framework de formulaire sans symfony +> +>Les composants du framework symfony sont assez ~découplés|Découplage~. Cela signifie que la +>plupart d'entre eux peuvent être utilisés sans utiliser l'ensemble du framework MVC. C'est le cas +>pour le framework de formulaire, qui n'a pas de dépendance sur symfony. Vous pouvez l'utiliser dans +>n'importe quelle application PHP en prenant les répertoires `lib/form/`, `lib/widgets/` et +>`lib/validators/`. +> +>Un autre composant réutilisable est le framework de routage. Copiez le répertoire +>`lib/routing/` dans votre projet non-symfony, et profitez de jolies URL gratuitement. +> +>Les composants qui sont indépendants de symfony forment la +>**~plateforme de symfony|Plateforme de symfony~**: +> +> + +Soumission du formulaire +------------------------ + +Ouvrons le fichier `jobActionsTest` pour ajouter des ~test|Test~s fonctionnels à la +création d'emploi et au processus de validation. + +A la fin du fichier, ajoutez le code suivant pour obtenir la page de création d'emploi : + + [php] + // test/functional/frontend/jobActionsTest.php + $browser->info('3 - Post a Job page')-> + info(' 3.1 - Submit a Job')-> + + get('/job/new')-> + with('request')->begin()-> + isParameter('module', 'job')-> + isParameter('action', 'new')-> + end() + ; + +Nous avons déjà utilisé la méthode `click()` pour simuler des clics sur les liens. La +même méthode `click()` peut être utilisée pour soumettre un ~formulaire|Formulaires~. +Pour un formulaire, vous pouvez transmettre les valeurs à soumettre pour chaque champ en +deuxième argument de la méthode. Comme un vrai navigateur, l'objet navigateur va fusionner +les valeurs par défaut du formulaire avec les valeurs soumises. + +Mais pour passer les valeurs des champs, nous avons besoin de connaître leurs noms. Si +vous ouvrez le code source ou utilisez la fonctionnalité "Formulaires > Afficher les détails +du formulaire" de la barre d'outils web de Firefox, vous verrez que le nom du champ `company` +est `jobeet_job[company]`. + +>**NOTE** +>Lorsque PHP rencontre un champ de saisie avec un nom comme `jobeet_job[company]`, il le +>convertit automatiquement en un tableau nommé `jobeet_job`. + +Pour rendre les choses un peu plus propre, nous allons changer le format de `job[%s]` +en ajoutant le code suivant à la fin de la méthode `configure()` de +`JobeetJobForm` : + + [php] +<propel> + // lib/form/JobeetJobForm.class.php +</propel> +<doctrine> + // lib/form/doctrine/JobeetJobForm.class.php +</doctrine> + $this->widgetSchema->setNameFormat('job[%s]'); + +Après ce changement, le nom de `company` devrait être `job[company]` dans votre +navigateur. Il est maintenant temps de cliquer sur le bouton "Preview your job" et +de passer les valeurs valides au formulaire : + + [php] + // test/functional/frontend/jobActionsTest.php + $browser->info('3 - Post a Job page')-> + info(' 3.1 - Submit a Job')-> + + get('/job/new')-> + with('request')->begin()-> + isParameter('module', 'job')-> + isParameter('action', 'new')-> + end()-> + + click('Preview your job', array('job' => array( + 'company' => 'Sensio Labs', + 'url' => 'http://www.sensio.com/', + 'logo' => sfConfig::get('sf_upload_dir').'/jobs/sensio-labs.gif', + 'position' => 'Developer', + 'location' => 'Atlanta, USA', + 'description' => 'You will work with symfony to develop websites for our customers.', + 'how_to_apply' => 'Send me an email', + 'email' => '[email protected]', + 'is_public' => false, + )))-> + + with('request')->begin()-> + isParameter('module', 'job')-> + isParameter('action', 'create')-> + end() + ; + +Le navigateur simule aussi des ~téléchargements de fichier|Téléchargements de fichier~, +si vous passez le chemin absolu du fichier à télécharger. + +Après la soumission du formulaire, nous avons vérifié que l'action exécutée est `create`. + +Le testeur du formulaire +------------------------ + +Le formulaire que nous avons soumis doit être valide. Vous pouvez tester cela en +utilisant le **testeur de formulaire** : + + [php] + with('form')->begin()-> + hasErrors(false)-> + end()-> + +Le testeur de formulaire dispose de plusieurs méthodes pour tester l'état du formulaire +actuel, comme les erreurs. + +Si vous commettez une erreur lors de test, et que ce dernier ne passe pas, vous pouvez utiliser +l'instruction `with('response')->~debug|Debug~()` que nous avons vu pendant le jour 9. Mais il +vous faudra plonger dans le code HTML généré pour vérifier les messages d'erreur. Ce n'est +pas vraiment commode. Le testeur de formulaire fournit également une méthode `debug()` qui +renvoie l'état du formulaire et de tous les messages d'erreur qui lui sont associés : + + [php] + with('form')->debug() + +Test de redirection +------------------- + +Comme le formulaire est valide, l'emploi aurait dû être créé et l'utilisateur +~redirigé|Redirection (Test)~ vers la page `show` : + + [php] + with('response')->isRedirected()-> + followRedirect()-> + + with('request')->begin()-> + isParameter('module', 'job')-> + isParameter('action', 'show')-> + end()-> + +Le `isRedirected()` teste si la page a été redirigée et la méthode +`followRedirect()` suit la redirection. + +>**NOTE** +>La classe du navigateur ne suit pas les redirections automatiquement car vous voudrez +>peut-être introspecter les objets avant la redirection. + +Le testeur de ##ORM## +--------------------- + +Finalement, nous voulons tester que l'emploi a été créé dans la base de données et +vérifier que la colonne `is_activated` est mise à `false` car l'utilisateur ne l'a pas +encore publié. + +Cela peut se faire assez facilement en utilisant un autre ~testeur|Testeurs~, le +**testeur ##ORM##**. Comme le testeur ##ORM## n'est pas enregistré par défaut, +ajoutons-le maintenant : + +<propel> + [php] + $browser->setTester('propel', 'sfTesterPropel'); +</propel> +<doctrine> + [php] + $browser->setTester('doctrine', 'sfTesterDoctrine'); +</doctrine> + +Le testeur ##ORM## fournit la méthode `check()` pour vérifier qu'un ou plusieurs +objets dans la base de données correspondent à vos critères passée en argument. + + [php] +<propel> + with('propel')->begin()-> +</propel> +<doctrine> + with('doctrine')->begin()-> +</doctrine> + check('JobeetJob', array( + 'location' => 'Atlanta, USA', + 'is_activated' => false, + 'is_public' => false, + ))-> + end() + +<propel> +Les critères peuvent être un tableau de valeurs comme ci-dessus, ou une instance +`Criteria` pour les queries les plus complexes. Vous pouvez tester l'existence +d'objets correspondants aux critères avec un booléen en troisième argument (la valeur par +défaut est `true`), ou le nombre d'objets correspondants en passant un nombre entier. +</propel> +<doctrine> +Les critères peuvent être un tableau de valeurs comme ci-dessus, ou une instance +`Doctrine_Query` pour les queries les plus complexes. Vous pouvez tester l'existence +d'objets correspondants aux critères avec un booléen en troisième argument (la valeur par +défaut est `true`), ou le nombre d'objets correspondants en passant un nombre entier. +</doctrine> + +Test pour les ~Erreurs~ +----------------------- + +La création du ~formulaire|Formulaires~ d'emploi fonctionne comme prévu lorsque nous soumettons des valeurs valides. +Ajoutons donc un test pour vérifier le comportement lorsque nous soumettons des données non valides : + + [php] + $browser-> + info(' 3.2 - Submit a Job with invalid values')-> + + get('/job/new')-> + click('Preview your job', array('job' => array( + 'company' => 'Sensio Labs', + 'position' => 'Developer', + 'location' => 'Atlanta, USA', + 'email' => 'not.an.email', + )))-> + + with('form')->begin()-> + hasErrors(3)-> + isError('description', 'required')-> + isError('how_to_apply', 'required')-> + isError('email', 'invalid')-> + end() + ; + +La méthode `hasErrors()` permet de tester le nombre d'erreurs si un entier est passé. +La méthode `isError()` teste le code erreur pour un champ donné. + +>**TIP** +>Dans les tests que nous avons écrit pour la soumission de données non-valides , nous +>n'avons pas re-testé de nouveau le formulaire en entier. Nous avons seulement ajouté des +>tests pour des choses spécifiques. + +Vous pouvez également tester le code ~HTML~ généré afin de vérifier qu'il contient +les messages d'erreur, mais il n'est pas nécessaire dans notre cas car nous n'avons +pas personnalisé la présentation de formulaire. + +Maintenant, nous avons besoin de tester la barre d'administrateur trouvée sur la page de +prévisualisation d'emploi. Lorsqu'un emploi n'a pas encore été activé, vous pouvez éditer, supprimer, +ou publier l'emploi. Pour tester ces trois liens, nous aurons besoin de créer d'abord un emploi. +Mais c'est beaucoup de copier et coller. Comme je n'aime pas gaspiller des e-arbres, nous allons ajouter +une méthode de création d'emploi dans la classe `JobeetTestFunctional` : + + [php] + // lib/test/JobeetTestFunctional.class.php + class JobeetTestFunctional extends sfTestFunctional + { + public function createJob($values = array()) + { + return $this-> + get('/job/new')-> + click('Preview your job', array('job' => array_merge(array( + 'company' => 'Sensio Labs', + 'url' => 'http://www.sensio.com/', + 'position' => 'Developer', + 'location' => 'Atlanta, USA', + 'description' => 'You will work with symfony to develop websites for our customers.', + 'how_to_apply' => 'Send me an email', + 'email' => '[email protected]', + 'is_public' => false, + ), $values)))-> + followRedirect() + ; + } + + // ... + } + +La méthode `createJob()` crée un emploi, suit la redirection et retourne le +navigateur pour ne pas casser la fluent interface. Vous pouvez également passer un +tableau de valeurs qui sera fusionnée avec certaines valeurs par défaut. + +Forcer la ~méthode HTTP~ d'un lien +---------------------------------- + +Le test du lien "Publish" est maintenant plus simple : + + [php] + $browser->info(' 3.3 - On the preview page, you can publish the job')-> + createJob(array('position' => 'FOO1'))-> + click('Publish', array(), array('method' => 'put', '_with_csrf' => true))-> + +<propel> + with('propel')->begin()-> +</propel> +<doctrine> + with('doctrine')->begin()-> +</doctrine> + check('JobeetJob', array( + 'position' => 'FOO1', + 'is_activated' => true, + ))-> + end() + ; + +Si vous vous souvenez du jour 10, le lien "Publish" a été configuré pour être +appelé avec la méthode HTTP `~PUT|PUT (Méthode HTTP)~`. Comme les navigateurs +ne comprennent pas les requêtes `PUT`, le helper `link_to()` convertit le lien +vers un formulaire avec quelques JavaScript. Comme le navigateur de test n'exécute +pas le code JavaScript, nous avons besoin de forcer la méthode à `PUT` en passant une +troisième option à la méthode `click()`. En outre, le helper `link_to()` intégre aussi +un ~jeton CSRF~ que nous avons activé par la protection CSRF durant le jour 1. L'option +`_with_csrf` simule ce jeton. + +Tester le lien "Delete" est assez similaire : + + [php] + $browser->info(' 3.4 - On the preview page, you can delete the job')-> + createJob(array('position' => 'FOO2'))-> + click('Delete', array(), array('method' => 'delete', '_with_csrf' => true))-> + +<propel> + with('propel')->begin()-> +</propel> +<doctrine> + with('doctrine')->begin()-> +</doctrine> + check('JobeetJob', array( + 'position' => 'FOO2', + ), false)-> + end() + ; + +Les tests comme protection +-------------------------- + +Quand un emploi est publié, vous ne pouvez plus le modifier. Même si le lien "Edit" +ne s'affiche plus sur la page de prévisualisation, ajoutons quelques tests de cette +exigence. + +D'abord, ajoutez un autre argument à la méthode `createJob()` pour permettre la +publication automatique de l'emploi, et créez une méthode `getJobByPosition()` qui +renvoie pour un emploi donné sa valeur pour `position` : + + [php] + // lib/test/JobeetTestFunctional.class.php + class JobeetTestFunctional extends sfTestFunctional + { + public function createJob($values = array(), $publish = false) + { + $this-> + get('/job/new')-> + click('Preview your job', array('job' => array_merge(array( + 'company' => 'Sensio Labs', + 'url' => 'http://www.sensio.com/', + 'position' => 'Developer', + 'location' => 'Atlanta, USA', + 'description' => 'You will work with symfony to develop websites for our customers.', + 'how_to_apply' => 'Send me an email', + 'email' => '[email protected]', + 'is_public' => false, + ), $values)))-> + followRedirect() + ; + + if ($publish) + { + $this-> + click('Publish', array(), array('method' => 'put', '_with_csrf' => true))-> + followRedirect() + ; + } + + return $this; + } + +<propel> + public function getJobByPosition($position) + { + $criteria = new Criteria(); + $criteria->add(JobeetJobPeer::POSITION, $position); + + return JobeetJobPeer::doSelectOne($criteria); + } +</propel> +<doctrine> + public function getJobByPosition($position) + { + $q = Doctrine_Query::create() + ->from('JobeetJob j') + ->where('j.position = ?', $position); + + return $q->fetchOne(); + } +</doctrine> + + // ... + } + +Si un emploi est publié, la page d'édition doit retourner un code erreur ~404|Erreur 404~ : + + [php] + $browser->info(' 3.5 - When a job is published, it cannot be edited anymore')-> + createJob(array('position' => 'FOO3'), true)-> + get(sprintf('/job/%s/edit', $browser->getJobByPosition('FOO3')->getToken()))-> + + with('response')->begin()-> + isStatusCode(404)-> + end() + ; + +Mais si vous exécutez les tests, vous n'aurez pas le résultat escompté, car nous +avons oublié hier d'implémenter cette mesure de ~sécurité|Sécurité~. L'écriture des +tests est aussi un excellent moyen de découvrir des bogues, car vous avez besoin de +penser à tous les ~cas limites|Cas limites~. + +Résoudre le bogue est simple puisque nous avons juste besoin de transmettre une page +404 si l'emploi est activé : + + [php] + // apps/frontend/modules/job/actions/actions.class.php + public function executeEdit(sfWebRequest $request) + { + $job = $this->getRoute()->getObject(); + $this->forward404If($job->getIsActivated()); + + $this->form = new JobeetJobForm($job); + } + +Le correctif est trivial, mais êtes-vous sûr que tout le reste fonctionne toujours +comme prévu ? Vous pouvez ouvrir votre navigateur et commencer à tester toutes les +combinaisons possibles pour accéder à la page d'édition. Mais il y a un moyen plus +simple : lancez votre suite de tests, si vous avez introduit une ~régression|Régression~, +symfony va vous le dire tout de suite. + +Retour vers le futur dans le test +--------------------------------- + +Quand un emploi expire dans moins de cinq jours, ou s'il a déjà expiré, +l'utilisateur peut étendre la validation de l'emploi pour 30 autres jours à +compter de la date actuelle. + +la date d'expiration est automatiquement réglé lorsque le travail est créé pour 30 jours dans le futur. +Le test de cette exigence dans un navigateur n'est pas facile, car la date d'expiration est automatiquement +définie dans 30 jours dans le futur lorsque l'emploi est créé. Ainsi, lors de la récupération de la page emploi, +le lien n'est pas présent pour prolonger l'emploi. Bien sûr, vous pouvez adapter la date d'expiration dans la base +de données, ou peaufiner le Template pour afficher en permanence le lien, mais c'est fastidieux et plutôt sensibles +aux erreurs. Comme vous l'avez déjà deviné, l'écriture de tests nous aidera encore une fois. + +Comme toujours, nous avons besoin d'ajouter une nouvelle route pour la première méthode `extend` : + + [yml] + # apps/frontend/config/routing.yml + job: + class: sfPropelRouteCollection + options: + model: JobeetJob + column: token + object_actions: { publish: PUT, extend: PUT } + requirements: + token: \w+ + +Puis, mettez à jour le lien "Extend" codé dans le partial `_admin` : + + [php] + <!-- apps/frontend/modules/job/templates/_admin.php --> + <?php if ($job->expiresSoon()): ?> + - <?php echo link_to('Extend', 'job_extend', $job, array('method' => 'put')) ?> for another <?php echo sfConfig::get('app_active_days') ?> days + <?php endif; ?> + +Puis, créez l'action `extend` : + + [php] + // apps/frontend/modules/job/actions/actions.class.php + public function executeExtend(sfWebRequest $request) + { + $request->checkCSRFProtection(); + + $job = $this->getRoute()->getObject(); + $this->forward404Unless($job->extend()); + +<propel> + $this->getUser()->setFlash('notice', sprintf('Your job validity has been extended until %s.', $job->getExpiresAt('m/d/Y'))); +</propel> +<doctrine> + $this->getUser()->setFlash('notice', sprintf('Your job validity has been extended until %s.', $job->getDateTimeObject('expires_at')->format('m/d/Y'))); +</doctrine> + + $this->redirect('job_show_user', $job); + } + +Comme prévu par l'action, la méthode `extend()` de `JobeetJob` retourne `true` +si l'emploi a été prolongé ou `false` dans les autres cas : + +<propel> + [php] + // lib/model/JobeetJob.php + class JobeetJob extends BaseJobeetJob + { + public function extend() + { + if (!$this->expiresSoon()) + { + return false; + } + + $this->setExpiresAt(time() + 86400 * sfConfig::get('app_active_days')); + + return $this->save(); + } + + // ... + } +</propel> +<doctrine> + [php] + // lib/model/doctrine/JobeetJob.class.php + class JobeetJob extends BaseJobeetJob + { + public function extend() + { + if (!$this->expiresSoon()) + { + return false; + } + + $this->setExpiresAt(date('Y-m-d', time() + 86400 * sfConfig::get('app_active_days'))); + + $this->save(); + + return true; + } + + // ... + } +</doctrine> + +Enfin, ajoutez un scénario de test : + + [php] + $browser->info(' 3.6 - A job validity cannot be extended before the job expires soon')-> + createJob(array('position' => 'FOO4'), true)-> + call(sprintf('/job/%s/extend', $browser->getJobByPosition('FOO4')->getToken()), 'put', array('_with_csrf' => true))-> + with('response')->begin()-> + isStatusCode(404)-> + end() + ; + + $browser->info(' 3.7 - A job validity can be extended when the job expires soon')-> + createJob(array('position' => 'FOO5'), true) + ; + + $job = $browser->getJobByPosition('FOO5'); +<propel> + $job->setExpiresAt(time()); +</propel> +<doctrine> + $job->setExpiresAt(date('Y-m-d')); +</doctrine> + $job->save(); + + $browser-> + call(sprintf('/job/%s/extend', $job->getToken()), 'put', array('_with_csrf' => true))-> + with('response')->isRedirected() + ; + +<propel> + $job->reload(); + $browser->test()->is( + $job->getExpiresAt('y/m/d'), + date('y/m/d', time() + 86400 * sfConfig::get('app_active_days')) + ); +</propel> +<doctrine> + $job->refresh(); + $browser->test()->is( + $job->getDateTimeObject('expires_at')->format('y/m/d'), + date('y/m/d', time() + 86400 * sfConfig::get('app_active_days')) + ); +</doctrine> + +Ce scénario de test introduit quelques nouveautés : + + * La méthode `call()` récupère une URL avec une méthode différente `GET` ou + `POST` +<propel> + * Après que l'emploi a été mis à jour par l'action, nous avons besoin de recharger + l'objet local avec `$job->reload()` +</propel> +<doctrine> + * Après que l'emploi a été mis à jour par l'action, nous avons besoin de recharger + l'objet local avec `$job->refresh()` +</doctrine> + * A la fin, nous utilisons l'objet incorporé `lime` directement pour tester + la nouvelle date d'expiration. + +Sécurité des formulaires +------------------------ + +### La magie de la sérialisation des formulaires ! + +Les ~formulaire|Formulaires~s ##ORM## sont très faciles à utiliser car ils automatisent +beaucoup de travail. Par exemple, la sérialisation d'un formulaire pour la base de données +est aussi simple qu'un appel à `$form->save()`. + +Mais comment ça marche ? Fondamentalement, la méthode `save()` suit les étapes +suivantes: + + * Commence une transaction (car les formulaires imbriqués ##ORM## sont tous sauvés + d'un seul coup) + * Processe les valeurs soumises (en appelant les méthodes `updateCOLUMNColumn()` si + elles existent) + * Appelle la méthode `fromArray()` de l'objet ##ORM## pour mettre à jour les valeurs de la colonne + * Sauve l'objet vers la base de données + * Commit la transaction + +### Fonctionnalités de sécurité intégrées + +La méthode `fromArray()` prend un tableau de valeurs et met à jour les valeurs des +colonnes correspondantes. Est-ce que ceci représente un problème de ~sécurité|Sécurité~ ? +Que faire si quelqu'un essaie de sousmettre une valeur pour une colonne à laquelle il n'a pas +l'autorisation ? Par exemple, puis-je forcer la colonne `token` ? + +Commençons par écrire un test pour simuler une soumission d'un emploi avec un champ `token` : + + [php] + // test/functional/frontend/jobActionsTest.php + $browser-> + get('/job/new')-> + click('Preview your job', array('job' => array( + 'token' => 'fake_token', + )))-> + + with('form')->begin()-> + hasErrors(7)-> + hasGlobalError('extra_fields')-> + end() + ; + +Lors de la soumission du formulaire, vous devez disposer d'une erreur globale `extra_fields`. +C'est parce que les formulaires par défaut ne permettent pas que des champs supplémentaires soient +présents dans les valeurs soumises. C'est aussi pourquoi tous les champs du formulaire doivent avoir +un validateur associé. + +>**TIP** +>Vous pouvez également soumettre des champs supplémentaires dans le confort de votre navigateur +>en utilisant des outils comme la barre d'outils Firefox pour les développeurs Web. + +Vous pouvez contourner cette mesure de sécurité en mettant l'option `allow_extra_fields` +à `true` : + + [php] + class MyForm extends sfForm + { + public function configure() + { + // ... + + $this->validatorSchema->setOption('allow_extra_fields', true); + } + } + +Le test doit maintenant passer mais la valeur `token` a été sortie des valeurs. +Donc, vous n'êtes toujours pas en mesure de contourner la mesure de sécurité. Mais si +vous voulez vraiment la valeur, définissez l'option `filter_extra_fields` à `false` : + + [php] + $this->validatorSchema->setOption('filter_extra_fields', false); + +>**NOTE** +>Les tests écrits dans cette section sont uniquement à des fins de démonstration. Vous +>pouvez maintenant les retirer du projet Jobeet car les tests n'ont pas besoin de valider +>des caractéristiques de symfony. + +### Protection ~XSS~ et ~CSRF~ + +Durant le jour 1, vous avez appris que la tâche `generate:app` crée une +application sécurisée par défaut. + +Premièrement, elle a activé la protection contre les attaques XSS. Cela signifie que toutes +les variables utilisées dans les Templates sont échappés par défaut. Si vous essayez de soumettre +une description d'un emploi avec certaines balises HTML à l'intérieur, vous remarquerez que lorsque +symfony rend la page des emplois, les balises HTML de la description ne sont pas interprétées, +mais rendues en texte brut. + +Puis, elle a permis la protection CSRF. Quand un jeton CSRF est fixé, tous les +formulaires intégrent un champ caché `_csrf_token`. + +>**TIP** +>La stratégie d'échapement et le secret CSRF peuvent être modifiés à tout moment +>en modifiant le fichier de ~configuration|Configuration~ `apps/frontend/config/settings.yml`. +>Comme pour le fichier `databases.yml`, les paramètres sont configurables par +>environnement : +> +> [yml] +> all: +> .settings: +> # Form security secret (CSRF protection) +> csrf_secret: Unique$ecret +> +> # Output escaping settings +> escaping_strategy: true +> escaping_method: ESC_SPECIALCHARS + +Les tâches de maintenance +------------------------- + +Même si symfony est un framework web, il est livré avec un outil de ~ligne de +commande|Ligne de commande~. Vous l'avez déjà utilisé pour créer la structure des +répertoires par défaut du projet et de l'application, mais aussi pour générer différents +fichiers pour le modèle. L'ajout d'une nouvelle ~tâche|Tâches~ est plutôt facile car les +outils utilisés par la ligne de commande de symfony sont intégrés dans un framework. + +Lorsqu'un utilisateur crée un emploi, il doit l'activer pour le mettre en ligne. Mais sinon, +la base de données grandira avec des vieux emplois. Nous allons créer une tâche qui suppriment +de vieux emplois de la base de données. Cette tâche devra être exécuté régulièrement dans une tâche cron. + + [php] + // lib/task/JobeetCleanupTask.class.php + class JobeetCleanupTask extends sfBaseTask + { + protected function configure() + { + $this->addOptions(array( +<doctrine> + new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application', 'frontend'), +</doctrine> + new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environement', 'prod'), + new sfCommandOption('days', null, sfCommandOption::PARAMETER_REQUIRED, '', 90), + )); + + $this->namespace = 'jobeet'; + $this->name = 'cleanup'; + $this->briefDescription = 'Cleanup Jobeet database'; + + $this->detailedDescription = <<<EOF + The [jobeet:cleanup|INFO] task cleans up the Jobeet database: + + [./symfony jobeet:cleanup --env=prod --days=90|INFO] + EOF; + } + + protected function execute($arguments = array(), $options = array()) + { + $databaseManager = new sfDatabaseManager($this->configuration); + +<propel> + $nb = JobeetJobPeer::cleanup($options['days']); + $this->logSection('propel', sprintf('Removed %d stale jobs', $nb)); +</propel> +<doctrine> + $nb = Doctrine::getTable('JobeetJob')->cleanup($options['days']); + $this->logSection('doctrine', sprintf('Removed %d stale jobs', $nb)); +</doctrine> + } + } + +La configuration des tâches se fait dans la méthode `configure()`. Chaque tâche doit +avoir un nom unique (`namespace`:`name`), et peut avoir des arguments et des options. + +>**TIP** +>Parcourez les tâches intégrées de symfony (`lib/task/`) pour plus d'exemples d'utilisation. + +La tâche `jobeet:cleanup` définit deux options : `--env` et `--days` avec certaines +valeurs par défaut raisonnables. + +L'exécution de la tâche est similaire à l'exécution des autres tâches intégrées de symfony : + + $ php symfony jobeet:cleanup --days=10 --env=dev + +<propel> +Comme toujours, le code de nettoyage de la base de données a été pris +dans la classe `JobeetJobPeer` : + + [php] + // lib/model/JobeetJobPeer.php + static public function cleanup($days) + { + $criteria = new Criteria(); + $criteria->add(self::IS_ACTIVATED, false); + $criteria->add(self::CREATED_AT, time() - 86400 * $days, Criteria::LESS_THAN); + + return self::doDelete($criteria); + } + +La méthode `doDelete()` supprime les enregistrements de la base de données correspondant +à l'objet `Criteria` donné. Il peut aussi prendre un tableau de clés primaires. +</propel> +<doctrine> +Comme toujours, le code de nettoyage de la base de données a été pris +dans la classe `JobeetJobTable` : + + [php] + // lib/model/doctrine/JobeetJobTable.class.php + public function cleanup($days) + { + $q = $this->createQuery('a') + ->delete() + ->andWhere('a.is_activated = ?', 0) + ->andWhere('a.created_at < ?', date('Y-m-d', time() - 86400 * $days)); + + return $q->execute(); + } +</doctrine> + +>**NOTE** +>Les tâches symfony se comportent bien avec leur environnement, car ils renvoient +>une valeur en fonction du succès de la tâche. Vous pouvez forcer une valeur de retour +>en retournant un entier de manière explicite à la fin de la tâche. + +À demain +-------- + +Les tests sont au cœur de la philosophie et des outils de symfony . Aujourd'hui, nous +avons encore appris comment exploiter les outils symfony pour rendre le processus de +développement plus facile, plus rapide et plus important, plus sûr. + +Le framework de formulaire symfony offre beaucoup plus que de simples widgets +et validateurs : il vous donne un moyen simple de tester vos formulaires et assurer +que vos formulaires sont sécurisés par défaut. + +Notre tour des fonctionnalités de symfony ne s'achève pas aujourd'hui. Demain, nous allons +créer l'application backend pour Jobeet. La création d'une interface backend est un must pour +la plupart des projets web et Jobeet n'est pas différent. Mais comment allons-nous pouvoir +développer une telle interface en seulement une heure ? Simple, nous allons utiliser le framework +admin generator de symfony. Jusque-là, prenez garde. + +__ORM__ \ No newline at end of file
-- You received this message because you are subscribed to the Google Groups "symfony SVN" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/symfony-svn?hl=en.
