Programmatically change the "Add to Cart" line item type

Published on Thursday 27, November 2014
Drupal Commerce has a very interesting "Add to Cart" form. The add to cart form is a field formatter for the product reference field (or even entityreference field.) In a general sense, a node has a product reference field using the "Add to Cart" formatter. What makes it more unique is that this form exposes certain fields from the product entities, but also the line item type being used. Specific fields on products can be exposed as attributes, things you pick to make your purchase. Exposed line item fields allow you to get customer input values on that specific item added to the cart. Add to Cart display formatter settings The problem is that the line item type is configured per node type. Sometimes the site architecture works out and this isn't a big deal. However, sometimes the requirements would make you create far too many node types, have to manage an insane amount of field instance configurations, and cause a lot of discrepancies across configurations. Below is a snippet that allows you to programmatically set the line item type, allowing you to choose based off the form's context and reducing the required number of content types. The form is built in two ways: hook_field_formatter_view() and field_attach_view_alter(). The latter hook is when Commerce Cart works it's magic, so we need to intercept and inject ourselves right in the middle. But how? Drupal runs hooks based on module weight and then based on the name in alphabetical order. That's a pain to guess, so first add a hook_module_implements_alter() to force your hook to run first.
<?php
/**
 * Implements hook_module_implements_alter().
 */
function mymodule_module_implements_alter(&$implementations, $hook) {
  if (
$hook == 'field_attach_view_alter') {
   
$module = 'my_module';
   
$group = array($module => $implementations[$module]);
    unset(
$implementations[$module]);
   
$implementations = $group + $implementations;
  }
?>
Essentially we're hooking into Drupal's hook system to say "when invoking this alter, run ours first" in a play of hookception. Now we add our hook_attach_view_alter to modify the line item type before it hits Commerce Order
<?php
/**
 *  Implements hook_field_attach_view_alter().
 */
function mymodule_field_attach_view_alter(&$output, $context) {
 
// Loop through the fields passed in looking for any product reference fields
  // formatted with the Add to Cart form display formatter.
  // @see commerce_cart_field_attach_view().
 
foreach ($output as $field_name => $element) {
    if (!empty(
$element['#formatter']) && $element['#formatter'] == 'commerce_cart_add_to_cart_form') {
      foreach (
element_children($element) as $key) {
       
// Store the current line item type
       
$line_item_type = $output[$field_name][$key]['#arguments']['line_item']->type;

       
// Check to make sure we're not already using a custom line item type.
       
if ($line_item_type == 'product') {

         
// In this example, we'll switch line item type based on catalog tid
         
$store_category = $context['entity']->field_store_category[LANGUAGE_NONE][0]['tid'];
         
// If not set to custom line item type already, override by term ID.
         
if ($store_category == '87') {
           
$line_item_type = 'my_line_item_type';
          }
          elseif (
$store_category == '36') {
           
$line_item_type = 'my_other_line_item_type';
          }
         
$output[$field_name][$key]['#arguments']['line_item']->type = $line_item_type;
        }
      }
    }
  }
}
?>