Programmatically alter custom checkout panes depending on cart quantity In a checkout process, I need to collect additional data depending on the quantity in the cart.

What is it about ?


Customers book travels
For the sake of argument, let’s say 2 couples of lovers book a week in Venice : they will add 4 "Venice trip" to the cart
During checkout, I  collect usual customer data, plus additional information (name, passport, birthday…) for each other participant in a dynamic custom pane.


My current work :


Set up a custom pane with Commerce Fieldgroup Panes, disabled by default
Add a rule with Commerce Rules Extra, which enables the pane if qty > 1 for specific product type
Then with MYMODULE_form_FORM_ID_alter, I tried 2 different ways to add the relevant fields :




First one:  duplicate the whole custom pane into the checkout page :

function vts_commerce_form_commerce_checkout_form_checkout_alter(&amp;$form, &amp;$form_state, $form_id) {

  $order = $form_state['build_info']['args'][0];
  //load line item
  $line_item = commerce_line_item_load($order-&gt;commerce_line_items['und'][0]['line_item_id']);
  //get quantity
  $qty = round($line_item-&gt;quantity);

  //duplicate the pane for each participant (minus the customer user) 
  for($n=1; $n&lt;$qty; $n++) {
    $form['commerce_fieldgroup_pane__group_pax_sup_'.$n] = array_merge(array(), $form['commerce_fieldgroup_pane__group_pax_sup']);
  }
}


The pane is duplicated fine, but the fields of duplicated panes are not saved, nor displayed at next step. Maybe because I have to tell the system these ARE checkout panes, + subsequent  callbacks (as incheckout_pane_example module) ?

If so, I don't find how to declare the added panes are checkout ones inside that loop.



Second one : duplicate each field of the custom pane inside the checkout pane itself :

function vts_commerce_form_commerce_checkout_form_checkout_alter(&amp;$form, &amp;$form_state, $form_id) {

  $order = $form_state['build_info']['args'][0];
  //load line item
  $line_item = commerce_line_item_load($order-&gt;commerce_line_items['und'][0]['line_item_id']);
  //get quantity
  $qty = round($line_item-&gt;quantity);

  //duplicate the pane for each participant (minus the customer user) 
  for($n = 1; $n&lt;$qty; $n++) {

    foreach ($form['commerce_fieldgroup_pane__group_pax_sup'] as $key =&gt; $value) {

      //searching fields into the fieldgroup array
      if(substr($key, 0, 5) == 'field') {

        //we duplicate the field
        $form['commerce_fieldgroup_pane__group_pax_sup'][$key.'_'.$n] = array_merge(array(), $form['commerce_fieldgroup_pane__group_pax_sup'][$key]);
        //rename field to have it unique
        $form['commerce_fieldgroup_pane__group_pax_sup'][$key.'_'.$n]['und'][0]['#field_name'] = $form['commerce_fieldgroup_pane__group_pax_sup'][$key.'_'.$n]['und'][0]['#field_name'].'_'.$n;
        //add it to the fieldgroup : NOT SURE WHICH IS THE RIGHT ONE OR IF I MUST LEAVE ALL
        $group_pax_sup = $form['commerce_fieldgroup_pane__group_pax_sup']['#groups']['group_pax_sup'];
        $group_pax_sup-&gt;children[] = $key.'_'.$n;  
        $form['commerce_fieldgroup_pane__group_pax_sup']['#group_children'][$key.'_'.$n] = 'group_pax_sup' ;
      }
    }
  }


This is adding the fields correctly, but again nothing is saved or displayed in next steps. Here it seems that I don't manage to add the fields to the fieldgroup correctly? 

How could I achieve this ? Thank you so much!
It occurred to me there may be a nice way to do this with contrib so I'll put it down, but it does require different pieces playing together nicely, which doesn't always happen (:

I see twothree clean approaches.

Using the Field API and contrib

If you want to use the Field API to create/update/delete fields, then I still think you should use custom line item products. I'm a little surprised it doesn't have the option of displaying the form at checkout built-in. Maybe a module will be built to add that, it should be relatively simple. But in the meantime this might get you there with existing contrib:


Install editablefields and Commerce Views Pane.
Create a custom line item type for the customisable product. Add the necessary fields but don't configure them to show up on the add-to-cart form. Also ensure the line items can't be combined.
Create a line item view that accepts an order ID as an argument, and add the fields you'd like to edit (and give them the editable formatter). Add a filter to ensure it only displays line items of the correct bundle. You can test the view in preview by creating a new order with a few customisable products, find the order ID (eg. from the checkout page), and pass that order ID to the Views preview. It should show you a row for each customisable line item. I wouldn't try to get the Views pane working until it works in preview.
Add that view as a checkout pane.
Make a circle of salt around the server and whisper a prayer.


Using the Field API and combined line items (involves code)

If you'd like to have a single line item have multiple customisations, then you need to set up the fields to allow that.


Create a field collection with the fields you'd like.
Add a multi-value field collection of that type to the appropriate line item type (ensure it won't show up on the add-to-cart form).
Create your own checkout pane that displays the field edit form. You'd read the quantity from the line item and display just that many fields (as it's a multi-value field it'll save ok however many there are).


Creating a custom pane and storing data serialised with the order (involves code)

This has the disadvantages that you can't use the Field API to set up, modify, or read the fields (so no Views). checkout_pane_example.module shows how to do this, adding a nickname and second email field and storing the data with the order.

Ugly method

I think you could probably do it with Commerce Fieldgroup Panes if you create a field collection with the fields you want, and then add it as a multi-value field to the order entity. You'd need some code to ensure only the correct number of field collections are shown (to match the number of line items that need customising). This method feels dirty and has two concrete drawbacks I can think of:


As you add more customisable products the order entity will get more and more fields.
You can't associate the customisations with a particular line item (what if I want a want one message on my red sweater, and a different one on my blue sweater - which message goes with which line item?).