Today I had the challenge to add some extra (programatically calculated) fields to show in commerce.module product display nodes. In our specific case it was a "base price" that should be shown additionally to calculated SALE prices.
First approach using hook_field_extra_fields
hook_field_extra_fields seemed to be a very good solutions and with extrafield_views_integration.module there was a good module solution to expose these values to views.
Everything worked fine except the fact that the product selection (options) in the product display did not correctly replace the field when changing values (AJAX update functionality).
After investigating I found out that commerce_cart_add_to_cart_form_attributes_refresh only handles extra fields containing a "theme" attribute:
// Only include extra fields that specify a theme function and that
// are visible on the current view mode.
if (!empty($product_extra_field['theme']) &&
!empty($display[$product_extra_field_name]['visible'])) {
// ...
?>
Creating a workaround without final success
So I added that attribute and a custom theme function. Now the function handled the field via AJAX but I was still missing the field display in the product node type configuration (only had it available in the product itself).
Use HOOK_commerce_product_add_extra_fields_alter to be happy!
I went on investigating and finally found a better and really handy solution in this issue: 2104341.do
Using HOOK_commerce_product_add_extra_fields_alter is very clean (patch from the issue is required!) because it handles our extra fields like native products extra fields (like "sku" for example). AJAX handling, adding the field to product and product display, etc. is done automatically.
This solution made it possible to add arbitrary extra fields to the product and product display with just some lines of code.
Code examples
Feel free to modify the example / POC from below for your own requirements:
/**
* Implements HOOK_commerce_product_add_extra_fields_alter().
* @See https://www.drupal.org/node/2104341
* !! IMPORTANT: Requires commerce_product patch #3 from https://www.drupal.org/node/2104341 !!
*
*/
function MY_MODULE_commerce_product_add_extra_fields_alter(&$extra) {
// We define our product (type "coupon") extra field in this alter function because HOOK_field_extra_fields is not being
// recognized by commerce_product_reference.
//
// We need that to show and handle the field in the product itself but also in the product display and especially its automatical AJAX handling for product selection.
// Only for product type coupon:
$extra['commerce_product']['coupon']['display']['commerce_product_coupon_regular_price'] = array(
'label' => t('Regular price'),
'description' => t('Displays the regular price. Important: This field is only displayed for products with SALE option currently active!'),
/*
* Specify theme function for commerce_product_coupon_regular_price extra_field.
* This is required because "commerce_cart_add_to_cart_form_attributes_refresh()" only adds extra_fields
* automatically to the AJAX handling (product switch) which provide a "theme" callback / parameter!
*/
// 'theme' => 'MY_MODULE_extra_field_commerce_product_coupon_regular_price',
'weight' => 10,
);
}
/**
* Implements HOOK_theme
*/
function MY_MODULE_theme() {
// #webksde#JP20150805: Add custom theme function for the extra_field.
// Theme function for commerce_product_coupon_regular_price extra_field.
// * This is required because "commerce_cart_add_to_cart_form_attributes_refresh()" only adds extra_fields
// * automatically to the AJAX handling (product switch) which provide a "theme" callback / parameter!
return array('MY_MODULE_extra_field_commerce_product_coupon_regular_price' => array());
}
/**
* Theme function for commerce_product_coupon_regular_price extra_field.
* This is required because "commerce_cart_add_to_cart_form_attributes_refresh()" only adds extra_fields
* automatically to the AJAX handling (product switch) which provide a "theme" callback / parameter!
*/
function theme_MY_MODULE_extra_field_commerce_product_coupon_regular_price($variables) {
return drupal_render($variables['commerce_product_coupon_regular_price']);
}
/**
* Implements HOOK_entity_load().
*
* @param type $entities
* @param type $type
*/
function MY_MODULE_entity_load($entities, $type) {
// Add a normal price field containing the regular price.
// Attach the field values to the entity.
if ($type == 'commerce_product') {
foreach ($entities as $entity) {
if($entity->type == 'coupon') {
// Is a coupon product type!
$regular_price = _MY_MODULE_commerce_product_regular_price_field_extra_display($entity);
$entity->commerce_product_coupon_regular_price = array('#markup' => $regular_price);
}
}
}
}
// + the concrete implementation of the normal price calculation
?>
Views integration finally
To make the values available in views without writing an own views handler, you may simply install extrafield_views_integration.module and add a callback function like this:
/**
* Implements HOOK_field_extra_fields().
*/
function MY_MODULE_field_extra_fields_alter(&$info) {
// Add a callback function to make the values available in views via extrafield_views_integration.module:
$info['commerce_product']['coupon']['display']['commerce_product_coupon_regular_price']['callback'] = '_MY_MODULE_commerce_product_regular_price_field_extra_display';
$info['node']['product_display']['display']['product:commerce_product_coupon_regular_price']['callback'] = '_MY_MODULE_commerce_product_regular_price_field_extra_display';
}
?>