How to sort WooCommerce order items by meta values

Had a request by a client to sort / rearrange the individual order items that you see on the WordPress admin side (i.e. on the WooCommerce “Edit order” page ).

For this particular case, each order item has a date string saved in the item meta. They wanted the items to be sorted from earliest date to latest date.

Need to somehow automatically sort like this

After some searching, I found this super old post by Josh Kohlbach which I modified to work. Here is how I did it.

Step 1: ‘woocommerce_order_get_items’ filter

The first step was getting the correct WooCommerce hook to use. Luckily, there is a filter hook to use: woocommerce_order_get_items

It returns an array of the items in the order.

So, let’s set up the basic snippet:

function custom_order_items_sort($items, $order) {
	// code goes here..
  
  	return $items; 
}
 
add_filter('woocommerce_order_get_items', 'custom_order_items_sort', 10 ,2);

Step 2: Use PHP function ‘uasort’

We can then use the builtin PHP function uasort, which allows us to sort an array with our own custom sorting function.

The basic syntax is something like uasort(array, sortingFunction).

The array we are using $items is provided by the filter hook. So, now all we need to do is create a new custom sorting function that I have named my_sorting_function. You could do it inline also, but I think it’s easier to read this way.

function custom_order_items_sort($items, $order) {
  uasort($items, my_sorting_function); // sorts array $items with `my_sorting_function`

  return $items; 
}

function my_sorting_function ($a, $b) { // our new custom sorting/comparison function
  //sorting code goes here
}
 
add_filter('woocommerce_order_get_items', 'custom_order_items_sort', 10 ,2);

Step 3: Building a custom sorting function

uasort works by comparing two items at a time (i.e. $a, $b) from the array (i.e. $items), and sorts them according to the provided function (i.e. my_sorting_function).

The custom sorting function must compare the two items, and return an integer (usually -1, 0, 1) to arrange the items in the needed order. Here’s a basic example of how sorting works…

uasort basic example

Our array we want to sort is [2, 7, 3] . If we want to use uasort to sort from smallest to largest, the basic sorting function would look something like this:

//just an example
function example_sort_function ($a, $b) {
	if ($a == $b) return 0; // if they are the same, we don't need to rearrange order
 	if ($a < $b) return -1; // if a less than b, move it before b 
  	if ($a > $b) return 1; // if a is greater than b, move it after b 
}

This function would first compare 2 ($a) and 7 ($b). Since 2 < 7, it would return a value of -1, which would move 2 before 7. Since 2 is already before 7, nothing happens to the array.

Then, it would continue to the next pair and compare 7 with 3. Since 7 > 3, it would return a value of 1, and move 7 after 3. uasort continues to loop through all possible pairs in the array until it is completely sorted. In this example, the final array is [2,3,7].

Back to our snippet…

For our snippet, we need to first get the meta data from each order item.

We can do this with get_meta_data() function, which gives us an array of all meta data for that item.

function custom_order_items_sort($items, $order) {
  uasort($items, my_sorting_function); // sorts array $items with `my_sorting_function`
  
  return $items;
}

function my_sorting_function ($a, $b) {
	$a_meta_data = $a->get_meta_data(); // get meta data for a
	$b_meta_data = $b->get_meta_data(); // get meta data for b
}
 
add_filter('woocommerce_order_get_items', 'custom_order_items_sort', 10 ,2);

Then, we need to loop through each item’s meta data to find the specific key we need. In my case, I am looking for “Date” meta key and value. I then had to convert the date string into a date object so I could compare them properly.

function custom_order_items_sort($items, $order) {
  uasort($items, my_sorting_function); // sorts array $items with `my_sorting_function`
  
  return $items;
}

function my_sorting_function ($a, $b) {
	$a_meta_data = $a->get_meta_data(); // gets array of meta data for a 
	$b_meta_data = $b->get_meta_data(); // gets array of meta data for b
  
  	// gets the date object for a 
  	foreach($a_meta_data as $key=>$meta_array) {
		if($meta_array->key == "Date") { // looking for "Date" meta key
			$a_date = $meta_array->value; // returns date string like "28/05/2022 7:00 am"
			$a_date = DateTime::createFromFormat('d/m/Y g:i a', $a_date); // convert to date object for comparison
		}
	}
  
	// gets the date object for b
  	foreach($b_meta_data as $key=>$meta_array) {
		if($meta_array->key == "Date") { 
			$b_date = $meta_array->value; 
			$b_date = DateTime::createFromFormat('d/m/Y g:i a', $b_date);
		}
	}
  
}
 
add_filter('woocommerce_order_get_items', 'custom_order_items_sort', 10 ,2);

Yes, I repeated my code. You could probably make this nicer somehow. Maybe someone out there has a suggestion?

Step 4: Final Snippet

The final step is to compare the dates and return -1, 1, or 0.

function custom_order_items_sort($items, $order) {
  uasort($items, my_sorting_function); // sorts array $items with `my_sorting_function`
  
  return $items; // don't forget to return the sorted items
}

function my_sorting_function ($a, $b) {
	$a_meta_data = $a->get_meta_data(); // gets array of meta data for a 
	$b_meta_data = $b->get_meta_data(); // gets array of meta data for b
  
  	// gets the date object for a 
  	foreach($a_meta_data as $key=>$data_array) {
		if($data_array->key == "Date") { // looking for "Date" meta key
			$a_date = $data_array->value; // returns date string "28/05/2022 7:00 am"
			$a_date = DateTime::createFromFormat('d/m/Y g:i a', $a_date); // convert to date object for comparison
		}
	}
  
	// gets the date object for b
  	foreach($b_meta_data as $key=>$data_array) {
		if($data_array->key == "Date") { 
			$b_date = $data_array->value; 
			$b_date = DateTime::createFromFormat('d/m/Y g:i a', $b_date);
		}
	}
  
	// compares the dates
	if ($a_date == $b_date) return 0; // if dates are same, do nothing
	return ($a_date < $b_date ) ? -1 : 1 ; // if a_date is earlier than b_date, moves $a before $b, else move after
  
}
 
add_filter('woocommerce_order_get_items', 'custom_order_items_sort', 10 ,2);

That’s it! 🙃

I hope this helps someone out there.

Get Weekly WordPress tips

Was this helpful? Every week, I post a useful tip about WordPress development. Subscribe below to get updates by email. (It's free 🙂)

Leave a Comment