Tutorial : créer sa première application Ruby on Rails - Part2
Par jblanche le mercredi 1 août 2007, 00:07 - geeks - Lien permanent
Pour cette deuxième partie du tutorial, je vous propose de construire en quelques minutes l'interface d'administration de notre application, de lui ajouter un petit module de recherche et enfin de modifier la page d'accueil.
Commençons donc par l'interface d'administration :
Je ne reviendrais pas sur les méthodes index et show du controller admin puisque se sont exactement les mêmes que dans le controller snippets.
La méthode new est appellée au clic sur le lien new snippet, elle crée un objet snippet que la vue va nous permettre d'éditer.
La méthode create est appellée au clic sur le bouton "Create snippet", elle crée un objet snippet à partir des données passées en paramètres, le sauvegarde (insertion dans la BD) et nous redirige vers la page d'accueil du controller Admin.
def new
@snippet = Snippet.new
end
def create
@snippet = Snippet.new(params[:snippet])
@snippet.save
redirect_to admin_path
end
La méthode edit est appellée au clic sur le lien edit snippet, elle crée un objet Snippet à partir de l'id passé en paramètre pour afficher les valeurs actuelles dans l'édition.
La méthode update est appellée au clic sur le bouton "Save Snippet", elle récupère l'objet Snippet à partir de l'ID, appelle la méthode update_attributes avec les paramètres passés par la vue pour mettre à jour notre Snippet dans la base, puis elle nous redirige vers la vue "Show" du Snippet édité.
def edit
@snippet = Snippet.find(params[:id])
end
def update
@snippet = Snippet.find(params[:id])
@snippet.update_attributes(params[:snippet])
redirect_to admin_instance_path(params[:snippet])
end
La méthode Destroy supprime le snippet passé en paramètre et redirige vers la liste.
def destroy
Snippet.find(params[:id]).destroy
redirect_to admin_path
end
Passons donc aux vues :
La vue index.rhtml du controller admin ressemble à celle du controller snippets à la différence près qu'elle rajoute un lien éditer et un bouton supprimer pour chaque snippet ainsi qu'un lien pour créer un nouveau snippet en bas de page.
La vue show est la même que celle du controller snippets à un lien près.
Les vues new et edit méritent plus de précisions :
<% form_for:snippet, :url => admin_url , :method=>:post do |f| %>
<%= content_tag :label , "Titre", {:for=>"titre"} %><br />
<%= f.text_field :titre %><br />
<%= content_tag :label , "Texte", {:for=>"texte"} %><br />
<%= f.text_area :texte %><br />
<%= submit_tag "Create Snippet"%>
<%end%>
La méthode form_for demande plusieurs paramètres.
Le premier désigne le modèle en relation avec le formulaire crée (ici snippet).
Le deuxième défini l'url d'action du formulaire.
Le troisième la méthode d'envoi.
A l'intérieur du bloc crée par form_for do ... end, les éléments seront automatiquement associés au modèle correspondant (regardez le paramètre name des champs dans la source générée pour mieux comprendre).
Vous l'avez peut être remarqué, les controllers comme les vues font un usage intensif de l'utilisation des routes automatiquement créees par Rails dans le cas d'une application REST (comme admin_path, admin_instance_path...).
J'ai volontairement choisi le nom admin pour ce controller pour vous montrer une petite spécifité de Rails.
Normalement, les noms des controllers sont au pluriel (comme Snippets) mais si l'on désire nommer son controller Admin (car c'est de l'administration et non pas des administrateurs que l'on parle), on fait face à un petit problème.
En effet, Rails se charge de mettre au singulier admin et nous propose admin, ce qui n'est pas faux, mais très génant puisque des routes devant être différentes ont désormais le même nom.
On doit donc dans ce cas, spécifier dans le fichier routes.rb le singulier que l'on utilisera grâce au paramètre :singular (j'ai pour ma part choisi admin_instance).
map.resources :admin, :singular => :admin_instance
Il existe un autre problème dans notre application telle qu'elle est actuellement.
En effet, si vous editez ou créez un snippet en plaçant des retours à la ligne dans la description de celui-ci, vous remarquerez qu'à l'affichage, les retours chariots ont disparus.
La raison en est simple, à l'enregistrement, Rails enregistre le texte situé dans la textarea en utilisant le caractère de retour chariot normal à savoir "\n".
Ce comportement est parfaitement logique puisque si nous voulons fournir notre contenu dans différents formats (HTML, XML, PDF...), nous ne souhaiterions pas avoir de marquage HTML dans nos données.
Pourtant à l'affichage dans notre vue, on aimerait bien que ceux-ci soient interprétés et traduit en langage HTML. Rails fournit un moyen très simple d'y parvenir en utilisant la méthode simple_format d'ActionView.
Celle-ci remplacera par exemple les sauts de ligne par des <br /> et les doubles sauts de lignes par des paragraphes(<p> </p>)
On aura donc dans nos vues quelquechose comme :
<%= content_tag :p, simple_format(@snippet.texte) %>
Pour poursuivre cette deuxième partie du tutorial, je vous propose de commencer la mise en place d'un moteur de recherche très simple pour nos snippets.
Grâce à ce moteur, je souhaiterais afficher les snippets dont le titre ou le texte contient la recherche faite par l'utilisateur.
Commençons donc par créer la méthode associée dans notre modèle :
def self.find_by_titre_or_texte(search)
search ||= ""
self.find(:all,:conditions => ["titre ILIKE (?) OR texte ilike (?)","%"+search+"%","%"+search+"%"],:order => "id desc")
end
Le nom de la méthode est préfixé par self car celle-ci est une méthode du modèle Snippet et non pas une méthode applicable à une instance d'un Snippet.
La première ligne correspond à dire search = search si search existe et la chaine vide "" dans le cas contraire.
La méthode est assez triviale, elle effectue un find avec les paramètres all et conditions.
Le premier indique de récupérer tous les enregistrements (et non un seul).
Le deuxième défini les conditions de la requête, on y demande donc de vérifier si le titre ou le texte IS LIKE "%search%".
En SQL, les % désignent n'importe quelle chaîne de caractère et le I de ILIKE indique de ne pas tenir compte de la casse.
Cette requête sera donc traduit en :
SELECT * FROM snippets WHERE titre ILIKE '%search%' or texte ILIKE '%search%' ;
Dans notre controller snippets, la méthode qui nous sert à afficher une liste de snippets est la méthode index.
Nous allons donc lui passer un paramètre search pour lui indiquer de n'afficher que les résultant correspondant à notre recherche.
Le paramètre search vaut par défaut la chaîne vide. De cette façon, la recherche donnera des conditions comme :
titre ILIKE '%%' ce qui est vrai pour tous les titres.
Les paramères en GET sont passés en ajoutant ?param1=value1¶m2=value2¶m3=value3 à l'adresse demandée.
Nous appelerons donc snippets?search=rails pour récupérer tous les snippets traitant de rails.
(ex : http://depot.blogbangbang.com/snippets?search=rails).
J'ai également ajouté un formulaire de recherche simpliste dans le layout afin de pouvoir faire une recherche de partout.
<% form_tag'/snippets', :method=>:get do -%>Celui ci appelle donc l'URL snippets en get, et le paramètre de recherche est nommé search comme le paramètre récupéré dans notre controller.
<%= text_field_tag 'search' %>
<%= submit_tag 'Search' %>
<%end-%>
En guise de conclusion de cette deuxième partie, nous allons corriger un autre défaut de notre application.
Vous avez peut être constater qu'à la fin de la première partie, l'adresse : depot.blogbangbang.com amenait à la page d'accueil d'une application RubyOnRails.
Il serait préférable de remplacer ce comportement par défaut par la page d'accueil de notre application, à savoir la page snippets.
Pour celà rien de plus simple, ne sortez surtout pas l'artillerie lourde et les ré-écriture d'URL..., rails s'occupe de tout (oui encore :-D)
Nous lui signifions une seule chose dans le fichier routes.rb
map.connect '', :controler => "snippet"
Supprimons également le fichier index.html dans le dossier public. Et voilà c'est tout ! Grâce à cette ligne, rails sait que si l'url demandé est vide (''), il doit rediriger vers le controlleur snippets qui par defaut éxécutera l'action index.
Voilà, nous arrivons au terme de ce deuxième tutorial, j'espère que vous commencez à saisir la puissance du framework RubyOnRails et le plaisir que peut avoir un développeur à coder grâce à celui-çi.
Dans le prochain épisode, nous ajouterons des tags à nos snippets afin de les trier plus facilement.
Archive pour voir l'état actuel de l'ensemble des fichiers.
Commentaires
bonjour
merci pour le tuto juste deux trucs
pourquoi mettre la méthode de recherche dans le modèle et non le contrôleur ? Cela doit avoir une relation avec le fait que dans le formulaire vous n'appelez aucune action
J'utilise url => { :action => 'search' } dans un form remote tag et cela marche bien (bcp moins bien dans un observe field mais c'st une autre histoire ...)
pourquoi ilike ?
j'utilise like en minuscules et cela marche impec
merci de vos éclaircissements
G
La méthode find_by_titre_or_texte est au même titre que les méthodes find_by_XXX générées par rails est une méthode qui ne fait que retouner un objet ActiveRecord et ne participe pas au traitement de celui ci, elle est donc purement reliée à la Base de données donc dans le modèle.
Le formulaire a bien une action, celle ci est définie par l'URL et la méthode, ce qui nous donne ici /snippets en get qui correspond en REST à la méthode index du controller snippets (qui à été modifiée pour prende en compte la recherche).
Si vous regardez le code de la dernière version, vous constaterez d'ailleur que cette méthode à été modifié et remplacée (pour plus de clarté) par une méthode search à part qui fonctionne comme la méthode tagged de la partie 3 de ce tutorial.
LE I de ILIKE est là pour ne pas tenir compte de la casse.
Merci pour ce retour, la suite arrive bientôt
Je viens d'aller faire un tour sur le site de ta société, et une entreprise qui met en avant les standards, l'accessibilité et les logiciels libres, c'est du tout bon
Il semblerai que dans les nouvelles versions, pour mapper correctement la racine il faille utiliser map.root :controller => "snippets" à la place de map.connect '', :controler => "snippet" (celui-ci génère une erreur "Illegal route")
Fil des commentaires de ce billet