Update Drupal Commerce Order URL's

Drupal Commerce Tip
Change Drupal Commerce admin order URLs to use order number or id
Mon, June 16th, 2014
andyg5000
commerce

Today I had the task of changing the Drupal Commerce admin order URLs so that the order number can be used to load the view, edit, delete or any other defined order admin URL. The reason for this is that store administrators wanted to easily be able to switch orders by changing a value in the URL and they always reference order number instead of id. 

There are a few requirements for this to work. First off, we must know that an order number will never match the order id of another order in the system. In our instance, we came up with a specific routine for setting order numbers on checkout complete (another blog post?). 

Secondly, Drupal uses string matching to determine which load function to call based on the wildcard in the menu item. For example, admin/commerce/orders/%commerce_order will call commerce_order_load(). The only way to call another load function is to replace %commerce_order in the menu item with your own function prefix. In our case we'll use mymodule_commerce_order_load(). 

Lastly, we must maintain the default order load URLs so that form redirects and other linkage in Drupal Commerce core still work. For example, when you save changes to an order, Drupal Commerce will redirect to the URL using the order id. 

Steps to build this out

Create a hook_menu_alter() so that we can loop thorough each of the commerce admin urls and replace the function loader wildcard. 
The nice thing about doing it in a loop like this is that other modules declaring the same URL base (ie: Commerce Payment UI) will also be replaced.

<?php
/**
 * Implements hook_menu_alter().
 */
function mymodule_menu_alter(&$items) {

  // Loop through each of the menu items and override all administrative
  // order URLs with our auto-loader wildcard string. This allows us to use the
  // order number or order id in the admin order URLs.
  foreach ($items as $key => $item) {
    if (strstr($key, 'admin/commerce/orders/%commerce_order')) {
      $new_key = str_replace('%commerce_order', '%mymodule_commerce_order', $key);
      $items[$new_key] = $items[$key];
      unset($items[$key]);
    }
  }
}
?>

Next we need to create our loader function that attempts to load the order by its order number first. If an order can successfully be loaded by the order number, we return that. If not we default to the regular load function.

<?php
/**
 * Custom order load override to attempt loading by order number first.
 */
function mymodule_commerce_order_load($order_id) {
  // Attempt to load the order by order number.
  $order = commerce_order_load_by_number($order_id);
  if (!empty($order)) {
    return $order;
  }

  // Default to the standard commerce load by id.
  return commerce_order_load($order_id);
}
?>

Now you can access the admin order URLs by going to admin/commerce/orders/ORDER_ID or admin/commerce/orders/ORDER_NUMBER.

Note: The revisions page and any others that use views contextual filters expecting the order id will not work without editing the view or rewriting the page callback for that menu item.