Order Items Tutorial

Hey 🎉,

In this tutorial I want to talk about WooCommerce order items. To understand what I mean, just look at the screenshot below:

WooCommerce Order Items on the Edit order page: line items (products), shipping and fee.

You can see the Edit Order page with products purchased, shipping and fee. All of these are order items:

Order items has their own tables in WordPress database – {prefix}woocommerce_order_items and {prefix}woocommerce_order_itemmeta.

What is the reason in creating a separate database table for products in an order than linking them by IDs? Actually the reason is really simple. Let’s say that someone purchased a snowboard 🏂 from your store. A couple days ago the price of this snowboard has been changed… But we can not change it in that order! Did you get it?

How to do some magic with Order Items in code


Let’s begin with WC_Order’s get_items() that returns all products in an order. But why? – order items are not only products, shipping and fee are also order items, aren’t they?

Sure, get_items() returns only products because it has the only parameter $types that is line_item by default. Anyway we can pass an array instead: get_items( array('line_item', 'fee', 'shipping').

$order_items = $order_object->get_items( array('line_item', 'fee', 'shipping') );
if ( !is_wp_error( $order_items ) ) {
	foreach( $order_items as $item_id => $order_item ) {
		echo $order_item->get_order_id();
		echo $order_item->get_name();
		echo $order_item->get_total(); // item price
		echo $order_item->get_type(); // line_item | fee | shipping

		// for products only:
		echo $order_item->get_quantity(); // or $order_item['quantity'];
		echo $order_item->get_product_id(); // or $order_item['product_id'];
		echo $order_item->get_variation_id(); // or $order_item['variation_id'];
		echo $order_item->get_product(); // get the associated product

You can add a column with order items and play with code.


Actually you can add the order items to an order when you create it with wc_create_order() function. But today we’re not talking about the orders themselves, so I suppose that an order with some $order_id is already exists and we want to add order items to it in the code:

// add an order item linked to a product
$order_item_id = wc_add_order_item( $order_id, array(
	'order_item_name' => 'Some product',
	'order_item_type' => 'line_item', // product
wc_add_order_item_meta( $order_item_id, '_qty', 2, true ); // quantity
wc_add_order_item_meta( $order_item_id, '_product_id', 13, 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

// add a fee order item
$order_item_id = wc_add_order_item( $order_id, array(
 	'order_item_name' => 'Some fee',
 	'order_item_type' => 'fee',
wc_add_order_item_meta( $order_item_id, '_fee_amount', 20, true );
wc_add_order_item_meta( $order_item_id, '_line_total', 20, true );

$order = new WC_Order( $order_id );

If you would like to add a shipping order item, use shipping order_item_type and cost parameter to pass the shipping price and method_id as a shipping type.


To update the existing order items you can use wc_update_order_item( $item_id, $args), where $args is the array with the same parameters wc_add_order_item() accepts and wc_update_order_item_meta( $item_id, $key, $value, $prev_value ).

If you want to reattach an order item to another order, this code will help:

wc_update_order_item( $order_item_id, array( 'order_id' => $new_order_id ) );

If you just would like to change the quantity of the product from the above example, you can use this code:

wc_update_order_item_meta( $order_item_id, '_qty', 3 );
wc_update_order_item_meta( $order_item_id, '_line_total', 30 );


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.

Add a custom product meta to an associated order item automatically

It is very easy to do, woocommerce_add_order_item_meta action hook will help you with it:

add_action( 'woocommerce_add_order_item_meta', 'misha_order_item_meta', 10, 2 );

// $item_id – order item ID
// $cart_item[ 'product_id' ] – associated product ID (obviously)
function misha_order_item_meta( $item_id, $cart_item ) {

	// get product meta
	$event_time = get_post_meta( $cart_item[ 'product_id' ], 'event_time', true );

	// if not empty, update order item meta
 	if( ! empty( $event_time ) ) {
		wc_update_order_item_meta( $item_id, 'event_time', $event_time );

How to check if order item meta is added?

How to add a possibility for customers to provide their custom data to each product purchase. Example with names on T-Shirts

Please look on the screenshots below to understand what we are going to do step by step.

 * Step 1. Add an input field just before the "Add to cart" button
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('t-shirts', 'product_cat', $product->get_id() ) ) {

	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">


 * Step 2. Save the provided field value to the cart 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;


 * Step 3. Display "Name on T-shirt" on the pages: Cart, Checkout, Order Received
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;


 * Step 4. Add the field value to the Order Item meta
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'] );


Have a question? Scroll down to comments 👇

Misha Rudrastyh

Misha Rudrastyh

I develop websites since 2008, so it is total of 14 years of experience, oh my gosh. Most of all I love love love to create websites with WordPress and Gutenberg, some ideas and thoughts I share throughout my blog.

Need some developer help? Contact me

Follow me on Twitter