playlist Validation de commande

Publié il y a 3 mois dans la série : Rails
Nicolas Cavigneaux
Votre formateur
Nicolas Cavigneaux

A la recherche d'un langage polyvalent, j'ai fait la découverte de Ruby en 2003. J'ai donc très vite commencé à utiliser Ruby au quotidien pour des tâches diverses et variées (scripting, applications lourdes…).

Courant 2004, une vague de fraicheur est apparue avec l'arrivée de Ruby on Rails qui m'a de suite conquis. J'ai donc décidé de participer activement à la communauté (forums, patches, librairies, …). En 2010, je fais la rencontre de Martin Catty et retrouve dans sa vision la rigueur et les bonnes pratiques que j'aime mettre en place, le déclic a donc été immédiat.

Synbioz met en place des solutions robustes sur la base d'outils modernes et funs, je veux faire partie de l'aventure.

Catégories : Développement

Dans cette vidéo nous allons mettre un moyen pour l'utilisateur de valider un panier pour déclencher une commande.
Afficher le transcript complet de la vidéo

Bonjour et bienvenue dans cette vidéo consacrée à Ruby on Rails. Dans cet épisode nous allons mettre en place le système de validation du panier qui permettra aux client de procéder à l'achat du contenu de son panier.


Nous n'iront pas jusqu'à implémenter le paiement en ligne qui nous prendrait trop de temps et qui sort du cadre de la découverte de Rails.


1.1 Création du modèle Order


Une commande va tout simplement être représentée par un panier, qui contient la liste et la quantité de produits voulus, ainsi que des informations de paiement.


Créons donc le nécessaire pour la gestion de commande :

bin/rails generate scaffold Order name:string address:text email:string status:integer cart:references


On ajoute quelques contraintes sur les colonnes :

name null: false
address null: false
email null: false
status null: false, default: 0


Nous avons choisi d'utiliser un entier pour gérer le statut. On aurait pu stocker le statut courant sous forme la forme d'une chaîne mais pour ce genre de cas de figure, où la valeur doit faire partie d'une énumération, il est plus efficient d'utiliser un entier d'autant plus que Rails nous fournit les outils nécessaire à le gérer de manière transparente.


On peut jouer les migrations :

bin/rails db:migrate


1.2 Gestion du statut de la commande grâce à un enum


Modifions le modèle app/models/order.rb pour prendre en charge cette énumération :

class Order < ApplicationRecord
  enum status: [:ordered, :payed, :in_progress, :sent]
end


Ici le mot-clé enum fournit par ActiveModel permet de faire savoir au modèle que l'attribut status est une énumération dont les valeurs ne pourront être que celles définies dans le tableau passé en tant que valeur.


Si on essaie de passer autre chose, l'objet ne sera pas valide. En coulisses, Rails fait un mapping de ces noms de statuts vers un entier qui sera stocké en base. Ce mapping par défaut est tout simple, l'entier correspond à l'index de l'élément dans le tableau. ordered vaut donc 0 ici, payed vaut 1 et ainsi de suite.


Il est possible de définir explicitement le mapping si nécessaire.


1.3 Ajout de validations


On peut maintenant passer à l'ajout des validations :

class Order < ApplicationRecord
  # …
  validates :name, :address, :email, presence: true
  validates :status, inclusion: statuses.keys
end


On s'assure donc que le nom, l'adresse et l'email aient bien était remplis. On s'assure ensuite que le statut de la commande correspond bien à une des valeurs autorisé pour notre énumération. Pour ce faire, on se base sur une méthode définie automatiquement par Rails lorsqu'on a déclaré l'énumération. Cette méthode statuses est simplement le pluriel de l'attribut déclaré en tant qu'énumération. Elle retourne un objet qui représente l'ensemble des valeurs possibles et des méthodes pour obtenir une liste des clés et des valeurs.


Ici on utilise la méthode keys qui va nous retourner l'ensemble des clés valides. Exactement ce dont on a besoin pour mettre en place notre validation.


1.4 Ajout du formulaire de commande


On va maintenant permettre à l'utilisateur de valider son panier pour arriver sur la page de commande. Il nous suffit d'ajouter un bouton qui va nous rediriger vers un formulaire qu'on va créer ensuite.


On modifie donc notre partiel ~app/views/carts/cart.html.erb~ :

<%= button_to "Acheter", new_order_path, method: :get, class: "btn btn-primary" %>


Ce bouton va rediriger vers l'action new du contrôleur order, cette action devra donc avoir accès au panier pour pouvoir le manipuler et surtout afficher son contenu. Assurons nous de ça en modifiant le fichier ~app/controller/orderscontroller.rb~ :

include CurrentCart
before_action :set_cart, only: [:new, :create]


On prend un peu d'avance en s'assurant que le panier est également récupéré dans l'action create.


Il nous reste à s'assurer d'une chose, que l'utilisateur ne puisse pas se retrouver sur la page de paiement alors que son panier est vide.


On va donc ajouter un filtre sur l'action new pour rediriger l'utilisateur avec un message s'il tente d'accéder à la page avec un panier vide :

before_action :redirect_if_cart_is_empty, only: :new

# …

private

def redirect_if_cart_is_empty
  if @cart.line_items.empty?
    redirect_to root_url, notice: "Votre panier est vide"
  end
end


Grâce à ce filtre, on s'assure que la relation line_items du panier courant n'est pas vide, si elle est vide alors on redirige sur la page d'accueil avec un petit message.


1.5 Modification des tests contrôleur


Comme à l'habitude, on va modifier nos tests pour qu'ils s'assurent pour nous que notre logique applicative est bien respectée et qu'elle le restera dans le futur. On ouvre le fichier test/controllers/orders_controller_test.rb.


On y ajoute d'abord un test qui s'assure que l'accès est impossible avec un panier vide :

test "order can't be placed with an empty cart" do
  get new_order_url
  assert_redirected_to root_path
  assert_equal flash[:notice], "Votre panier est vide"
end


puis on modifie le test qui prouve que l'action est accessible dans les bonnes conditions en s'assurant que le panier n'est pas vide :

post line_items_url, params: { product_id: products(:tshirt).id }


On peut jouer nos tests pour vérifier que tout fonctionne comme attendu :

bin/rails controllers:test


Tout passe, on peut continuer.


1.6 Modification du formulaire


On va maintenant modifier le formulaire, il contient trop de choses. Le scaffold crée systématiquement un champ de formulaire par attribut qu'on lui passe, hors on ne souhaite pas laisser l'utilisateur l'identifiant de son panier ou encore l'état de la commande, cette partie sera gérée automatiquement par l'application.


On supprime donc les champs superflus dans le fichier app/views/orders/_form.html.erb.


On note au passage l'utilisation d'helpers comme form_with qui permet de créer le formulaire puis des helpers dédiés à la création des =input=s HTML comme text_field ou text_area.


Les erreurs seront aussi affichées s'il y en a.


Pour finir on va modifier un peu le markup restant pour qu'il soit plus agréable à utiliser.


1.7 Modification de l'action create du contrôleur Order


Pour que notre passage de commande soit fonctionnel, il nous reste à modifier le contrôleur pour que la commande soit associée au panier courant puis remettre le panier à zéro.

def create
  @order.cart = @cart

  respond_to do |format|
    if @order.save
      session.delete(:cart_id)
      format.html { redirect_to root_path, notice: "Commande validée" }
      # …
    else
      # …
    end
  end
end


On s'est donc assuré que la commande est rattachée au panier courant avant de tenter de l'enregistrer. Dans le cas où la commande est passée avec succès on supprime l'identifiant du panier de la session ce qui nous assure qu'on en aura un nouveau qui sera créé et définie comme panier par défaut.


Comme vous vous en doutez, il ne faut plus que le panier qui a servi pour la commande soit modifiable.


On peut maintenant tester notre formulaire de commande !


On peut aussi vérifier, via la console Rails, que notre commande a bien été enregistrée :

Order.last


On note que le statut par défaut est ordered comme prévu, on pourrait le passer à paid dès lors que le paiement est confirmé.


Dans le prochain épisode nous verrons comment gérer l'envoie de mails.


À bientôt.



9/9 dans la sérieRails