Récupérer les informations d'un produit depuis un numéro de commande avec Drupal Commerce

Logo Drupal Commerce

Drupal Commerce est une solution extrêmement puissante et modulaire (à l'image de Drupal) pour construire un site e-commerce. Que ce soit une boutique en ligne classique, ou un site Internet plus complexe  (abonnement, inscription, devis, etc.). La force de Drupal Commerce réside en l'utilisation des entités de Drupal, et permet ainsi d'utiliser, outre l'API fournie par Drupal Commerce, toutes les API fournies nativement par Drupal (Field API, Form API, Entity API, etc.) pour développer rapidement et sûrement une plateforme de vente en ligne robuste et sur mesure.

Un autre atout majeur de Drupal Commerce est une utilisation systématique du framework Rules pour tous les paramétrages ou actions fournies par Drupal Commerce (grâce à une intégration complète de Drupal Commerce dans le framework Rules). Ainsi, la plupart des paramétrages ou actions déclenchées sur certains événements sont gérés depuis Rules, et peuvent être alors modifiés, adaptés aux besoins, sans nécessairement devoir se plonger dans le code et développer des modules spécifiques.

Cette force et cette modularité impliquent bien sûr une plus grande complexité, complexité bien sûr toute relative comparée à d'autres solutions e-commerce.

Aperçu de l'architecture de Drupal Commerce

L'architecture de Drupal Commerce s'articule autour de quatre notions clés principales qui sont implémentées au moyen des entités correspondantes (entre parenthèses) dans l'écosystème Drupal (d'autres notions sont présentes également comme le client, les informations de facturation et de livraison, etc. et sont développées selon la même logique) :

  • L'affichage des produits (Product Display)
  • Le produit lui-même (Product)
  • Les éléments d'une commande (Line Item)
  • La commande (Order)

L'affichage des produits a pour vocation de présenter le ou les produits disponibles. A cet effet on peut associer plusieurs produits (ou variations de produit) à un même affichage de produit, et ne présenter que les différences de chaque produit sur leur affichage commun. Une commande fera référence bien entendu au client (et ses informations liées) et aux différents éléments constituant la commande. Ces élements de commande feront quant à eux référence aux produits eux-même.

Drupal commerce entity schema

Ces quatre notions étant implémentées au moyen du système d'entité de Drupal, il est donc possible de leur associer à peu près n'importe quelle information supplémentaire. Nous pouvons imaginer par exemple un produit existant dans plusieurs variantes selon leur couleur. Il sera alors possible d'associer une information sur la couleur dans l'entité Produit pour afficher au sein de l'affichage commun toutes les variations du produit selon leur couleur. De même, si les produits partagent la même information (une taille unique par exemple), il sera possible d'associer cette information à l'affichage du produit plutôt qu'aux produits eux-mêmes.

Ces possibilités offertes par le système d'entité de Drupal permettent d'envisager toutes les architectures possibles pour concevoir une boutique en ligne pour n'importe quel type d'activité et de métier (vente de produits, vente de services, catalogue complexe, etc.).

Afficher les informations contenues dans le produit ou l'affichage du produit depuis un numéro de commande

Cette modularité permet de concevoir une architecture du site e-commerce sur mesure et de positionner les informations des produits ou services sur les entités les plus pertinentes. Pour récupérer ces informations depuis un numéro de commande, et les utiliser depuis le framework Rules pour par exemple personnaliser les courriels envoyés selon les actions réalisées par l'internaute, il n'est pas inutile d'écrire une petite fonction qui va nous permettre d'automatiser tout cela selon nos besoins.

Pour ce faire, nous allons créer un petit module que nous allons appeler MYMODULE et y créer une fonction que nous pourrons utiliser depuis Rules. Nous allons utiliser dans ce snippet la fonction entity_metadata_wrapper fournie par l'excellent module Entity API. Pour plus d'informations, sur la manipulation des entités avec Entity API, vous pouvez consulter cet article par exemple qui introduit la puissance fournie par le module Entity API.

Nous allons donc récupérer dans un premier temps les identifiants des produits liés à une commande.

 function MYMODULE_get_display_info_by_order_id($order_id, $product_field_name = 'YOUR_FIELD_PRODUCT_REFERENCE', $mode = 'text') {
  global $base_url;
  global $base_path;
  
  // Get product_id from order
  $product_ids = array();
  foreach (entity_metadata_wrapper('commerce_order', $order_id)->commerce_line_items as $delta => $line_item_wrapper) {
    if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
      $product_ids[] = $line_item_wrapper->commerce_product->raw();
    }
  }
  if (empty($product_ids)) {
    return FALSE;
  }
} 

Cette fonction prend comme paramètres

  • le numéro de commande ($order_id),
  • le nom machine du champ Product reference qui vous permet de référencer les produits depuis le noeud d'affichage à modifier par votre propre champ ($product_field_name)
  • et un dernier paramètre qui va nous permettre de personnaliser les informations qui seront retournées ($mode)

Cette première partie de la fonction retourne un tableau contenant tous les identifiants des produits inclus dans la commande. A ce stade, si les informations que vous souhaitez récupérer sont présentes dans le produit lui-même, c'en est presque fini.

Nous allons maintenant, depuis les identifiants de ces produits, récupérer certaines informations présentes dans l'affichage du produit (node ou product display).

   // Get node display informations from product_id
  $titles = array();
  $links = array();
  $codes = array();
  foreach ($product_ids as $product_id) {  
    $query = new EntityFieldQuery;
    $result = $query->entityCondition('entity_type', 'node', '=')
    ->propertyCondition('type', 'YOUR_TYPE_PRODUCT_DISPLAY')
    ->fieldCondition($product_field_name, 'product_id', $product_id, '=')
    ->range(0, 1)->execute();
     
    if (empty($result['node'])) {
      return FALSE;
    }
    $nid = reset($result['node'])->nid;
    $node = node_load($nid);
    $titles[] = entity_metadata_wrapper('node', $node)->title->value();
    $codes[] = entity_metadata_wrapper('node', $node)->field_code->value();
    
    // get the path alias of node display
    $path = drupal_lookup_path('alias', "node/" . $nid);
    
    // Get the product title
    $product = commerce_product_load($product_id);
    $product_title = entity_metadata_wrapper('commerce_product', $product)->title->value();
    
    $links[] = array(
      '#theme' => 'link',
      '#text' => entity_metadata_wrapper('node', $node)->title->value(),
      '#path' => $base_url . $base_path . $path,
      '#options' => array(
        'attributes' => array('title' => entity_metadata_wrapper('node', $node)->title->value()),
        'html' => FALSE,
      ),
    );
  } 

Dans cette partie, nous allons récupérer comme informations, le titre et le lien du noeud d'affichage, un champ intitulé Code (nom machine field_code) contenu dans ce noeud, ainsi que le titre du produit lui-même. Il faut bien entendu remplacer YOUR_TYPE_PRODUCT_DISPLAY par le nom machine du type de contenu qui vous sert pour l'affichage des produits.

La dernière partie de cette fonction va nous permettre de retourner selon le dernier paramètre de la fonction ($mode) le champ souhaité.

 if ($mode == 'link') {
    return render($links);
  }
  
  if ($mode == 'code') {
    return implode(',', $codes);
  }
  
  return implode(',', $titles); 

Retrouvez ci-dessous la fonction complète

 /**
 * Get display node informations for a commerce order.
 * @param $order_id
 * An integer value of the product id.
 * @param string $product_field_name
 * Name of the commerce_product_reference field used to reference products from display node.
 * @param string $mode
 * Return the text node title (value to use 'text') or the code course (value to use 'code') or the link to the node (value to use 'link')
 * Returns FALSE if no results.
 */
function MYMODULE_get_display_info_by_order_id($order_id, $product_field_name = 'YOUR_FIELD_PRODUCT_REFERENCE', $mode = 'text') {
  global $base_url;
  global $base_path;
  
  // Get product_id from order
  $product_ids = array();
  foreach (entity_metadata_wrapper('commerce_order', $order_id)->commerce_line_items as $delta => $line_item_wrapper) {
    if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
      $product_ids[] = $line_item_wrapper->commerce_product->raw();
    }
  }
  if (empty($product_ids)) {
    return FALSE;
  }
  // Get node title form product_id
  $titles = array();
  $links = array();
  $codes = array();
  foreach ($product_ids as $product_id) {  
    $query = new EntityFieldQuery;
    $result = $query->entityCondition('entity_type', 'node', '=')
    ->propertyCondition('type', 'YOUR_TYPE_PRODUCT_DISPLAY')
    ->fieldCondition($product_field_name, 'product_id', $product_id, '=')
    ->range(0, 1)->execute();
     
    if (empty($result['node'])) {
      return FALSE;
    }
    $nid = reset($result['node'])->nid;
    $node = node_load($nid);
    $titles[] = entity_metadata_wrapper('node', $node)->title->value();
    $codes[] = entity_metadata_wrapper('node', $node)->field_code->value();
    
    // get the path alias of node display
    $path = drupal_lookup_path('alias', "node/" . $nid);
    
    // Get the product title
    $product = commerce_product_load($product_id);
    $product_title = entity_metadata_wrapper('commerce_product', $product)->title->value();
    
    $links[] = array(
      '#theme' => 'link',
      '#text' => entity_metadata_wrapper('node', $node)->title->value(),
      '#path' => $base_url . $base_path . $path,
      '#options' => array(
        'attributes' => array('title' => entity_metadata_wrapper('node', $node)->title->value()),
        'html' => FALSE,
      ),
    );
  }
  
  if ($mode == 'link') {
    return render($links);
  }  
  if ($mode == 'code') {
    return implode(',', $codes);
  }  
  return implode(',', $titles);
} 

Nous disposons alors d'une fonction que nous pourrons appeler où bon nous semble pour récupérer ces informations en fonction d'un numéro de commande. Pour utiliser cette fonction depuis le framework Rules, il vous faudra bien entendu avoir activé le module PHP filter. Une autre possibilité est bien sûr d'intégrer cette fonctionnalité au sein de Rules en lui fournissant les actions correspondantes. Mais ceci est un autre sujet, que nous pourrons aborder à l'occasion d'un autre billet.

Une question ? N'hésitez pas à la laisser dans les commentaires.

Ajouter un commentaire