1

So, I am trying to implement a drag and drop interface for WooCommerce order line items so that I can control the order in which each line item appears (without having to add each item to the cart in the proper sequence). This functionality overrides the default WooCommerce functionality which displays order items (in the frontend shopping cart, frontend order review, and backend order admin) in the order which they were put into the cart (read, ascending order_item_id from the woocommerce_order_items table).

My solution involves using the woocommerce_order_itemmeta table to store an additional meta key (_order_item_order) / value (integer) pair, which are used in a custom query, specifically in the ORDER BY:

$line_items = $wpdb->get_results( $wpdb->prepare( "
SELECT          woi.order_item_id, woi.order_item_name, woi.order_item_type
FROM            {$wpdb->prefix}woocommerce_order_items woi
INNER JOIN      wp_woocommerce_order_itemmeta woim1 ON 
                (woi.order_item_id=woim1.order_item_id AND woim1.meta_key='_order_item_order' AND CAST(woim1.meta_value AS CHAR) != '')
WHERE 
                woi.order_item_type = 'line_item'
AND
                woi.order_id = %d
ORDER BY        woim1.meta_value ASC
", $order->id ) );

Looking at the get_items() public function found on lines 247 through 283 of the class-wc-order.php, shows the exact functionality which I am trying to alter with my custom query. (The query above has been tested and returns what I want.)

Josh Kohlbach from 'Code My Own Road' shared a method he used to order his order items by the order_item_name in the woocommerce_order_items table, by hooking into woocommerce_order_get_items.

Other than this, I have not come across anything which has helped me wrap my head around what I need to do. I am admittedly ignorant about the proper application of filters in WordPress — despite having read, re-read, and read yet again various tutorials and having consulted the WP documentation (on ThemeShaper, WP Tuts+, and WordPress Codex to name a few...)

So, my question is: how do I properly utilize filtering to implement my query?

  • You can overload that `woocommerce_order_get_items` hook by creating a function out of your query, and then using `add_filter('woocommerce_order_get_items, 'your_function_name', 999);`. The 999 sets the priority of your function. Having it so high will ensure that it runs last. This is not necessarily optimal because you are hitting the database twice (once in `get_items()` and once in your function), but I'm not sure how to extend `get_items()` without directly modifying the function as you need meta information that `get_items()` is not fetching. – Tom Lagier Dec 12 '13 at 01:19
  • Hi @TomLagier, thanks for the reply. I share your reservation that I am making two calls on the database — that is why I would really like to directly change the core functionality, not just create a second function. Is it possible to do this by extending the `WC_Order` class? I'm barely tweaking `WC_Order`'s `get_items()`... – forgetfuljames Dec 12 '13 at 16:11
  • You could certainly extend `WC_Order`, which would get you access to other methods that it contains - however neither you nor native `get_items()` are really using any helper methods. If you overloaded `get_items()` in a child class, you would have to replace any references to `WC_Order` with your child class, and at that point I think you are better off simply modifying the `get_items()` function. [Here](http://stackoverflow.com/questions/137006/redefine-class-methods-or-class) is a description of exactly what we're trying to achieve, and an explanation about why it isn't supported. – Tom Lagier Dec 12 '13 at 21:29
  • @TomLagier, thanks again. I will take a closer look at the 'monkey patching' mentioned in the linked SO thread tomorrow. I'll let you know if I get it to work. Just to make sure that I'm tracking, we're basically saying that we have 3 options: 1. Creating a replacement function for `get_items()` to be applied with the `woocommerce_order_get_items` filter 2. Extending the `WC_Order` class with the replacement `get_items()` method 3. 'Monkey Patching' `get_items()` with [runkit](http://docs.php.net/manual/en/book.runkit.php) to alter the method at runtime... which is an iffy solution – forgetfuljames Dec 13 '13 at 00:03
  • The 2nd option you present is the best, I don't know if it's possible. You can inherit the already defined class, but without directly replacing the `get_items()` method in `class-wc-order.php`, I'm not sure how you can overload it. Method 3 shouldn't be used, and method 1 incurs 2 database calls. – Tom Lagier Dec 13 '13 at 20:03

0 Answers0