Tutoriel d'intégration de suggestion de produits avec l'API sur son site Kiubi
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
theme/fr
AUTHORS
LICENSE.md
README.md

README.md

Suggestions de produits

Introduction

Ce dépôt est un tutoriel qui explique comment utiliser l'API de son site Kiubi pour afficher une liste de produits comme suggestion d'achat pour l'internaute.

Nous allons lui proposer des produits en relation avec sa dernière commande lorsqu'il se connecte, puis des produits associés à ceux présents dans son panier, et enfin nous allons créer un bloc contenant les derniers produits visités.

Prérequis

Ce tutoriel suppose que vous avez un site Kiubi et qu'il est bien configuré :

  • l'API est activée
  • le catalogue et l'ecommerce activés
  • des produits configurés avec des produits associés
  • le widget panier est inséré dans l'entête des mises en page
  • le site est en thème personnalisé, basé sur Shiroi

Il est préférable d'être à l'aise avec la manipulation des thèmes personnalisés. En cas de besoin, le guide du designer est là.

Ce tutoriel est applicable à tout thème graphique mais les exemples de codes donnés sont optimisés pour un rendu basé sur le thème Shiroi.

Inclusions des bibliothèques javascript

Pour réaliser notre exemple il nous faut 2 librairies, le client JS Kiubi pour utiliser l’API et un plugin jQuery qui nous permettra de gérer facilement un cookie pour la mémorisation des produits visités.

Le plugin jQuery utilisé est jquery-cookie et est distribué sous licence MIT. Les sources sont disponibles à l’adresse suivante : https://github.com/carhartl/jquery-cookie/ On rajoute l’inclusion des deux fichiers grâce au module Injection de code et on met le code d’inclusion avant la balise </head> :

<script type="text/javascript" src="//{cdn}/js/kiubi.api.pfo.jquery-1.0.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.3.1/jquery.cookie.min.js"></script>

Nous utilisons ici un cdn public tiers pour l’inclusion du plugin jQuery, vous pouvez également récupérer le fichier et le déposer dans votre thème puis l’inclure dans vos templates de pages.

Suggestions de produits à la connexion

Nous allons modifier le template du panier afin de pouvoir se conncter sans rechargement de la page. Une fois connecté, nous afficherons le statut de la dernière commande de l'intenaute et nous lui proposerons des produits en relation avec ceux déjà achetés.

Mise en place

Remplacez le template theme/fr/widgets/commandes/panier/index.html de votre thème par celui de ce tutoriel.

Allez sur la page d'accueil et cliquez sur le lien Se connecter en haut à droite. Le mini formulaire de connexion doit s'afficher. Si vous vous connectez et que vous avez déjà passé une commande, une liste de produit associés doit s'afficher sous l'entête.

Explications

Examinons en détail le code HTML du template panier/index.html :

  • Dans le bloc main.nonidentifie on ajoute l'html suivant pour faire un mini formulaire de connexion qui est par défaut masqué :
<span style="display:none;"> : 
  <label for="api-login">Identifiant : </label><input type="text" class="field" id="api-login" size="10"/> 
  <label for="api-pwd">Mot de passe : </label><input type="password" class="field" id="api-pwd" size="10"/>
  <input id="api-submit" type="button" class="submit" style="padding:2px;" value="Ok" />
</span>

On se greffe sur l'événement click du lien Se connecter pour changer l'état d'affichage de notre mini formulaire :

$(document).on('click', '#api-link-login', function(){
	$('p.login > span').toggle();
	return false;
});

Lorsque l'on valide le mini formulaire, on effectue une connexion à Kiubi via l'API, si celle ci échoue, on change le style des champs du formulaire. Si l'utilisateur est connecté on affiche son nom, un lien pour se déconnexcter puis on récupère ses dernières commandes via la méthode getOrders()

$(document).on('click', "#api-submit", function(){
  var login = $("#api-login").val();
  var pwd = $("#api-pwd").val();
  
  // Pas de requête si aucun login ou aucun mot de passe
  if(login == '' || pwd == '') {
    $("p.login input.field").css('border', '2px solid red');
    return;
  }
  
  // Connexion à kiubi
  var query = kiubi.login(login, pwd);
  
  // En cas de connexion réussie
  query.done(function(meta, data){
    $(".login").hide().after('<p class="compte"><a href="{baseLangue}/compte/">'+data.firstname+' '+data.lastname+'</a> - <a id="api-link-logout" href="{baseLangue}/compte/logout.html">Déconnexion</a></p>');
    
    getOrders(data.user_id);
  });
  
  // En cas de connexion échouée
  query.fail(function(meta, error){
    $("p.login input.field").css('border', '2px solid red');
  });         
});

Si l'utilisateur clique sur le lien de déconnexion on éffectue celle-ci via l'API :

$(document).on('click', '#api-link-logout', function(){
	// Déconnexion de l'utilisateur courant
	kiubi.logout().done(function(meta, data){
		$("p.compte").hide();
		$("p.login").show();
		$("p.login span").hide();
		$("p.login input.field").removeAttr('style');
		$("#api-pwd").val('');
	});
	return false;
});

Dans le bloc main.identifie on se contente de récupérer les dernières commandes du membre :

getOrders({CLIENT.client_id});

La fonction getOrders() va réaliser plusieurs opérations :

  • récupération des commandes et affichage du statut de la dernière
  • récupération du détail de la dernière commande
  • récupération des produits associés aux produits de cette commande
  • affichage de ces produits avec la possibilité de les ajouter au panier en un clic
  • permettre à l'utilisateur de masquer cet encart de suggestion

On récupère les commandes uniquement si l'utilisateur n'a pas demandé la fermeture de l'encart de suggestions. Une fois la liste récupérée, on affiche devant son nom le statut de sa dernière commande s'il en a une.

if($.cookie('nosuggestion')=='1') return;
  kiubi.users.getOrders(id).done(function(meta, data){
    if(data.length) {
      $('p.compte > a:first').before('Etat de votre dernière commande : '+data[0].status+' - ');

Ensuite on récupère le détail de la dernière commande, et on stocke les identifiants de chaque produit acheté :

kiubi.users.getOrder(data[0].id).done(function(meta, data){
	var products = [];
	for(var i=0; i<data.items.length; i++) {
		if(data.items[i].product_id)
			products.push(data.items[i].product_id);
	}

Via l'API on requête la liste des produits associés de chacun des produits achetés, on stocke chaque produit associé qui n'a pas été acheté dans un tableau, dans la limite de 3 pour des questions d'affichage dans notre thème :

var linked = [], queries = [];
for(var i=0; i<products.length && linked.length<3; i++) {
  queries.push(kiubi.catalog.getLinkedProducts(products[i], {extra_fields:'price_label,variants'}).done(function(meta, data){
    for(var p=0; p<data.length; p++) {
      if(linked.length<3 && $.inArray(data[p].id, products)==-1) {
        linked.push(data[p]);
      }
    }             
  }));            
}

Une fois que toutes les requêtes vers tous les produits ont été effectués, on construit l'html de notre liste de suggestions avec les données stockées précédemment, puis on les affiche en dessous de notre entête :

$.when.apply($, queries).done(function(){           
  if(linked.length) {
    var html = '<div style="display:none;" id="suggestions"><br/><span style="float:right" id="closesuggestion">X</span><div><strong>Voici une liste de produits pour compléter votre dernière commande :</strong><br/>';
    for(var p=0; p<linked.length; p++) {
      html += '<div style="float:left;width:'+(100/linked.length)+'%">';
      html += '<h4><a href="'+linked[p].url+'">'+linked[p].name+'</a> - '+linked[p].price_min_inc_vat_label+'</h4>';
      html += '<a style="float:left;margin-right:10px;" href="'+linked[p].url+'"><img src="'+linked[p].main_thumb.url_miniature+'"/></a>';
      html += '<div>'+linked[p].header+'</div>';
      html += '<input class="bouton addtocart" type="submit" data-id="'+linked[p].variants[0].id+'" value="Ajouter au panier"/>';
      html += '</div>';
    }
    html += '</div></div>';
    $('.menu_header').append(html);
    $('#suggestions').slideDown('slow');

On se greffe sur l'événement click des boutons ajoutés dans notre liste de suggestion pour ajouter directement le produit dans le panier de l'internaute :

$('.addtocart').click(function(){
  var me = $(this);
  me.next('p').remove();
  kiubi.cart.addItem($(this).data('id'), '+1', null, {extra_fields:'price_label'}).done(function(meta, data){
    me.after('<p>Produit ajouté au panier !</p>');
    $('.panier_link a').html(data.cart.items_count+' article'+(data.cart.items_count>1?'s':'')+' - '+data.cart.price_total_inc_vat_label);
  });
});

Enfin, sur le clic de la croix ajoutée à droite de notre liste on masque les suggestions et on stocke un cookie afin de ne pas les représenter au visiteur :

$("#closesuggestion").click(function(){
  $('#suggestions').slideUp('fast');
  $.cookie('nosuggestion', '1');
});

Exemple complet

<!-- 
Template du Widget "Panier"
-->

<!-- BEGIN:main -->
<div class="panier">
  <!-- BEGIN:nonidentifie -->
  <p class="login"><a id="api-link-login" href="{baseLangue}/compte/">Se connecter</a>
    <span style="display:none;"> : 
      <label for="api-login">Identifiant : </label><input type="text" class="field" id="api-login" size="10"/> 
      <label for="api-pwd">Mot de passe : </label><input type="password" class="field" id="api-pwd" size="10"/>
      <input id="api-submit" type="button" class="submit" style="padding:2px;" value="Ok" />
    </span>
  </p>
  <script type="text/javascript">
    $(function(){
      // Interception du clic sur le lien "Se connecter" 
      // et affichage du mini formulaire de connexion
      $(document).on('click', '#api-link-login', function(){
        $('p.login > span').toggle();
        return false;
      });
      
      // Login sur le click du bouton du mini formulaire
      $(document).on('click', "#api-submit", function(){
        var login = $("#api-login").val();
        var pwd = $("#api-pwd").val();
        
        // Pas de requête si aucun login ou aucun mot de passe
        if(login == '' || pwd == '') {
          $("p.login input.field").css('border', '2px solid red');
          return;
        }
        
        // Connexion à kiubi
        var query = kiubi.login(login, pwd);
        
        // En cas de connexion réussie
        query.done(function(meta, data){
          $(".login").hide().after('<p class="compte"><a href="{baseLangue}/compte/">'+data.firstname+' '+data.lastname+'</a> - <a id="api-link-logout" href="{baseLangue}/compte/logout.html">Déconnexion</a></p>');
          
          getOrders(data.user_id);
        });
        
        // En cas de connexion échouée
        query.fail(function(meta, error){
          $("p.login input.field").css('border', '2px solid red');
        });         
      });
      
      // interception du clic sur le lien "Déconnexion"
      $(document).on('click', '#api-link-logout', function(){
        
        // Déconnexion de l'utilisateur courant
        kiubi.logout().done(function(meta, data){
          $("p.compte").hide();
          $("p.login").show();
          $("p.login span").hide();
          $("p.login input.field").removeAttr('style');
          $("#api-pwd").val('');
        });
        return false;
      });
    });
  </script>
  <!-- END:nonidentifie -->
  <!-- BEGIN:identifie -->
  <p class="compte"><a href="{baseLangue}/compte/">{prenom} {nom}</a> - <a href="{baseLangue}/compte/logout.html">Déconnexion</a></p>
  <script type="text/javascript">
    $(function(){
      getOrders({CLIENT.client_id});
    });
  </script>
  <!-- END:identifie -->
  <p class="panier_link"><a href="{baseLangue}/ecommerce/panier.html" title="Détail du panier">{nb_produits} article{pluriel_produits} - {total}</a></p>
</div>
<script type="text/javascript">
getOrders = function(id) {
  if($.cookie('nosuggestion')=='1') return;
  kiubi.users.getOrders(id).done(function(meta, data){
    if(data.length) {
      $('p.compte > a:first').before('Etat de votre dernière commande : '+data[0].status+' - ');

      kiubi.users.getOrder(data[0].id).done(function(meta, data){
        var products = [];
        for(var i=0; i<data.items.length; i++) {
          if(data.items[i].product_id)
            products.push(data.items[i].product_id);
        }
        if(products.length) {
          var linked = [], queries = [];
          for(var i=0; i<products.length && linked.length<3; i++) {
            queries.push(kiubi.catalog.getLinkedProducts(products[i], {extra_fields:'price_label,variants'}).done(function(meta, data){
              for(var p=0; p<data.length; p++) {
                if(linked.length<3 && $.inArray(data[p].id, products)==-1) {
                  linked.push(data[p]);
                }
              }             
            }));            
          }         
          $.when.apply($, queries).done(function(){           
            if(linked.length) {
              var html = '<div style="display:none;" id="suggestions"><br/><span style="float:right" id="closesuggestion">X</span><div><strong>Voici une liste de produits pour compléter votre dernière commande :</strong><br/>';
              for(var p=0; p<linked.length; p++) {
                html += '<div style="float:left;width:'+(100/linked.length)+'%">';
                html += '<h4><a href="'+linked[p].url+'">'+linked[p].name+'</a> - '+linked[p].price_min_inc_vat_label+'</h4>';
                html += '<a style="float:left;margin-right:10px;" href="'+linked[p].url+'"><img src="'+linked[p].main_thumb.url_miniature+'"/></a>';
                html += '<div>'+linked[p].header+'</div>';
                html += '<input class="bouton addtocart" type="submit" data-id="'+linked[p].variants[0].id+'" value="Ajouter au panier"/>';
                html += '</div>';
              }
              html += '</div></div>';
              $('.menu_header').append(html);
              $('#suggestions').slideDown('slow');
              $('.addtocart').click(function(){
                var me = $(this);
                me.next('p').remove();
                kiubi.cart.addItem($(this).data('id'), '+1', null, {extra_fields:'price_label'}).done(function(meta, data){
                  me.after('<p>Produit ajouté au panier !</p>');
                  $('.panier_link a').html(data.cart.items_count+' article'+(data.cart.items_count>1?'s':'')+' - '+data.cart.price_total_inc_vat_label);
                });
              });
              $("#closesuggestion").click(function(){
                $('#suggestions').slideUp('fast');
                $.cookie('nosuggestion', '1');
              });
            }
          });         
        }
      });
    }           
  });
} 
</script>
<!-- END:main -->

Suggestions de produits dans le panier

Nous allons modifier le template du détail panier afin d'insérer un produit associé à chaque produit présent dans le panier, en veillant à ne pas proposer un produit déjà présent dans le panier.

Mise en place

Remplacez le template theme/fr/widgets/commandes/panier_detail/index.html de votre thème par celui de ce tutoriel.

Mettre un produit dans votre panier. La page de détail du panier doit afficher 2 lignes, votre produit ajouté et une suggestion.

Explications

Examinons en détail le code HTML du template panier_detail/index.html :

On commence par récupérer le panier via l'API afin de stocker l'identifiant de chaque produit du panier :

kiubi.cart.get().done(function(meta, data){
  pid = [];
  for(var i=0; i<data.items.length; i++) {
    pid.push(data.items[i].product_id);
  }

Ensuite, pour chacun des produits on va récupérer ses produits associés, et stocker dans l'objet promise de notre requête l'identifiant de la variante d'origine sous la variable vid ainsi que le nom du produit afin de pouvoir les utiliser plus tard :

for(var i=0; i<data.items.length; i++) {
  var q = kiubi.catalog.getLinkedProducts(data.items[i].product_id, {random_fill:false,extra_fields:'price_label,variants'});
  q.promise.vid = data.items[i].variant_id;       
  q.promise.pname = data.items[i].product_name;     

Lorsque la requête est exécutée, la méthode done() est appelée. Si un produit associé, non présent dans le panier, a été trouvé et qu'il est disponible et en stock, on crée une nouvelle ligne de panier avec le produit associé et un bouton pour l'ajouter au panier. On insère ensuite cette ligne après la ligne du produit auquel elle se rapporte grâce à l'identifiaction de la variante que nous avions stocké dans notre objet promise et que nous accédons avec le code this.promise.vid :

q.done(function(meta, data){      
  if(!data.length) return;
  for(var p=0; p<data.length; p++) {  
    if($.inArray(data[p].id, pid)==-1 && data[p].variants[0].is_available && data[p].variants[0].in_stock) {
      pid.push(data[p].id);
      var html = '<tr><td class="produit_illustration"><a href="'+data[p].url+'"><img src="'+data[p].main_thumb.url_miniature+'"/></a></td>';
      html += '<td class="produit_info"><strong>Nous vous proposons en complément de '+this.promise.pname+' :</strong><br/><strong><a href="'+data[p].url+'">'+data[p].name+"</strong></a><br/><p>"+data[p].variants[0].name+'</p><p class="desc">'+data[p].header+'</p></td>';
      html += '<td>'+data[p].variants[0].price_inc_vat_label+'</td>';
      html += '<td nowrap="nowrap" class="produit_qte"><input type="text" class="textfield" value="1" style="width:30px" id="qt-'+data[p].variants[0].id+'"/></td>';                  
      html += '<td colspan="2"><input type="submit" style="margin-top:0px;" class="addsuggest" data-id="'+data[p].variants[0].id+'" value="Ajouter au panier"/>';
      html += '</td></tr>';
      $('#p-'+this.promise.vid).after(html);
      break;                
    }
  }
});

On se greffe sur l'événement click des boutons que nous venons d'insérer afin d'ajouter les produits au panier via l'API et recharger la page afin d'actualiser l'affichage du panier :

$(document).on('click', '.addsuggest', function(){
	kiubi.cart.addItem($(this).data('id'), $('#qt-'+$(this).data('id')).val()).done(function(){document.location.reload();});
});

La seule modification html apportée au template d'origine et l'ajout d'un id sur chaque ligne produit du panier afin de pouvoir repérer la variante et insérer à la suite sa suggestion :

<!-- BEGIN:produit -->
<tr id="{ref}">

Exemple complet

<!-- 
Template du Widget "Détail du panier"
-->
<!-- BEGIN:main -->

<ul class="chemin_commande">
  <li><a href="{baseLangue}/ecommerce/panier.html" title="Panier" class="actif">1. Panier</a></li>
  <li>2. Authentification</li>
  <li>3. Livraison</li>
  <li>4. Mode de paiement</li>
  <li>5. Validation</li>
</ul>
<article class="panier_detail">
  <!-- BEGIN:intitule -->
  <h1>{intitule}</h1>
  <!-- END:intitule -->
  <!-- BEGIN:produits -->
  <script type="text/javascript">
  $(function(){
    kiubi.cart.get().done(function(meta, data){
      pid = [];
      for(var i=0; i<data.items.length; i++) {
        pid.push(data.items[i].product_id);
      }
      for(var i=0; i<data.items.length; i++) {
        var q = kiubi.catalog.getLinkedProducts(data.items[i].product_id, {random_fill:false,extra_fields:'price_label,variants'});
        q.promise.vid = data.items[i].variant_id;       
        q.promise.pname = data.items[i].product_name;       
        q.done(function(meta, data){      
          if(!data.length) return;
          for(var p=0; p<data.length; p++) {  
            if($.inArray(data[p].id, pid)==-1 && data[p].variants[0].is_available && data[p].variants[0].in_stock) {
              pid.push(data[p].id);
              var html = '<tr><td class="produit_illustration"><a href="'+data[p].url+'"><img src="'+data[p].main_thumb.url_miniature+'"/></a></td>';
              html += '<td class="produit_info"><strong>Nous vous proposons en complément de '+this.promise.pname+' :</strong><br/><strong><a href="'+data[p].url+'">'+data[p].name+"</strong></a><br/><p>"+data[p].variants[0].name+'</p><p class="desc">'+data[p].header+'</p></td>';
              html += '<td>'+data[p].variants[0].price_inc_vat_label+'</td>';
              html += '<td nowrap="nowrap" class="produit_qte"><input type="text" class="textfield" value="1" style="width:30px" id="qt-'+data[p].variants[0].id+'"/></td>';                  
              html += '<td colspan="2"><input type="submit" style="margin-top:0px;" class="addsuggest" data-id="'+data[p].variants[0].id+'" value="Ajouter au panier"/>';
              html += '</td></tr>';
              $('#p-'+this.promise.vid).after(html);
              break;                
            }
          }
        });
      }
      $(document).on('click', '.addsuggest', function(){
        kiubi.cart.addItem($(this).data('id'), $('#qt-'+$(this).data('id')).val()).done(function(){document.location.reload();});
      });
    });
  });
  </script>
  <form method="post" action="">
    <table class="table_commande">
      <tr>
        <th colspan="2" scope="col" style="width: 100%; text-align: left;">{nb_produits} article{pluriel_produits}</th>
        <th nowrap="nowrap" scope="col">Prix</th>
        <th scope="col" style="text-align: center;">Qte</th>
        <th nowrap="nowrap" scope="col">Total</th>
        <th scope="col" class="produit_supp"> </th>
      </tr>
      <!-- BEGIN:produit -->
      <tr id="{ref}">
        <td style="width: {miniature_l}px;" class="produit_illustration"><!-- BEGIN:illustration -->
          <figure><a href="{lien_produit}" title="{intitule_produit|htmlentities} - Cliquez pour accéder"><img src="{racine}/media/miniature/{illustration}" alt="{intitule_produit|htmlentities}" class="illustration" /></a></figure>
          <!-- END:illustration -->
          <!-- BEGIN: noillustration -->
          <figure><a href="{lien_produit}" title="{intitule_produit|htmlentities} - Cliquez pour accéder"><img src="{racine}/{theme}/fr/images/produit_mini.gif" alt="{intitule_produit|htmlentities}" class="illustration" style="width: {miniature_l}px; height: {miniature_h}px;" /></a></figure>
          <!-- END: noillustration --></td>
        <td class="produit_info"><strong><a href="{lien_produit}">{intitule_produit}</a></strong><br />
          <!-- BEGIN:reference -->
          <p class="ref">Ref. : {reference}</p>
          <!-- END:reference -->
          <p class="variante">{intitule_variante}</p>
          <!-- BEGIN:accroche -->
          <p class="desc">{accroche}</p>
          <!-- END:accroche --></td>
        <td align="right" class="produit_pu">{prix_unitaire}</td>
        <td nowrap="nowrap" class="produit_qte"><input name="qt[{ref}]" type="text" class="textfield" value="{qt}" style="width:30px" />
          <!-- BEGIN:erreur_qt -->
          <div class="erreurs">max. : {max}</div>
          <!-- END:erreur_qt --></td>
        <td align="right" class="produit_tt">{prix_total}</td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:produit -->
      <!-- BEGIN:option -->
      <tr>
    <!-- BEGIN:simple -->
        <td colspan="2" style="text-align: left;">{intitule_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
    <!-- BEGIN:offerte -->
    <td> </td>
    <td style="text-align: center;">{qt}</td>
    <td>Offert</td>
    <!-- END:offerte -->
    <!-- BEGIN:payante -->
    <td>{prix_unitaire}</td>
    <td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
    <!-- END:payante -->
    <!-- END:simple -->
    <!-- BEGIN:textarea -->
        <td colspan="2" style="text-align: left;">{intitule_option} : {valeur_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
    <!-- BEGIN:offerte -->
    <td> </td>
    <td style="text-align: center;">{qt}</td>
    <td>Offert</td>
    <!-- END:offerte -->
    <!-- BEGIN:payante -->
    <td>{prix_unitaire}</td>
    <td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
    <!-- END:payante -->
    <!-- END:textarea -->
    <!-- BEGIN:select -->
        <td colspan="2" style="text-align: left;">{intitule_option} : {valeur_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
    <!-- BEGIN:offerte -->
    <td> </td>
    <td style="text-align: center;">{qt}</td>
    <td>Offert</td>
    <!-- END:offerte -->
    <!-- BEGIN:payante -->
    <td>{prix_unitaire}</td>
    <td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
    <!-- END:payante -->
    <!-- END:select -->
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:option -->
      <!-- BEGIN:bon -->
      <tr>
        <td colspan="4" style="text-align: left;">{intitule_produit} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
        <td class="produit_tt">{prix_total}</td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:bon -->
      <!-- BEGIN:remise -->
      <tr>
        <td colspan="4" style="text-align: left;">{intitule_remise} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
        <td align="right"> </td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:remise -->
      <tr class="tva">
        <td colspan="4" style="text-align: left;"><strong>Total hors frais de livraison</strong></td>
        <td align="right"><strong>{total_articles}</strong></td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- BEGIN:livrable -->
      <tr>
        <td colspan="4" style="text-align: left;">Frais de port applicables pour les livraisons vers : {zone_livraison}</td>
        <td align="right">{frais_port}</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:livrable -->
      <!-- BEGIN:nonlivrable -->
      <tr class="tva">
        <td colspan="4" style="text-align: left;">La commande dépasse le poids autorisé pour une livraison vers : {zone_livraison}</td>
        <td align="right">Non livrable</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:nonlivrable -->
      <!-- BEGIN:commande_HT -->
      <tr>
        <td colspan="4" style="text-align: left;"><strong>Total HT</strong></td>
        <td align="right"><strong>{total_HT}</strong></td>
        <td class="produit_supp"> </td>
      </tr>
      <tr>
        <td colspan="4" style="text-align: left;">TVA</td>
        <td align="right">{total_TVA}</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:commande_HT -->
      <tr class="ttc">
        <td colspan="5" class="total" style="text-align: left;"><span style="float: right;">{total_TTC}</span> Total TTC</td>
        <td class="total produit_supp"> </td>
      </tr>
    </table>
    <!-- BEGIN:options_disponibles -->
    <div class="option_commande">
      <h2>Envie d'une petite option ?</h2>
      <table>
        <!-- BEGIN:option -->
        <tr class="{type_option}">
          <!-- BEGIN:simple -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:simple -->
          <!-- BEGIN:textarea -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div>
            <textarea name="options_valeurs[{ref_option}]" rows="5">{valeur_option}</textarea></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:textarea -->
          <!-- BEGIN:select -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div>
            <select name="options_valeurs[{ref_option}]">
              <!-- BEGIN:choix -->
              <option value="{valeur}" {selected}>{valeur}</option>
              <!-- END:choix -->
            </select></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:select -->
          <td class="option_supp"> </td>
        </tr>
        <!-- END:option -->
      </table>
    </div>
    <!-- END:options_disponibles -->
    <div class="bon_reduction">
      <h2> Vous bénéficiez d'un bon de réduction ? </h2>
      <!-- BEGIN:erreur_bon -->
      <div class="erreurs">{erreur}</div>
      <!-- END:erreur_bon -->
      <label for="reduction">Renseignez le code de votre bon de réduction :</label>
      <input type="text" name="bon" value="{bon}" class="textfield" id="reduction" style="width: 40%;"  />
    </div>
    <p>
      <input type="submit" name="bt_commande" value="Commander" class="submit" title="Commander"/>
      <input type="submit" name="bt_recalcul" value="Recalculer" class="submit reset" title="Recalculer"/>
    </p>
    <input type="hidden" name="act" value="refresh" />
    <input type="hidden" name="ctl" value="{ctl}" />
    <p class="a_right"><a href="{lien_continuer}" class="bt_achats" title="Continuez vos achats">Continuez vos achats</a></p>
  </form>
  <!-- END:produits -->
  <!-- BEGIN:noproduits -->
  <p class="panier_vide">Votre panier est vide. <a href="/" title="Continuez vos achats">Retournez à l'accueil</a></p>
  <!-- END:noproduits -->
</article>
<!-- END:main -->

Les derniers produits vus

Cette fonctionnalité se fait en deux étapes, la première consiste à mémoriser un produit vu, la deuxième consiste à afficher une liste de ces mêmes produits.

Mise en place

Remplacez le fichier theme/fr/produits/simple/index.html de votre thème par celui de ce tutoriel et ajouter le fichier theme/fr/includes/produits_vus.html dans votre thème.

Dans la mise en page de votre page d'accueil, ajouter un widget Fragment de template configuré avec le template produits_vus dans la sidebar et enregistrez la mise en page.

Visitez la page d'un produit puis retournez sur la page d'accueil de votre site, vous avez dans la sidebar un bloc affichant ce produit. En visitant d'autres produits, la sidebar en page d'accueil se met à jour en fonction des derniers produits vus.

Explications

Examinons en détail le code du template produits/simple/index.html :

A chaque page affichée, on va stocker l'identifiant du produit dans un cookie, on va limiter la taille de ce cookie aux 3 derniers identifiants et rendre le cookie lisible sur tout le site (c'est la seule modification apportée au template original du détail produit) :

<script type="text/javascript">
$(function(){
  var c = $.cookie('pv');
  if(c == null) c = [];
  else c = c.split('-');
  if($.inArray('{produit_id}', c)==-1) {
    c.push({produit_id});
    if(c.length>3) {
      c = c.slice(-3);
    }
    $.cookie('pv', c.join('-'), {path:'/'});
  }
});
</script>

Examinons en détail le code du template includes/produits_vus.html :

Nous récupérons notre cookie, s'il n'est pas vide, on récupère le détail produit de chaque élément du cookie via l'API puis on ajoute un bloc contenant son titre, son image principale et une courte description à notre encart dans la sidebar.

L'encart est masqué par défaut, et n'est affiché que si au moins un produit a déjà été vu.

<script type="text/javascript">
$(function(){
  var c = $.cookie('pv');
  if(c !== null) {
    c = c.split('-');
    for(var i=0; i<c.length; i++) {
      kiubi.catalog.getProduct(c[i], {extra_fields:'price_label'}).done(function(meta, data){
        var html = '<div style="margin-bottom:15px;">';
        html += '<h2><a href="'+data.url+'">'+data.name+'</a></h2>';
        html += '<a href="'+data.url+'"><img src="'+data.main_thumb.url_miniature+'"/></a>';
        html += '<br/>Prix : '+data.price_min_inc_vat_label;
        html += '</div>';       
        $("#recent-view").show().append(html);      
      });
    }
  }
}); 
</script>
<article class="block" id="recent-view" style="display:none;"><h1>Derniers produits vus</h1></article>