Drupal Commerce: Same Product, Multiple Line Items

HOWTO: add the same product to your cart under separate line items using Drupal Commerce

In my previous post, I explained how to create dynamic line item titles in Drupal Commerce. The problem with this, while your line items may have difference titles, if two products with different line items titles get added into your cart, they'll get bundled up under the initially added line items name with a quantity of 2. This isn't what we want.

Do get around this limitation I dug around the Drupal Commerce code and found this little gem in commerce_cart_product_add().

Here's the definition for commerce_cart_product_add

<?php
/**
* Adds the specified product to a customer&#39;s shopping cart.
*
* @param $uid
* The uid of the user whose cart you are adding the product to.
* @param $product_id
* The ID of the product to add to the cart.
* @param $quantity
* The quantity of this product to add to the cart.
* @param $display_uri
* A URI array as returned by entity_uri() indicating the display to link the
* product line item to; defaults to NULL for no display.
* @param $combine
* Boolean indicating whether or not to combine like products on the same line
* item, incrementing an existing line item&#39;s quantity instead of adding a
* new line item to the cart order.
*
* @return
* The new or updated line item object or FALSE on failure.
*/
function commerce_cart_product_add($uid, $product_id, $quantity, $display_uri = NULL, $combine = FALSE);
?>

The variable we're interested in is $combine = FALSE; We want this set to TRUE. Here's how you do it.

<?php
// for lazy people whole function could get replaced by $form['#submit'] = 'yourmodule_commerce_cart_add_to_cart_form_submit';
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if(
strpos($form_id, 'commerce_cart_add_to_cart_form') === 0) {
   
$replaced_form_submit_handler = 0;
    if(!empty(
$form['#submit'])) {
      foreach(
$form['#submit'] as $i => $function) {
        if(
$function == 'commerce_cart_add_to_cart_form_submit') {
         
$form['#submit'][$i] = 'mymodule_commerce_cart_add_to_cart_form_submit';
         
$replaced_form_submit_handler = 1;
          break;
        }
      }
    }
    if(empty(
$replaced_form_submit_handler)) {
     
$form['#submit'][] = 'mymodule_commerce_cart_add_to_cart_form_submit';
    }
  }
}

// Copy and pasted from commerce_cart_add_to_cart_form_submit()
// only change is $combine = FALSE.
// IMHO this should get removed to a variable_get('commerce_cart_combine', TRUE);
function mymodule_commerce_cart_add_to_cart_form_submit($form, &$form_state) {
 
$product_id = $form_state['values']['product_id'];
 
$product = $form_state['products'][$product_id];
                                                                                                                                              
 
// Add the product to the specified shopping cart.
 
$form_state['line_item'] = commerce_cart_product_add(
   
$form_state['values']['uid'],
   
$product_id,
   
$form_state['values']['quantity'],
   
$form_state['values']['display_uri'],
   
$combine = FALSE
 
);

 
// TODO: Accommodate multiple product Add to Cart forms better; i.e. should it
  // display the product title or the product display node title?
 
drupal_set_message(t('%title added to <a href="!cart-url">your cart</a>.', array('%title' => $product->title, '!cart-url' => url('cart'))));
}
?>

The first function is hook_form_alter. We use it to overwrite the default commerce_cart_add_to_cart_form_submit in $form['#submit']  . This function is a little bit more robust than say $form['#submit'] = 'yourmodule_handler'; since I want to make sure I don't override any additional handles which future modules might attach to this form.

The second function, is that submit handler we create. It's just a copy & paste from commerce_cart_add_to_cart_form_submit() with only combine = FALSE added. This ensure that your line items are added one at a time.

I've asked the Drupal Commerce developers two things:

  1. Turn this combine variable into a variable_get('commerce_cart_combine', TRUE) type variable. This way we can more easily modify it.
  2. Consider changing how line items & products get added to the cart. If two line items are the same product, but have different titles, instead of increasing the quality, they should get added as two seperate entries.

Hopefully these improvements will make everyones life much easier in the future.