I ended using Andy's 2nd solution, using the Field API and combined line items.
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.
As I fighted a while with some aspects of the API, especially with Field collection, here's my full checkout pane .module
, maybe it will help somebody.
/**
* Implements hook_commerce_checkout_pane_info().
*/
function vts_commerce_checkout_pane_info() {
$panes['paxsup_pane'] = array(
'title' => t('Other Participants'),
'base' => 'vts_pane',
'name' => t('Other Participants'),
'page' => 'checkout',
'weight' => -5,
);
return $panes;
}
/**
* Implements hook_pane_checkout_form().
*/
function vts_pane_checkout_form($form, &$form_state, $checkout_pane, $order) {
//load the current line item.
$line_item = commerce_line_item_load($order->commerce_line_items['und'][0]['line_item_id']);
$pane_form = array();
if ($checkout_pane['pane_id'] == 'paxsup_pane') {
field_attach_form('commerce_line_item', $line_item, $pane_form, $form_state, NULL, array('field_name' => 'field_paxsup'));
}
return $pane_form;
}
/*
* Change general GUI edit field cardinality range from 1->1 to 1->20
*/
function vts_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
// Increase the max cardinality to 20
$range = drupal_map_assoc(range(1, 20));
$new_options = array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + $range;
// Change the options
$form['field']['cardinality']['#options'] = $new_options;
}
/*
* Implements hook_form_FORM_ID_alter().
*/
function vts_form_commerce_checkout_form_checkout_alter(&$form, &$form_state, $form_id) {
//Load line item to get pax number
$line_item = commerce_line_item_load($form_state['build_info']['args'][0]->commerce_line_items['und'][0]['line_item_id']);
$paxsup_nb = $line_item->quantity -1;
if($paxsup_nb > 0){
//Unset unnecessary items of paxsup field
for($n=19; $n > $paxsup_nb-1; $n--){
unset($form['paxsup_pane']['field_paxsup']['und'][$n]);
}
//Fill in paxsup with session values
for($n=0; $n<$paxsup_nb; $n++){
$form['paxsup_pane']['field_paxsup']['und'][$n]['field_paxsup_foo']['und']['#default_value'][0] = isset($_SESSION['paxsup_foo_'.$n])? $_SESSION['paxsup_foo_'.$n]:'';
$form['paxsup_pane']['field_paxsup']['und'][$n]['field_paxsup_bar']['und'][0]['value']['#default_value'] = isset($_SESSION['paxsup_bar_'.$n])? $_SESSION['paxsup_bar_'.$n]:'';
$form['paxsup_pane']['field_paxsup']['und'][$n]['field_paxsup_date_of_birth']['und'][0]['#default_value']['value'] = isset($_SESSION['paxsup_date_of_birth_'.$n])? $_SESSION['paxsup_date_of_birth_'.$n]:'';
}
}
else {
unset($form['paxsup_pane']);
}
}
/**
* Implements hook_pane_checkout_form_validate().
*/
function vts_pane_checkout_form_validate($form, &$form_state, $checkout_pane, $order) {
$line_item = commerce_line_item_load($order->commerce_line_items['und'][0]['line_item_id']);
$paxsup_nb = $line_item->quantity -1;
if($paxsup_nb > 0){
for($n=0; $n<$paxsup_nb;$n++) {
if(empty($form_state['values']['field_paxsup']['und'][$n]['field_paxsup_foo']['und'][0]['value'])
|| empty($form_state['values']['field_paxsup']['und'][$n]['field_paxsup_bar']['und'][0]['value'])
|| empty($form_state['values']['field_paxsup']['und'][$n]['field_paxsup_date_of_birth']['und'][0]['value']) ) {
return FALSE;
}
}
}
return TRUE;
}
/**
* Implements hook_pane_checkout_form_submit().
*/
function vts_pane_checkout_form_submit($form, &$form_state, $checkout_pane, $order) {
$line_item = commerce_line_item_load($order->commerce_line_items['und'][0]['line_item_id']);
$paxsup_nb = $line_item->quantity -1;
if($paxsup_nb > 0){
//Set paxsup SESSION values
for($n=0; $n<$paxsup_nb;$n++) { //
$_SESSION['paxsup_foo_'.$n] = $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_foo']['und'][0]['value'];
$_SESSION['paxsup_bar_'.$n] = $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_bar']['und'][0]['value'];
$_SESSION['paxsup_date_of_birth_'.$n] = $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_date_of_birth']['und'][0]['value'];
}
// Add the paxsup values to the line_item fields
if (!empty($form_state['values']['field_paxsup'])) {
if(0 == count($line_item->field_paxsup)){
//CREATE A NEW PAXSUP SET
$entity = entity_load_single('commerce_line_item', $line_item->line_item_id);
$wrapper = entity_metadata_wrapper('commerce_line_item', $entity);
$collections = array();
for($n=0; $n<$paxsup_nb;$n++) { //
$fc = entity_create('field_collection_item', array('field_name' => 'field_paxsup'));
$fc->setHostEntity('commerce_line_item', $line_item, LANGUAGE_NONE, FALSE);
$fc->field_paxsup_foo['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_foo']['und'][0]['value']);
$fc->field_paxsup_bar['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_bar']['und'][0]['value']);
$fc->field_paxsup_date_of_birth['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_date_of_birth']['und'][0]['value']);
$fc->save(TRUE);
$collections[] = $fc;
}
//Set the field collection items via the wrapper.
$wrapper->field_paxsup->set($collections);
$wrapper->save();
}
else {
//UPDATE FIELD COLLECTION
for($n=0; $n<$paxsup_nb;$n++) { //
$fc = entity_load_single('field_collection_item', $line_item->field_paxsup['und'][$n]['value'] );
$fc->field_paxsup_foo['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_foo']['und'][0]['value']);
$fc->field_paxsup_bar['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_bar']['und'][0]['value']);
$fc->field_paxsup_date_of_birth['und'][0] = array('value'=> $form_state['values']['field_paxsup']['und'][$n]['field_paxsup_date_of_birth']['und'][0]['value']);
$fc->save(TRUE);
}
}
}
}
}
/**
* Implements hook_pane_review().
*
*/
function vts_pane_review($form, $form_state, $checkout_pane, $order) {
$line_item = commerce_line_item_load($order->commerce_line_items['und'][0]['line_item_id']);
$paxsup_nb = $line_item->quantity -1;
if (!empty($line_item->field_paxsup['und'][0])) {
$view = '';
for($n=0; $n<$paxsup_nb;$n++) {
$paxsup = entity_load_single('field_collection_item', $line_item->field_paxsup['und'][$n]['value'] );
$display = array('label' => 'hidden');
$foo = field_view_field('field_collection_item', $paxsup, 'field_paxsup_foo', $display);
$bar = field_view_field('field_collection_item', $paxsup, 'field_paxsup_bar', $display);
$date = field_view_field('field_collection_item', $paxsup, 'field_paxsup_date_of_birth', $display);
$view .= '<p>'.$foo[0]['#markup'] .' '. $bar[0]['#markup'] .' '. $prenom[0]['#markup'] .t(', né(e) le '). $date[0]['#markup'] .'</p>';
}
return $view;
}
}
hook_commerce_line_item_type_info
.