Order Items
Welcome to a complete tutorial about WooCommerce order items. Here we are going to talk about how to work with order items in code and then I have a very interesting real life example for you.
What are Order Items Exactly?
In order to understand what order items really are, let’s imagine a simple situation. In WooCommerce we have orders and we have products, right? When customer makes a purchase, an order is created, right? And somehow a purchased product is connected to this order, right? But what if tomorrow the actual price of the product is going to be changed? And the day after tomorrow it happens again?
That’s why WooCommerce has order items. They are not a post type and more than that, there are separate SQL tables in database for them – {prefix}woocommerce_order_items
and {prefix}woocommerce_order_itemmeta
.
If you open any edit order page, you would probably see something like this:

What do you think are order items on the above screenshot?
line_item
– products (and I think it is 100% clear here).fee
– yes, any additional fees added to an order are also order items.shipping
– information about shipping is also stored in database as order items.coupon
– didn’t expect that? But yes.
Get Order Items of a Specific Order
In the example below I will show you how to get WooCommerce order items by order ID. It is all about working with get_items()
method of WC_Order
object.
// some order ID we want to get order items of
$order_id = 63;
// get and order object
$order = wc_get_order( $order_id );
// let's decide what order item types we would like to get
$types = array( 'line_item', 'fee', 'shipping', 'coupon' );
// defaults to line_item which allows to get products from order
// iterating through each order item in the order
foreach( $order->get_items( $types ) as $item_id => $item ) {
// line_item | fee | shipping | coupon
$item_type = $item->get_type();
// WC_Order object
$item_order = $item->get_order();
// order ID
$item_order_id = $item->get_order_id();
// order item name (product title, name of a shipping method or coupon code)
$item_name = $item->get_name();
// product only
if( $item->is_type( 'line_item' ) ) {
// product quantity
$item_quantity = $item->get_quantity();
// product subtotal without discounts
$item_subtotal = $item->get_subtotal();
// WC_Product object
$item_product = $item->get_product();
// product ID
$item_product_id = $item->get_product_id();
// get order item variation ID
$item_product_variation_id = $item->get_variation_id();
}
// products, fees and shipping
if( $item->is_type( array( 'line_item', 'fee', 'shipping' ) ) ) {
// order item total
$item_total = $item->get_total();
}
}
Add an Order Item to an Order
Good news is that you can add any order item to an order and it doesn’t matter what order status is. It works great for paid orders as well as for unpaid ones.
The only thing you should keep in mind is when you add any order item to an existing order you should never charge extra, it could even lead to some legal issues. So just be careful with that.
Add Product
// create an order item first
$order_item_id = wc_add_order_item(
$order_id,
array(
'order_item_name' => 'Some product', // may differ from the product name
'order_item_type' => 'line_item', // product
)
);
if( $order_item_id ) {
// provide its meta information
wc_add_order_item_meta( $order_item_id, '_qty', 2, true ); // quantity
wc_add_order_item_meta( $order_item_id, '_product_id', 15, true ); // ID of the product
// you can also add "_variation_id" meta
wc_add_order_item_meta( $order_item_id, '_line_subtotal', 10, true ); // price per item
wc_add_order_item_meta( $order_item_id, '_line_total', 20, true ); // total price
}
It is not necessary to provide the actual price of the product as an order item. You can set 0
for example if you would like to add a product to an order as a gift.

You see that order total is $0.00? It is because we have to recalculate order every time we add an order item there.
$order = wc_get_order( $order_id );
$order->calculate_totals();
Another way to do that without using wc_add_order_item()
and wc_add_order_item_meta()
functions.
$item = new WC_Order_Item_Product();
$item->set_name( 'Some product' );
$item->set_quantity( 2 );
$item->set_product_id( 15 );
$item->set_subtotal( 10 );
$item->set_total( 10 );
$order = wc_get_order( $order_id );
$order->add_item( $item );
$order->calculate_totals();
Add Fee
$order_item_id = wc_add_order_item(
$order_id,
array(
'order_item_name' => 'Some fee',
'order_item_type' => 'fee',
)
);
if( $order_item_id ) {
wc_add_order_item_meta( $order_item_id, '_fee_amount', 20, true );
wc_add_order_item_meta( $order_item_id, '_line_total', 20, true );
}
Another way without wc_add_order_item()
and wc_add_order_item_meta()
functions.
$item = new WC_Order_Item_Fee();
$item->set_name( 'Some fee' );
$item->set_amount( 20 );
$item->set_total( 20 );
$order = wc_get_order( $order_id );
$order->add_item( $item );
$order->calculate_totals();
Add Shipping
$item = new WC_Order_Item_Shipping();
$item->set_method_title( 'Free shipping' );
$item->set_method_id( 'free_shipping:1' ); // set an existing Shipping method ID
$item->set_total( 0 ); // optional
$order = wc_get_order( $order_id );
$order->add_item( $item );
$order->calculate_totals();
Add Coupon to an Order Programmatically
Easier than expected.
$order = wc_get_order( $order_id );
$order->apply_coupon( 'blackfriday13' );
Well, you can also add coupons to an order using WC_Order_Item_Coupon
class and add_item()
method of WC_Order
class if you want, here I described how.
Updating Order Items
Every order item added to an order can be updated. You can update for example its name or subtotal. But in case of updating subtotals do not forget to recalculate the order.
Updating order item name:
wc_update_order_item( $order_item_id, array( 'order_item_name' => 'New product' ) );
Changing the quantity of the product order item:
wc_update_order_item_meta( $order_item_id, '_qty', 3 );
wc_update_order_item_meta( $order_item_id, '_line_total', 30 );
$order = wc_get_order( $order_id );
$order->calculate_totals();
Removing Order Items
The cool thing is that when you remove an order item with wc_delete_order_item( $order_item_id )
you shouldn’t care about deleting all its metadata, because it will be removed automatically.
But if you just want to delete some meta, wc_delete_order_item_meta( $item_id, $key, $value, $delete_all )
. The required parameters are only $item_id
and $key
.
Example. How to Add Extra Product Fields
In this example I will show you how to add extra form fields to your WooCommerce product pages and after that use the values of that fields on every stage of the purchase.
1. Add Extra Form Fields to Product Pages
As an example let’s add “Name on T-Shirt” field.
I think it is fully clear that we can not use that product custom meta as a product attribute. If you would like to allow the customer to choose the color of T-Shirts or the size of a pizza, please consider using product attributes of course.
allows you to add extra form fields to your WooCommerce product pages.
add_action( 'woocommerce_before_add_to_cart_button', 'misha_before_add_to_cart_field' );
function misha_before_add_to_cart_field() {
global $product; // awesome - we have a global product object here
// let's add the input field only for products in a specific category with "t-shirts" slug
if ( ! has_term( 'tshirts', 'product_cat', $product->get_id() ) ) {
return;
}
echo '<div class="misha-before-add-to-cart-field">
<label for="name-on-t-shirt">Name on T-Shirt: </label>
<input type="text" id="name-on-t-shirt" name="name-on-t-shirt" placeholder="Enter a name" maxlength="15">
</div>';
}

2. Product Form Field Validation
In case of adding a name on T-Shirt field validation is not necessary obviously. But sometimes you may need it. So, let’s look how to do it.
add_action( 'woocommerce_add_to_cart_validation', 'rudr_field_validation', 10, 3 );
function rudr_field_validation( $result, $product_id, $quantity ) {
if( has_term( 'tshirts', 'product_cat', $product_id ) && empty( $_REQUEST[ 'name-on-t-shirt' ] ) ) {
wc_add_notice( 'We need your name please!', 'error' );
return false;
}
return $result;
}

Once your product custom field is required, you should also consider replacing “Add to cart” with “Read more” buttons on catalog and product archive pages.
add_filter( 'woocommerce_loop_add_to_cart_link', 'rudr_add_to_cart_url', 25, 3 );
function rudr_add_to_cart_url( $html, $product, $args ) {
if( has_term( 'tshirts', 'product_cat', $product->get_id() ) ) {
return sprintf(
'<a href="%s" class="button">Read more</a>',
esc_url( $product->get_permalink() )
);
}
return $html;
}
Now we have that:

3. Display “Name on T-shirt” in Cart
// add field value to cart item data
add_filter( 'woocommerce_add_cart_item_data', 'misha_save_field_value_to_cart_data', 10, 3 );
function misha_save_field_value_to_cart_data( $cart_item_data, $product_id, $variation_id ) {
if( ! empty( $_POST[ 'name-on-t-shirt' ] ) ) { // here could be another validation if you need
$cart_item_data[ 'name-on-t-shirt' ] = sanitize_text_field( $_POST[ 'name-on-t-shirt' ] );
}
return $cart_item_data;
}
// display "Name on T-shirt" in Cart
add_filter( 'woocommerce_get_item_data', 'misha_display_field', 10, 2 );
function misha_display_field( $item_data, $cart_item ) {
if( ! empty( $cart_item[ 'name-on-t-shirt' ] ) ) {
$item_data[] = array(
'key' => 'Name on T-shirt',
'value' => $cart_item[ 'name-on-t-shirt' ],
'display' => '', // in case you would like to display "value" in another way (for users)
);
}
return $item_data;
}

4. Add Order Item Meta Finally
Action hook woocommerce_checkout_create_order_line_item
fires for every cart item and allows to process any cart item data manually. As easy as that – we are going to use add_meta_data()
method to store cart item data as order item meta data.
add_action( 'woocommerce_checkout_create_order_line_item', 'misha_add_order_item_meta', 10, 4 );
function misha_add_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( ! empty( $values[ 'name-on-t-shirt' ] ) ) {
$item->add_meta_data( 'Name on a T-Shirt', $values[ 'name-on-t-shirt' ] );
}
}
Once an order item has some meta data, it will be automatically added to the thank you page, to edit order page in WordPress admin and to emails as well.



Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
How to add this details to email?
Hey,
Please check this tutorial. And I also believe that the details should appear in emails after step 4.
Hello Misha, thank you for your response! :)
Hi! I’m looking to add a prefix to some item skus (based on product category and customer membership status) once they hit the order. How can I get at the order items and change their skus conditionally? Your Update section is helpful but I feel like there’s a hook or filter that I’m missing. Do you have an idea on what that might be? Thank you!
Hey Misha, great article for sure! I’m using a lot of what you’ve got here. Question for you, what about making the line itemmeta searchable?
Our client puts serial numbers into the line itemmeta after the order is payed but before shipping. They want to be able to look that up later, but freak… I’m running into a lot of road blocks!
Let me know if you have any thoughts.
Hey Kenny,
Thank you!
So, you would like to make it searchable on My Account page?
Hoping to make it searchable from the admin dashboard, under the “order search”. I spent a lot of time after commenting here trying to figure it out… and I’ve got nuthin. Interested to know if you think you can pull it off!
Ok, sure,
Please contact me by email and let’s do it! 🙃
Hey Misha,
How to loop through existing Item Meta Key and update it with a prefix?
Thanks
Hello Misha,
this article is very helpful , i was able to solve my problem because of this.
thanks
What a fantastic post!! So easy to follow and it works! :-). Thanks so much.
Hi Misha, thanks for very helpful article. I wonder if it is possible to add a field for each product line item – which would display current stock quantity of that product? (be it simple or variation)?
Thanks a lot in advance, this would be a huge help.
Misha!
I looking this kind of tutorial for days!
THANK YOU!
Hi,
Well done ! Thank you ! Easy and it works right away!
I was wondering how to make this field editable on the different pages (Cart, Checkout, Order Received).
So either the User or the Admin could edit this field even after the order is completed.
Thank you for your help.
Hey thanks for the awesome article.
Any idea how to get the net quantity taking into account any refunds?
The code above returns the original quantity even if there were refunds.
Thanks,
Jason