2

On the order edit admin screen at /admin/commerce/orders/270/edit, how do you customize the Order Items table? I've been looking all day and can't figure it out. There's a view that controls this same table on a user's orders tab, but it doesn't seem to affect this screen. All I want to do is add a column for the item's SKU and I would have thought that to be a pretty straightforward change.

Thanks!

2 Answers 2

4

I mentioned this above in a comment, but the reason this takes more work than editing a View is because there's no simple way for a field widget to use Views to build part of a form. You can easily use Views to render content, and you can even use Views to construct a complete form, but using it to embed part of a form inside another form is not likely to happen.

There is a more straightforward way to customize the table if you're interested, based on the fact that the Inline Entity Form module gives you two ways to override the fields shown in the table of its "complex" widget.

If you anticipate making more holistic changes to the behavior of the inline form, you might consider setting a custom inline_form handler for order items via hook_entity_type_alter():

use Drupal\custom\Form\CustomOrderItemInlineForm;

/**
 * Implements hook_entity_type_alter().
 */
function custom_entity_type_alter(&$entity_types) {
  $entity_types['commerce_order_item']->setHandlerClass('inline_form', CustomOrderItemInlineForm::class);
}

Your handler class would inherit the default OrderItemInlineForm class and override its getTableFields() method to return a different fields array that adds your SKU field in the right place:

namespace Drupal\custom\Form;

use Drupal\commerce_order\Form\OrderItemInlineForm;

/**
 * Defines the inline form for order items.
 */
class CustomOrderItemInlineForm extends OrderItemInlineForm {

  /**
   * {@inheritdoc}
   */
  public function getTableFields($bundles) {
    $fields = parent::getTableFields($bundles);

    $fields['sku'] = [
      'type' => 'callback',
      'callback' => 'custom_order_item_sku',
      'label' => t('SKU'),
      'weight' => 2,
    ];

    $fields['unit_price']['weight'] = 3;
    $fields['quantity']['weight'] = 4;

    return $fields;
  }
}

Alternately, you can just implement hook_inline_entity_form_table_fields_alter() to do the same thing:

/**
 * Implements hook_inline_entity_form_table_fields_alter().
 */
function custom_inline_entity_form_table_fields_alter(&$fields, $context) {
  // Note: you'd want to check the context here to only alter the desired table.
  $fields['sku'] = [
    'type' => 'callback',
    'callback' => 'custom_order_item_sku',
    'label' => t('SKU'),
    'weight' => 2,
  ];

  $fields['unit_price']['weight'] = 3;
  $fields['quantity']['weight'] = 4;
}

Note that when you define the field for the Inline Entity Form table to include, since the SKU doesn't come from the order item itself (which is what is rendered in that table), you can't just use the 'field' type - you have to define a custom callback. Your callback is a simple function that can load the purchased entity from the order item, confirm it has a SKU, and then return it for display:

/**
 * Returns the SKU for the product referenced by an order item.
 */
function custom_order_item_sku($entity, $variables) {
  // Extract the purchased entity from the order item.
  $purchased_entity = $entity->getPurchasedEntity();

  // If the purchased entity couldn't be loaded or doesn't have a SKU...
  if (!$purchased_entity || !$purchased_entity->hasField('sku')) {
    // Return a placeholder string.
    return '-';
  }
  else {
    // Otherwise, return the SKU itself.
    return $purchased_entity->getSku();
  }
}

I tested this myself locally and it worked like a charm. : )

0
0

OK, this was far more challenging that I would have ever imagined it would be. I'm posting what I did here in case it helps anyone, but if you have the easy way to do this that I just completely failed to see, by all means share. Thanks!

First, I had to add a hook_form_alter to add a 'purchased_entity' field to the table:

/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id){
  switch($form_id){
    case 'commerce_order_[order_type]_edit_form':
      $form['order_items']['widget']['entities']['#table_fields']['purchased_entity'] = [
        'type' => 'field',
        'label' => 'SKU',
        'weight' => 1,
      ];
      break;
  }
}

Then, I had to modify that field. This could be done with a twig template or hook in the theme, but I just kept the hook in the same module. I also checked for a specific order type - you could leave that out if you only have one type.

/**
 * Implements hook_preprocess_HOOK() for field.
 */
function mymodule_preprocess_field__purchased_entity(&$vars) {
  $route = \Drupal::routeMatch();
  if($route->getRouteName() != 'entity.commerce_order.edit_form'){
    return;
  }
  $sku = '';
  try {
    $oi = $vars['element']['#object'];
    if ($oi->getOrder()->bundle() != [order_type]) {
      return;
    }
    $sku = $vars['element']['#object']->purchased_entity->referencedEntities()[0]->sku->value;
  }
  catch (Exception $ex){
    \Drupal::logger('mymodule')->error($ex->getMessage());
  }
  $vars['items'][0]['content'] = $sku;
}
1
  • Glad you got it figured out. I was just about to reply when I saw your own answer. The reason this is more complicated than editing a View is because the table on the order edit form is generated by the Inline Entity Form module. You can use Views to create listings of entities and even to create forms, but using a View to provide the widget for an element embedded into another form is just not possible. If you wanted to, you could override the default inline_form handler in the OrderItem class, inherit the OrderItemInlineForm, and override the getTableFields() function to add a SKU. Commented Apr 14, 2020 at 21:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.