Author: forresst Date: 2010-01-28 13:57:00 +0100 (Thu, 28 Jan 2010) New Revision: 27276
Added: doc/branches/1.2/jobeet/fr/18.txt Log: [doc-fr][1.2] Add doc in french, jobeet/17 rev:en/20720 Added: doc/branches/1.2/jobeet/fr/18.txt =================================================================== --- doc/branches/1.2/jobeet/fr/18.txt (rev 0) +++ doc/branches/1.2/jobeet/fr/18.txt 2010-01-28 12:57:00 UTC (rev 27276) @@ -0,0 +1,305 @@ +Jour 18 : ~AJAX~ +================ + +Hier, nous avons mis en place un moteur de recherche très puissant pour Jobeet, +grâce à la librairie Lucene Zend. + +Aujourd'hui, pour renforcer la réactivité du moteur de recherche, nous tirerons +profit d'[AJAX](http://fr.wikipedia.org/wiki/Asynchronous_JavaScript_and_XML) pour +rendre le moteur de recherche plus vivant. + +Comme le formulaire doit travailler avec et sans l'activation du Javascript, la fonction +de recherche en direct sera mise en œuvre en utilisant du +[Javascript discret](http://fr.wikipedia.org/wiki/Javascript_discret). +L'~utilisation discrète|Javascript discret~ permet aussi une meilleure séparation +des préoccupations dans le code client entre le HTML, les CSS, le JavaScript et les +comportements. + +Installation de ~jQuery~ +------------------------ + +Au lieu de réinventer la roue et de gérer les nombreuses différences entre +les navigateurs, nous allons utiliser une bibliothèque JavaScript : jQuery. +Le framework Symfony est lui-même agnostique et peut fonctionner avec n'importe +quelle bibliothèque JavaScript. + +Accéder au site [jQuery](http://jquery.com/), téléchargez la dernière version +et mettez le fichier `.js` sous `web/js/`. + +Inclusion de jQuery +------------------- + +Comme nous aurons besoin de jQuery sur toutes les pages, mettez à jour le layout +pour l'inclure dans `<head>`. Faites attention d'insérer la fonction ~`use_javascript()`~ +avant l'appel de `include_javascripts()` : + + [php] + <!-- apps/frontend/templates/layout.php --> + + <?php use_javascript('jquery-1.2.6.min.js') ?> + <?php include_javascripts() ?> + </head> + +Nous aurions pu inclure le fichier jQuery directement avec une balise `<script>`, +mais en utilisant le helper `use_javascript()`, cela garantit que le même fichier +JavaScript ne sera pas inclus deux fois. + +>**NOTE** +>Pour des +>[~raisons de performances|Performances~](http://developer.yahoo.com/performance/rules.html#js_bottom), +>vous pouvez aussi déplacer l'appel du helper `include_javascripts()` juste avant la fin +>de la balise `</body>`. + +Ajout des ~comportements|Comportements (JavaScript)~ +---------------------------------------------------- + +Mettre en œuvre une ~recherche en direct|Recherche en direct~ signifie que chaque fois que +l'utilisateur tape une lettre dans la boîte de recherche, un appel vers le serveur doit être +déclenché, le serveur renverra alors les informations nécessaires pour mettre à jour certaines +régions de la page sans rafraîchir toute la page. + +Au lieu d'ajouter le comportement avec un attribut HTML `on*()`, le principe essentiel +de jQuery est d'ajouter des comportements au ~DOM~ après que la page soit complètement +chargée. De cette façon, si vous désactivez le support JavaScript dans votre navigateur, +aucun comportement est enregistrée, et le formulaire fonctionne toujours comme avant. + +La première étape est d'intercepter chaque fois qu'un utilisateur tape sur une touche dans le champ de recherche : + + [php] + $('#search_keywords').keyup(function(key) + { + if (this.value.length >= 3 || this.value == '') + { + // do something + } + }); + +>**NOTE** +>N'ajoutez pas le code pour l'instant, comme nous allons beaucoup le modifier. Le +>code final JavaScript sera ajoutée au layout dans la section suivante. + +Chaque fois que l'utilisateur tape sur une touche, jQuery exécute la fonction anonyme +définie dans le code ci-dessus, mais seulement si l'utilisateur a tapé plus de 3 caractères +ou s'il a enlevé quelque chose de la balise input. + +Faire un appel AJAX pour le serveur est aussi simple que d'utiliser la méthode `load()` +sur l'élément du DOM : + + [php] + $('#search_keywords').keyup(function(key) + { + if (this.value.length >= 3 || this.value == '') + { + $('#jobs').load( + $(this).parents('form').attr('action'), { query: this.value + '*' } + ); + } + }); + +Pour gérer l'~appel AJAX~, c'est la même action "normale" qui est appelée. Les changements +nécessaires dans l'action se feront dans la prochaine section. + +Enfin et surtout, si JavaScript est activé, nous voulons supprimer le +bouton de recherche : + + [php] + $('.search input[type="submit"]').hide(); + +Remontée de l'information à l'utilisateur +----------------------------------------- + +Chaque fois que vous effectuez un appel AJAX, la page ne sera pas mise à jour immédiatement. Le +navigateur attendra la ~réponse|Réponse HTTP~ du serveur avant d'actualiser la page. Dans l'intervalle, +vous devez fournir la ~remontée de l'information visuelle|Remontée de l'information visuelle~ à +l'utilisateur pour l'informer que quelque chose se passe. + +Une convention est d'afficher une icône du chargeur lors de l'appel AJAX. Mettez +à jour le layout pour ajouter l'image du chargeur et cachez le par défaut : + + [php] + <!-- apps/frontend/templates/layout.php --> + <div class="search"> + <h2>Ask for a job</h2> + <form action="<?php echo url_for('@job_search') ?>" method="get"> + <input type="text" name="query" value="<?php echo $sf_request->getParameter('query') ?>" id="search_keywords" /> + <input type="submit" value="search" /> + <img id="loader" src="/images/loader.gif" style="vertical-align: middle; display: none" /> + <div class="help"> + Enter some keywords (city, country, position, ...) + </div> + </form> + </div> + +>**NOTE** +>Le chargeur par défaut est optimisé pour le layout actuel de Jobeet. Si vous +>voulez créer le vôtre, vous trouverez une foule de services gratuits en ligne comme +>http://www.ajaxload.info/. + +Maintenant que vous avez toutes les pièces nécessaires pour faire fonctionner le HTML, +créez un fichier `search.js` qui contient le code JavaScript que nous avons écrit à ce jour : + + [php] + // web/js/search.js + $(document).ready(function() + { + $('.search input[type="submit"]').hide(); + + $('#search_keywords').keyup(function(key) + { + if (this.value.length >= 3 || this.value == '') + { + $('#loader').show(); + $('#jobs').load( + $(this).parents('form').attr('action'), + { query: this.value + '*' }, + function() { $('#loader').hide(); } + ); + } + }); + }); + +Vous devez également mettre à jour le layout pour inclure ce nouveau fichier : + + [php] + <!-- apps/frontend/templates/layout.php --> + <?php use_javascript('search.js') ?> + +>**SIDEBAR** +>JavaScript comme une action +> +>Bien que le JavaScript que nous avons écrit pour le moteur de recherche est statique, +>parfois, vous avez besoin d'appeler un peu de code PHP (pour utiliser le helper +>`url_for()` par exemple). +> +>JavaScript est juste un autre format comme le HTML, et comme cela a été vu il y a +>quelques jours, symfony rend la gestion du format assez facile. Comme le fichier JavaScript +>contiendra le comportement d'une page, vous pouvez même avoir la même URL que la page du fichier +>JavaScript, mais se terminant par `.js`. Par exemple, si vous souhaitez créer un fichier pour le +>comportement du moteur de recherche, vous pouvez modifier la route `job_search` +>comme suit et créer un Template `searchSuccess.js.php` : +> +> [yml] +> job_search: +> url: /search.:sf_format +> param: { module: job, action: search, sf_format: html } +> requirements: +> sf_format: (?:html|js) + +AJAX dans une action +-------------------- + +Si JavaScript est activé, jQuery interceptera toutes les touches tapées dans le champ +de recherche et appellera l'action `search`. Sinon, la même action même `search` est également +appelée lorsque l'utilisateur sousmet le formulaire en appuyant sur la touche "entrée" ou en +cliquant sur le bouton "recherche". + +Ainsi, l'action `search` doit maintenant déterminer si l'appel se fait via AJAX ou pas. +Chaque fois qu'une ~requête|Requête HTTP (AJAX)~ est faite avec un appel AJAX, la méthode +`isXmlHttpRequest()` de l'objet de requête retourne `true`. + +>**NOTE** +>La méthode `isXmlHttpRequest()` fonctionne avec toutes les grandes bibliothèques +>JavaScript comme Prototype, Mootools ou JQuery. + + [php] + // apps/frontend/modules/job/actions/actions.class.php + public function executeSearch(sfWebRequest $request) + { + if (!$query = $request->getParameter('query')) + { + return $this->forward('job', 'index'); + } + +<propel> + $this->jobs = JobeetJobPeer::getForLuceneQuery($query); +</propel> +<doctrine> + $this->jobs = Doctrine::getTable('JobeetJob')->getForLuceneQuery($query); +</doctrine> + + if ($request->isXmlHttpRequest()) + { + return $this->renderPartial('job/list', array('jobs' => $this->jobs)); + } + } + +Comme jQuery ne rechargera pas la page mais ne fera que remplacer l'élément du DOM +`#jobs` avec le contenu de la réponse, la page ne devrait pas être décoré par le layout. +Comme il s'agit d'un besoin commun, le layout est désactivé par défaut quand une requête +AJAX entre en jeu. + +En outre, au lieu de retourner le Template complet, il suffit de renvoyer le +contenu du partial `job/list`. La méthode `renderPartial()` utilisée dans l'action +renvoie le partial comme réponse au lieu de l'intégralité du Template. + +Si l'utilisateur supprime tous les caractères dans le champ de recherche, ou si la recherche +ne retourne aucun résultat, nous avons besoin d'afficher un message au lieu d'une page blanche. +Nous allons utiliser la méthode `renderText()` pour rendre une chaîne de test simple : + + [php] + // apps/frontend/modules/job/actions/actions.class.php + public function executeSearch(sfWebRequest $request) + { + if (!$query = $request->getParameter('query')) + { + return $this->forward('job', 'index'); + } + +<propel> + $this->jobs = JobeetJobPeer::getForLuceneQuery($query); +</propel> +<doctrine> + $this->jobs = Doctrine::getTable('JobeetJob')->getForLuceneQuery($query); +</doctrine> + + if ($request->isXmlHttpRequest()) + { + if ('*' == $query || !$this->jobs) + { + return $this->renderText('No results.'); + } + else + { + return $this->renderPartial('job/list', array('jobs' => $this->jobs)); + } + } + } + +>**TIP** +>Vous pouvez également retourner un Component dans une action en utilisant la méthode +>`renderComponent()`. + +~Test d'AJAX|Test fonctionel (AJAX)~ +------------------------------------ + +Comme le navigateur symfony ne peut pas simuler du JavaScript, vous avez besoin de l'aider +lors des tests des appels AJAX. Cela signifie principalement que vous devez ajouter manuellement +l'entête que jQuery et toutes les autres grandes bibliothèques JavaScript envoient avec la requête : + + [php] + // test/functional/frontend/jobActionsTest.php + $browser->setHttpHeader('X_REQUESTED_WITH', 'XMLHttpRequest'); + $browser-> + info('5 - Live search')-> + + get('/search?query=sens*')-> + with('response')->begin()-> + checkElement('table tr', 2)-> + end() + ; + +La méthode `setHttpHeader()` définit une ~entête HTTP|Entête HTTP~ pour la très prochaine requête +faite avec le navigateur. + +À demain +-------- + +Hier, nous avons utilisé la bibliothèque Zend Lucene pour mettre en œuvre le +moteur de recherche. Aujourd'hui, nous avons utilisé jQuery pour le rendre plus réactif. +Le framework symfony fournit tous les outils essentiels pour construire des applications MVC +avec aisance et joue aussi bien avec les autres composants. Comme toujours, essayez d'utiliser +le meilleur outil pour le travail. + +Demain, nous allons voir comment internationaliser le site Jobeet. + +__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.
