Aller au contenu principal

14-2 Panier de base lié à la session

À la différence de votre projet, le projet d'exemple Snowfall ne permet pas de s'inscrire à plusieurs événements à la fois. Cependant, faisons comme si nous avions un panier, quoique l'implémentation sera plus simple que ce que vous avez à faire dans votre projet.

En effet, lorsque l'utilisateur signifie son intérêt de s'inscrire à un événement, on le sauvegarde afin de lui permettre d'y revenir plus tard et de compléter son achat.

Pour conserver cet état, on peut utiliser le concept de Session, tel que vu d'entrée de jeu dans l'introduction. La session est similaire au localstorage dans le sens qu'elle permet de stocker des données localement dans le navigateur via un cookie, mais elle a aussi l'avantage d'être transférée automatiquement au serveur lors de chaque requête et automatiquement rendu accessible par le serveur/framework.

En effet, la session est stockée dans un cookie et ces derniers sont transmis au serveur lors de chaque requête. Le serveur décrypte le cookie quand il le reçoit, le lit et rend disponibles les informations (clé/valeurs) contenues dans ce dernier.

C'est un moyen très simple et très polyvalent de conserver un état entre le client et le serveur.

Le séquence est grosso modo la suivante:

  1. J'ajoute un item au "panier"
  2. Le serveur crée un cookie de session et assigne l'item à la clé "Panier" de la session
  3. Le cookie est transféré à chaque requête, ce qui permet au serveur d'avoir constamment l'état du panier du client, ou à tout de moins de sa session d'achat.

Définir le processus d'ajout au panier

Le processus d'ajout au panier ressemble à ceci:

  1. À partir d'une page d'événement, je clique sur le bouton Acheter un billet
  2. Le bouton fait une requête POST vers PanierController#Create (/panier), en incluant le id de l'événement dans le détail de la requête POST
  3. L'action Acheter du contrôleur ajoute à la clé panier de la session le id de l'événementé
  4. L'utilisateur est redirigé vers une vue montrant le contenu de son "panier"

Créer un contrôleur PanierController

  1. Sous le dossier Controllers du projet Mvc, ajoutez un nouveau contrôleur
  2. Nommez ce contrôleur PanierController. N'oubliez pas de définir sa route de base de contrôleur.
    Snowfall.Web.Mvc/Controllers/PanierController.cs
    [Route("[controller]")]
    public class PanierController : Controller
    {
    // GET
    public IActionResult Index()
    {
    return View();
    }
    }
  3. Supprimez l'action Index générée automatiquement et créez une action Create, pour une requête POST qui permettra d'ajouter un item au panier.
    Snowfall.Web.Mvc/Controllers/PanierController.cs
    [Route("[controller]")]
    public class PanierController : Controller
    {
    public async Task<IActionResult> Create()
    {

    }
    }
  4. Faites-en sorte que l'action Create réponde aux requêtes POST
    [HttpPost]
    public async Task<IActionResult> Create()
    {

    }
  5. Retournez simplement Ok() pour satisfaire le compilateur pour le moment.
    [HttpPost]
    public async Task<IActionResult> Create()
    {
    return Ok();
    }

Ajouter l'événement à la session d'achat (panier)

Lorsque l'utilisateur témoigne son intention de s'inscrire à un événement, on ajoutera l'événement à la session dans une clé Panier. Cette dernière ne contiendra qu'un élément, mais le concept pourrait facilement être étendu à plusieurs éléments

Lier un bouton d'achat à PanierController#Create

Pour ajouter un billet au panier et procéder à l'achat, il nous faut créer un bouton d'achat dans la vue d'un événement et lier ce dernier à l'action Create responsable de mettre l'item dans la session.

Dans la vue Show des événements, on peut ajouter un bouton quelque part dans la page pour procéder à l'achat. Par exemple, sous le libellé du nom de la ville.

img

Snowfall.Web.Mvc/Views/Evenements/Show.cshtml
<!-- ... -->

<div class="col-md-6">
<p>@Model.Description</p>
<p><strong>@Model.Prix$</strong></p>
<ul class="list-unstyled small">
<li>Date: @Model.Date.ToShortDateString()</li>
<li>Capacité: @Model.Capacite places</li>
</ul>

<span class="badge text-bg-light">@Model.Ville!.Nom, @Model.Ville!.PaysIso</span>

<form asp-controller="Panier" asp-action="Create">
<button class="btn btn-primary mt-5">Acheter un billet</button>
</form>
</div>

<!-- ... -->
info
  • Ce bouton, camouflé dans un formulaire, fera une requête POST vers asp-controller="Panier" asp-action="Create"

Vous pouvez essayer. Un bouton devrait être présent dans la page, mais vous serez mené vers une page vide puisque l'action Create ne retourne qu'un code 200. De plus, on ne passe aucune donnée au contrôleur: on ne sait pas quel événement ajouter au panier.

Ajouter un champ caché pour contenir l'identifiant de l'événement

La requête POST doit envoyer l'événement à ajouter au panier.

Pour cela, on peut utiliser un champ caché (hidden) dans la page:

Snowfall.Web.Mvc/Views/Evenements/Show.cshtml
<form asp-controller="Panier" asp-action="Create">
<input type="hidden" name="itemId" value="@Model.Id" />
<button class="btn btn-primary mt-5">Acheter un billet</button>
</form>
attention

Dans le projet d'exemple, les quantités ne sont pas gérées. Dans votre projet, n'oubliez pas que vous devez permettre à l'utilisateur de choisir la quantité qu'il veut ajouter à son panier!

Lier le formulaire à un ViewModel

Comme vu jusqu'à maintenant, nous pourrions faire cela au niveau de notre contrôleur pour accepter le champ itemId du formulaire en argument:

public async Task<IActionResult> Create(int itemId)
{
return Ok();
}

Et jusque dans une certaine mesure, si vous partez l'application en mode debug, mettez un breakpoint dans la fonction Create, vous verrez que cela fonctionne:

img

Par contre, conceptuellement, j'ajoute au panier des items. Ces items ont minimalement un Id, mais pourraient avoir d'autres propriétés comme une quantité, par exemple.

Ainsi, plutôt que d'accepter des paramètres individuellement, il est possible d'associer à un formulaire un ViewModel.

  1. Créer dans le dossier Models du projet Mvc une classe PanierItemViewModel sous un dossier Panier:
    Snowfall.Web.Mvc/Models/Panier/PanierItemViewModel.cs
    namespace Snowfall.Web.Mvc.Models.Panier;

    public class PanierItemViewModel
    {

    }
  2. Ajoutez une propriété ItemId pour contenir l'identifiant de l'item du panier.
    Snowfall.Web.Mvc/Models/Panier/PanierItemViewModel.cs
    public class PanierItemViewModel
    {
    public int ItemId { get; set; }
    }
  3. Modifiez la signature de la fonction Create pour accepter le ViewModel PanierItemViewModel.
    Snowfall.Web.Mvc/Controllers/PanierController.cs
    public async Task<IActionResult> Create(PanierItemViewModel panierItem)
    {
    return Ok();
    }

Les input du formulaire possédant le même nom (name) que les propriétés du ViewModel seront automatiquement associées!

img

Ajouter l'item à la session

Pour ajouter des données à la session, on utilise un système clé-valeur via l'objet HttpContext.Session.

Pour assigner des valeurs, la session possède deux fonctions:

  • HttpContext.Session.SetInt32();: stock un int dans la session
  • HttpContext.Session.SetString();: stock un string dans la session
info

La session est stockée dans un cookie qui est un format de stockage texte. Ainsi, il n'est pas possible de stocker directement un objet, par exemple.

Pour stocker un objet, il faut le transformer en texte. C'est un processus que nous appelons sérialisation.

Heureusement, nous connaissons un format de données qui est particulièrement bien adapté aux objets: le JSON!

.NET a une classe statique appelée JsonSerializer permettant de convertir au format JSON un objet.

Convertir le ViewModel en JSON

On peut convertir l'objet en JSON de cette façon:

Snowfall.Web.Mvc/Controllers/PanierController.cs
public async Task<IActionResult> Create(PanierItemViewModel panierItem)
{
string itemSerialized = JsonSerializer.Serialize(panierItem);
return Ok();
}

Utiliser SetString sur la session

Finalement, on peut ajouter le tout à la session à l'aide de HttpContext.Session.SetString.

Snowfall.Web.Mvc/Controllers/PanierController.cs
public async Task<IActionResult> Create(PanierItemViewModel panierItem)
{
string itemSerialized = JsonSerializer.Serialize(panierItem);
HttpContext.Session.SetString("panier", itemSerialized);

return Ok();
}
info

La session est un système "clé-valeur", ce qui veut dire qu'on associe une valeur à une certaine clé. Ici, la clé choisie est panier.

C'est la même clé que nous utiliserons pour lire le contenu de la session.

Faites un ajout au panier à l'aide du bouton d'achat et vous pourrez voir un cookie Session dans les outils de développeur de votre navigateur! :)

img