Drupal Commerce, ajouter un pane custom dans votre checkout
Comme nous avons pu le voir dans l’article précédent, il est simple de configurer le checkout de Drupal Commerce de façon à créer l’expérience utilisateur que vous désirez.
Dans cet article nous allons voir comment avec du code, ajouter et modifier des panes. Pour rappel, les panes sont les éléments qui composent les différentes pages du checkout. On y retrouve par exemple, le pane de paiement, d’adresse ou encore de choix de solution de transport.
Histoire d’illustrer cela, je vous propose de créer un module Drupal afin d’ajouter un pane permettant à vos clients de choisir parmi plusieurs emballages cadeaux. Ce simple module est accessible sur Github pour plus de compréhension.
Déclaration de notre nouveau Pane
La première chose à faire consiste à déclarer au système notre nouveau pane en utilisant le
hook_commerce_checkout_pane_info()
1
2
3
4
5
6
7
8
9
<span class='line'><span class="k">function</span> <span class="nf">sandbox_commerce_checkout_pane_info</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$panes</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'title'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Paper gifts'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'base'</span> <span class="o">=></span> <span class="s1">'paper_gift_pane'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'page'</span> <span class="o">=></span> <span class="s1">'checkout'</span><span class="p">,</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="nv">$panes</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>
Notre pane sera alors reconnu par Drupal et disponible dans le checkout. Dès à présent, vous pouvez activer le module et voir le pane apparaitre sur la page d’administration du checkout.
Comme à la manière des hooks de Drupal, le code qui génère l’affichage des panes va faire de l’introspection dans le code, de façon à chercher toutes les fonctions qui commenceront par la “base” que vous avez définie dans le hook_commerce_checkout_pane_info().
Ainsi le code sera à la recherche des fonctions suivantes :
- BASE_settings_form()
- BASE_checkout_form()
- BASE_checkout_form_validate()
- BASE_checkout_form_submit()
- BASE_review()
Implémentation du formulaire d’administration du Pane
L’intérêt du pane est d’être affiché sur une page du checkout, néanmoins vous pouvez créer un formulaire de configuration pour celui-ci. Pour y accéder, rendez-vous sur l’interface d’administration du checkout. Notez qu’il n’y a pas de table dans laquelle les informations seront stockées, il vous faudra utiliser les méthodes variable_get() et variable_set() pour sauvegarder vos réglages.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: settings form callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_settings_form</span><span class="p">(</span><span class="nv">$checkout_pane</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$form</span><span class="p">[</span><span class="s1">'paper_gifts'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'#type'</span> <span class="o">=></span> <span class="s1">'checkboxes'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'#title'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Ship this item in a gift box'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'#default_value'</span> <span class="o">=></span> <span class="nx">variable_get</span><span class="p">(</span><span class="s1">'paper_gifts'</span><span class="p">,</span> <span class="k">array</span><span class="p">()),</span>
</span><span class='line'> <span class="s1">'#options'</span> <span class="o">=></span> <span class="nx">paper_gift_pane_default_paper_gifts</span><span class="p">(),</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="nv">$form</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Defines a list of default paper gifts to use.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_default_paper_gifts</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'boy'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Boys'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'girl'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Girls'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'christmas'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Christmas'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'birthday'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Birthday'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'none'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'None'</span><span class="p">),</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span>
Dans le code ci-dessus, j’ai créé un formulaire d’administration et ajouté un champ texte, permettant au webmaster de modifier le texte qui sera affiché à l’utilisateur.
Création du Pane
Un pane n’est ni plus, ni moins qu’un formulaire dans le formulaire de checkout. Comme à l’habitude, vous y retrouverez le form, le form_validate et le form_submit.
Form
Le form contiendra ce qui sera affiché à l’utilisateur. Dans le cas de notre exemple, l’utilisateur pourra choisir entre plusieurs papiers cadeaux ou pas d’emballage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$paper_gifts</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
</span><span class='line'> <span class="nv">$default_paper_gifts</span> <span class="o">=</span> <span class="nx">paper_gift_pane_default_paper_gifts</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'> <span class="k">foreach</span> <span class="p">(</span><span class="nv">$default_paper_gifts</span> <span class="k">as</span> <span class="nv">$name</span> <span class="o">=></span> <span class="nv">$title</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$image</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'path'</span> <span class="o">=></span> <span class="nx">drupal_get_path</span><span class="p">(</span><span class="s1">'module'</span><span class="p">,</span> <span class="s1">'sandbox'</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/images/'</span> <span class="o">.</span> <span class="nv">$name</span> <span class="o">.</span> <span class="s1">'.jpg'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'title'</span> <span class="o">=></span> <span class="nv">$title</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'alt'</span> <span class="o">=></span> <span class="nv">$title</span><span class="p">,</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'> <span class="nv">$paper_gifts</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">(</span><span class="s1">'image'</span><span class="p">,</span> <span class="nv">$image</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nv">$pane_form</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'#type'</span> <span class="o">=></span> <span class="s1">'radios'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'#default_value'</span> <span class="o">=></span> <span class="s1">'none'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'#title'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Select your paper gift'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'#description'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Your items will be shipped in a gift box.'</span><span class="p">),</span>
</span><span class='line'> <span class="s1">'#options'</span> <span class="o">=></span> <span class="nv">$paper_gifts</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'#attributes'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'class'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'paper-gift-pane-selection'</span><span class="p">)),</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="nv">$pane_form</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">][</span><span class="s1">'#attached'</span><span class="p">][</span><span class="s1">'css'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="nx">drupal_get_path</span><span class="p">(</span><span class="s1">'module'</span><span class="p">,</span> <span class="s1">'sandbox'</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/css/sandbox.css'</span><span class="p">,</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="nv">$pane_form</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>
Form validate
Le form_validate permet d’effectuer des opérations de validation sur les champs saisis par l’utilisateur.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form validation callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form_validate</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// Validate the given value or set to none if null.</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$form_state</span><span class="p">[</span><span class="s1">'values'</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">'pane_id'</span><span class="p">]]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$pane_values</span> <span class="o">=</span> <span class="nv">$form_state</span><span class="p">[</span><span class="s1">'values'</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">'pane_id'</span><span class="p">]];</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$pane_values</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$pane_values</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'none'</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">return</span> <span class="k">TRUE</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>
Ici, on regarde simplement s’il y a une valeur, sinon on définit que l’utilisateur ne veut pas d’emballage.
Form submit
La sauvegarde des informations intervient dans le form_submit une fois que l’on a passé la validation.
1
2
3
4
5
6
7
8
9
10
11
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form submission callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form_submit</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$form_state</span><span class="p">[</span><span class="s1">'values'</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">'pane_id'</span><span class="p">]]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$pane_values</span> <span class="o">=</span> <span class="nv">$form_state</span><span class="p">[</span><span class="s1">'values'</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">'pane_id'</span><span class="p">]];</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$pane_values</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$order</span><span class="o">-></span><span class="na">data</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$pane_values</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">];</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span>
Remarquez que vous avez accès à l’objet $order et que vous pouvez faire ce que vous en voulez. Pour l’exercice j’aurais pu créer un nouveau line item, mais pour faire simple le choix de l’utilisateur résidera comme simple information dans la commande.
Form review
Une fois l’information attachée à la commande, il ne nous reste plus qu’à la faire paraitre sur le pane de review afin que l’utilisateur puisse la voir.
Pour cela il suffit d’implémenter le form_review
1
2
3
4
5
6
7
8
9
10
11
12
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: Review</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_review</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nv">$order</span><span class="o">-></span><span class="na">data</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">&&</span> <span class="nv">$order</span><span class="o">-></span><span class="na">data</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">!=</span> <span class="s1">'none'</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nv">$content</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'> <span class="s1">'#type'</span> <span class="o">=></span> <span class="s1">'item'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'#markup'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Items in your order will be wrapped with the %paper paper gift'</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">'%paper'</span> <span class="o">=></span> <span class="nv">$order</span><span class="o">-></span><span class="na">data</span><span class="p">[</span><span class="s1">'paper_gift'</span><span class="p">])),</span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">drupal_render</span><span class="p">(</span><span class="nv">$content</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span>
Modification d’un Pane existant
Comme mentionné précédemment, les panes ne sont que des forms et comme tout form vous avez la possibilité de les alterer via un hook_form_alter() ou encore mieux un hook_form_ID_alter().
Dans le cas ou vous voudriez modifier les paramètres d’un pane, vous implémentez la fonction hook_commerce_checkout_pane_info_alter()
Le mot de la fin
Au final, ce n’était pas si compliqué que cela ? En plus du fait d’avoir maintenant un pane pour offrir un emballage papier cadeau à vos clients, vous savez maintenant qu’il y a 3 grandes étapes dans la vie d’un pane. Le formulaire de settings pour l’admin, le formulaire destiné aux clients et le formulaire de review. Je serais curieux de savoir pourquoi vous avez besoin d’ajouter un pane dans votre checkout…
Comme mentionné au début de ce post, le code est accéssible sur Github.