By default, the WooCommerce orders table includes the most basic columns and filter options only. You can filter your orders by these params:
- Order status
- Order date
- Customer
Near the default filter options, you can see the search box. This filed allows you to search orders by:
- Customer’s name
- Customer’s email
- Customer’s billing and shipping data
- Products in an order
- Shipping method
In most cases, these functionalities are more than enough. But sometimes, WooCommerce websites owners and administrators need to filter orders by some parameter that is not among the standard filters.
Ready-to-use plugins
For example, we might need to filter orders by the payment method or by the chosen shipping option. By default, WooCommerce doesn’t allow us to do something like this. Luckily, a WordPress developer named Anton Bond developed the “Additional Order Filters for WooCommerce” plugin. This plugin allows us to extend the default filter options in the WooCommerce orders table. Here’re the new filter options this plugin allows to add:
- Order status
- Payment method
- Customer group
- Shipping method
- Customer email
- Customer first name
- Customer last name
- Customer billing address
- Customer billing country
- Customer phone
- Track number
- Product SKU
- Order date
- Order total
Moreover, you don’t have to use all of these filter options together. You can include and exclude them in the plugin settings.
Many thanks to Anton for the great work he has done!
Custom code
But only a few plugins are 100% all-purpose. As you might have guessed, Anton’s plugin allows us to filter the orders by the default WooCommerce fields and parameters only. The only exception is the Track Number field, which is a part of another plugin. You just can’t filter the orders by a custom field using this plugin.
But what to do if we need to filter orders by a custom meta field? Sometimes, the meta fields are a part of other plugins (e.g., you use a plugin that allows your customers to choose a shipping date). Sometimes, the meta fields were added by your theme’s developers.
In such cases, you’ll have to add some custom code. But don’t be upset: adding the custom filter options isn’t so hard as you might have thought. Today, I want to tell and show you how to add custom filters to the WooCommerce orders table without plugins.
Let’s imagine that every order in our database has a custom meta field, is_first_order. This field is a boolean field, which means that it can be true or false.
As you might have guessed, this field shows us which order was the first order for a customer on our website. We can use this data to send our new clients a welcome gift, for example.
In this article, I won’t tell you how to create and fill this field automatically. This time, we will imagine that all of this data already exists in our database: we just need to filter our orders by this field. But if you’re interested in this topic, feel free to write about that in the comments section. I’ll be happy to write a post about this.
Adding a new table column
First of all, let’s add a new column to our orders table – First order. If an order was the first order for a client, we’re going to display a ✔️ emoji in this column. If not, we just won’t show anything.
To achieve this, we can use this code:
add_filter( 'manage_edit-shop_order_columns', 'register_is_first_order_column', 10, 1 );
function register_is_first_order_column( $columns ) {
$columns['is_first_order'] = 'First order';
return $columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'display_is_first_order_column', 10, 1 );
function display_is_first_order_column( $column ) {
global $post;
if ( 'is_first_order' === $column ) {
$is_first_order = get_post_meta( $post->ID, 'is_first_order', true );
if ( false !== $is_first_order && strlen( $is_first_order ) > 0 ) {
echo "✔️";
}
}
}
And here’s what we’ve got now:
Very clearly, I think. There’re only 5 orders in the orders table on my website. If there were hundreds or thousands of orders, I just could not visually check the First order column to easily see my new customers. It’s time to start developing our custom filter.
Getting familiar with the pre_get_posts hook
In most cases, to retrieve the posts (WooCommerce orders are a custom post type too), WordPress uses the WP_Query class. Before sending a request to the database, every WP_Query object triggers the pre_get_posts action.
The pre_get_posts action takes a $wp_query object as an argument. This object is passed into an action handler by reference, which means that we can use this hook to modify a database query before it is executed.
WordPress uses the WP_Query class to display the orders in the WooCommerce orders table too, so we can use the pre_get_posts hook to modify this query.
But be careful: before modifying any query parameters, we have to make sure that we modify only the request we need. Many novices forget about the conditional statements using the pre_get_posts hook. Because of this, their action handlers modify the wrong queries, which may lead to undesirable side effects.
Here’s an example of code using the pre_get_posts hook with some conditional statements, which allow us to make sure that we’re working with the WooCommerce orders table query only:
add_action( 'pre_get_posts', 'filter_woocommerce_orders_in_the_table', 99, 1 );
function filter_woocommerce_orders_in_the_table( $query ) {
// This condition allows us to make sure that we won't modify any query that came from the frontend
if ( ! is_admin() ) {
return;
}
global $pagenow;
// This condition allows us to make sure that we're modifying a query that fires on the wp-admin/edit.php?post_type=shop_order page
if ( 'edit.php' === $pagenow && 'shop_order' === $query->query['post_type'] ) {
// Our filtering logic goes here
}
return;
}
How to use the meta_query variable to filter the posts
There’s a lot of ways we can modify a query using the pre_get_posts hook. But this time, we just need to find all orders that have the is_first_order field with a true value. To achieve this, let’s use the meta_query query variable.
Since meta_query expects nested arrays, even if you only have one query, our code should look like this.
add_action( 'pre_get_posts', 'filter_woocommerce_orders_in_the_table', 99, 1 );
function filter_woocommerce_orders_in_the_table( $query ) {
if ( ! is_admin() ) {
return;
}
global $pagenow;
if ( 'edit.php' === $pagenow && 'shop_order' === $query->query['post_type'] ) {
$meta_query = array(
array(
'key' => 'is_first_order',
'value' => 1,
'compare' => '='
)
);
$query->set( 'meta_query', $meta_query );
}
return;
}
The code above means that we want to retrieve only the posts with the is_first_order meta field with a value equal to 1. Let’s check our orders table to make sure that our code works.
Yup, it seems that everything works as expected. But we can’t control this behavior, so we need to make this filtering option dynamic. Let’s add a custom filter option – First orders only. Since our field can be only true or false, I think that a checkbox input would be the best choice.
How to add custom inputs before the orders table?
There’re two action hooks that we can use to show our custom checkbox. The first one is restrict_manage_posts. This hook triggers right before the Filter button on the Posts, and Pages list tables. You can use this hook if you want to integrate your custom filter options among the default WordPress filters.
Using this action and some basic CSS, you can achieve something like this:
And here’s a code sample you can use to achieve something similar:
<?php
add_action( 'restrict_manage_posts', 'render_custom_orders_filters' );
function render_custom_orders_filters() {
if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) {
return;
}
?>
<label>
First orders only
<input type="checkbox" name="is_first_order" id="is_first_order">
</label>
<select name="coupon">
<option value="0">All coupons</option>
</select>
<select name="region">
<option value="0">All regions</option>
</select>
<select name="state">
<option value="0">All states</option>
</select>
<select name="state">
<option value="0">All countries</option>
</select>
<input type="text" name="custom" placeholder="By tracking number">
<?php
}
Using this hook, you can keep all the filter options together as one whole. But If you’ll have a lot of options, your action block will look messy.
That’s why I personally prefer another hook when I create my custom filters – manage_posts_extra_tablenav. As the WordPress documentation says, this hook “fires immediately following the closing “actions” div in the tablenav for the posts list table“. In simple words, this hook fires right after the Filter button.
This is how your custom filters might look using this hook and some additional CSS:
In my opinion, this is much better than our previous version. Here’s a sample code you can use to achieve something similar:
<?php
add_action( 'manage_posts_extra_tablenav', 'render_custom_orders_filters' );
function render_custom_orders_filters() {
if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) {
return;
}
?>
<div class="custom-filters-container">
<label>
First orders only
<input type="checkbox" name="is_first_order" id="is_first_order">
</label>
<select name="coupon">
<option value="0">All coupons</option>
</select>
<select name="region">
<option value="0">All regions</option>
</select>
<select name="state">
<option value="0">All states</option>
</select>
<select name="state">
<option value="0">All countries</option>
</select>
<input type="text" name="custom" placeholder="By tracking number">
</div>
<?php
}
Note: even though our filters are a part of a separate block that goes after the default filter options, they’re all still belong to the one form, so you can just press the default “Filter” button, and WordPress will fetch your new filters’ values. But, from the UX point of view, it’s much better to have one more submit button at the end of your custom filters.
In this tutorial, we’re going to focus on only one custom filter – First orders only. But in the examples above, I’ve added some placeholder select boxes and inputs to show you that you can add any inputs you might need there.
Let’s add our custom checkbox
Let’s get back to our checkbox and remove the filter options we don’t need right now. Since our checkbox doesn’t take too much space, I’m going to use the restrict_manage_posts hook. Here’s the code I will use:
<?php
add_action( 'restrict_manage_posts', 'show_is_first_order_checkbox' );
function show_is_first_order_checkbox() {
?>
<label>
First orders only
<input type="checkbox" name="is_first_order" id="is_first_order" <?php echo isset( $_GET['is_first_order'] ) ? 'checked' : ''; ?>>
</label>
<?php
}
Okay, cool. Now we have our checkbox in place. This checkbox will allow us to see the first orders of our customers only. Now we need to get back to our pre_get_posts hook.
We’ll need to modify our initial code a little bit to check if the checkbox was checked. If the checkbox was checked, we’re going to pass a new meta_query condition.
add_action( 'pre_get_posts', 'filter_woocommerce_orders_in_the_table', 99, 1 );
function filter_woocommerce_orders_in_the_table( $query ) {
if ( ! is_admin() ) {
return;
}
global $pagenow;
if ( 'edit.php' === $pagenow && 'shop_order' === $query->query['post_type'] ) {
// We don't need to modify a query if a checkbox wasn't checked
if ( ! isset( $_GET['is_first_order'] ) ) {
return $query;
}
$meta_query = array(
array(
'key' => 'is_first_order',
'value' => 1,
'compare' => '='
)
);
$query->set( 'meta_query', $meta_query );
}
return;
}
Everything looks great so far. Let’s check our checkbox and filter our orders to see if our code really works.
Good news: it seems that it works just like expected. At the moment, our checkbox is checked, so I can see the first orders of my customers only.
Summary
Here’s a full version of the code:
<?php
// Register a new column that will show if an order was a first order for a customer
add_filter( 'manage_edit-shop_order_columns', 'register_is_first_order_column', 10, 1 );
function register_is_first_order_column( $columns ) {
$columns['is_first_order'] = 'First order';
return $columns;
}
// Show a checkmark emoji in the column if it's true
add_action( 'manage_shop_order_posts_custom_column', 'display_is_first_order_column', 10, 1 );
function display_is_first_order_column( $column ) {
global $post;
if ( 'is_first_order' === $column ) {
$is_first_order = get_post_meta( $post->ID, 'is_first_order', true );
if ( false !== $is_first_order && strlen( $is_first_order ) > 0 ) {
echo "✔️";
}
}
}
// Display our custom checkbox near the default WooCommerce filter options
add_action( 'restrict_manage_posts', 'show_is_first_order_checkbox' );
function show_is_first_order_checkbox() {
?>
<label>
First orders only
<input type="checkbox" name="is_first_order" id="is_first_order" <?php echo isset( $_GET['is_first_order'] ) ? 'checked' : ''; ?>>
</label>
<?php
}
// Modify a query if the checkbox was checked
add_action( 'pre_get_posts', 'filter_woocommerce_orders_in_the_table', 99, 1 );
function filter_woocommerce_orders_in_the_table( $query ) {
if ( ! is_admin() ) {
return;
}
global $pagenow;
if ( 'edit.php' === $pagenow && 'shop_order' === $query->query['post_type'] ) {
// We don't need to modify a query if a checkbox wasn't checked
if ( ! isset( $_GET['is_first_order'] ) ) {
return $query;
}
$meta_query = array(
array(
'key' => 'is_first_order',
'value' => 1,
'compare' => '='
)
);
$query->set( 'meta_query', $meta_query );
}
return;
}
You can use this code inside of your theme’s functions.php file, or you can create a plugin and use this code there. Feel free to experiment with this code and add new filter options there.
Let me know in the comments what custom filters you would like to see in the WooCommerce orders table. I’ll be happy to write more posts using your most interesting ideas.
thats nice i was hoping it shows how to add them step by step and am looking for a filter to filter orders by-products count(not a search filter) for me as a mediator to place orders by quantity. am not finding it, could you post something like that
Hi Ammar! I don’t get your idea. How exactly do you want to filter your orders? What do you mean by “filter orders by products count”?
my business model is an aggregator so if filter the order by today’s orders I wouldn’t know what exactly the items were ordered unless I go through each order separately and check what are the items were ordered today and make a list to count them to prepare a purchase request for each item separately by today’s orders to buy them in bulks from the supplier.
there are filters for items by search but I cannot go through each item to check whether this item was ordered today or not
Hello! That is very helpful, thank you! I feel like I am missing the last piece. I would like to filter the orders by shipping method title. Unfortunately it is not stored as post_meta, but in the
woocommerce_order_items
table as a row with “shipping” value inorder_item_type
column. Is there a query_var (like meta_query), which I could use to filter by shipping method titles? I’m afraid this can only be achieved by custom SQL.Hi Sanya! Thank you for your comment. Have you tried the plugin I mentioned in the Ready-to-use plugins section? You can also check the plugin’s source code to see how the “Shipping method” filter is implemented.
Hi kayart,
Is it possible to filter orders made manually by admin in the backend? or show an emoji or something like that? pls help
Hi Baqer, you can use the woocommerce_process_shop_order_meta hook to inject a custom meta field to mark manually created orders and then use this meta field for a custom filter.
Very useful tips, thank you!
We have a sales agent role, and each customer is assigned to a particular sales agent.
How exactly can we add a filter to show, on a sales agent’s dashboard, only the orders by the customers assigned to him/her?
I’m imagining, there is a meta field for an order, called “this orders customer belongs to me”. Then WordPress can choose to display the order to the sales agent or otherwise.
Any suggestions? Thanks in advance.
To add on to this comment
https://kayart.dev/how-to-add-custom-filters-in-the-woocommerce-orders-table/#comment-50
I forgot to mention that we use the Woocommerce B2B Sales Agent plugin so that on the admin’s WordPress edit page of the customer’s profile there’s a dropdown of the available sales agents. The admin can assign a sales agent to the customer.
Hello Jason! Honestly, I haven’t worked with the plugin you mentioned, but feel free to contact me directly with more details – @kayart.dev">artemy@kayart.dev
Ok, replied.
hi kaydash,
nice and helpfull.
but i have a question. Is it possible to filter orders in myaccount/orders by user ?
Hi abazar! Well, technically, yes, but what’s the point? On /my-account/orders/ page a user by default sees their own orders.
we have a wholsale customer users thats have so much orders. they wants to find whats ordered and not….
Well, I’d have to write a separate post to write a complete solution for you, but here’s what I’d suggest to you:
1) You can use this post as a reference, but some things will be completely different
2) In the post, I use restrict_manage_posts hook to show custom inputs before the table. Obviously, it’s not going to work on my-account/orders page. You can use woocommerce_before_account_orders action hook; it fires right before a user’s orders table. By using this hook, you can add your filtering form. Same logic that I used in the post.
3) To modify the default query on my-account/orders page, you’ll need to write something like this:
Note that this is not a working solution for you. To make it work, you’ll need to integrate this code with your filter form.
Also, you may consider another option. You can create a custom my-account subpage with your own table.
thanks for your help.
Thank you very much… Without this, I was in a big trouble.. again thank you very much.
I’m glad my post helped you 🙂
Hi! Is there a way to filter my customers (orders) by the city they live in? Thanks in advance, Wackers
Hi! Yes, you can do this. First, you need to get the ID of each user that lives in the city (it’s gonna be a simple get_users() query). Then, you need to filter the orders by _customer_user meta field using the IDs you got.
Hi everyone.
I want to have a ‘City’ based orders FILTER… means, we are offering EXPRESS delivery to certain cities. So we want to see those orders by selecting/choosing that city in that FILTER.
Please share code for that. and also let me know, the exact position where i need to paste the code in woocommerce plugin’s code.
Thanks.
Hello Adil! You can filter your orders by shipping city using my tutorial. This data is stored as a meta field called _shipping_city.
If you want to a copy-and-paste solution you can contact me or hire someone else.
Thanks for your explanation! Was very helpful in modifying the admin screen
You’re welcome! Thank you for your comment 🙂
Hi Artemy: this is so interesting I will save it for later but for naw, i´m looking for a way to show more orders than default (which in my case is 28 orders) so that i can have fewer pagination. Is it possible?. Thanks for your blog
Hi Roger! Yes, WordPress has a built-in feature to change this number:
How to Show More Posts Per Page in the Admin Section of WordPress
Thank you very much! i´ve been there so many times and never noticed!
Hi~
I’ve installed “Product Pre-Orders for Woocommerce” plugin, which adds a column “pre-order product” in woocommerce order list. Is it possible to filter the order by this kind of column? (set pre-order option in product page)
Technically yes, this is possible. However, to filter the orders by this column you’ll need to filter orders by order items which is a bit more complicated task. Maybe I’ll write a tutorial on this topic in the future.
You can hire someone to do this or you can contact me:
https://kayart.dev/contact
Thanks, this was nice.
I have an interesting problem.
I have a meta_tag on my orders.
I want to show all orders that have that meta_tag as a duplicate.
Some orders only have that meta_tag, but sometimes 2 different orders share the same meta_tag. I want to display those duplicates.
Any suggestions?
Hi Yoni! What do you mean by meta_tag? A meta field?
yes a meta_field on the order
So you want to have a custom filter setting that’s gonna show only the orders that have a duplicated value? Like a checkbox?
yes, but its a numeric value
If you need to search for a numeric value, you can create a regular number field and search for the value you need. In this case, you’ll get all posts that have meta_value equal to the search query.
Hi, I would like to download a plugin that divides the orders into a table with “product name”, “attribute 1” “attribute 2”, “order date” and “customer name and surname”. These attributes are tourist tour dates.Thanks
Hi! I don’t know of any ready-to-use plugins that might possibly do something like this so you’ll probably need to custom code it or hire someone.