I'm trying to sort my Woocommerce orders by _payment_method on the 'Orders' admin screen (edit.php?post_type=shop_order).

So far I am able to sort by 'Pay_by_phone', but it sorts everytime the page loads, which of course is not ideal.

function filter_orders_pay_by_phone($query) {
    global $pagenow;
    $qv = &$query->query_vars;

    if ( $pagenow == 'edit.php' &&
        isset($qv['post_type']) && $qv['post_type'] == 'shop_order' ) {
        $query->set('meta_key', '_payment_method');
        $query->set('meta_value', 'Pay by phone');

    return $query;
add_filter('pre_get_posts', 'filter_orders_pay_by_phone');

I need to insert a button, or another sort option to this admin page called "Phone Orders" (the current options are All, Trash, Processing etc...) that upon click causes a page refresh and adds this filter so only those orders are displayed.

I am unaware of any hooks that I can use to generate this functionality.

1 Answer 1


Here I created a function to get all possible meta_values of a certain meta_key. Then I hooked restrict_manage_posts to add the new select field of meta_values to the filter form. Finally I hooked parse_query to add the new filter to the post query.

// Global to be used to stop filter from running on get_posts() in get_meta_values()
$GLOBALS['run_meta_filter_on_query'] = true;

// function to grab all possible meta values of the chosen meta key in this case '_payment_method'
function get_meta_values( $meta_key,  $post_type = 'post' ) {

    $GLOBALS['run_meta_filter_on_query'] = false;
    $posts = get_posts(
            'post_type' => $post_type,
            'meta_key' => $meta_key,
            'posts_per_page' => -1,
    $GLOBALS['run_meta_filter_on_query'] = true;

    $meta_values = array();
    foreach( $posts as $post ) {
        $meta_values[] = get_post_meta( $post->ID, $meta_key, true );

    return $meta_values;


//Hook the filter options form

function add_meta_value_to_posts(){

    // only add filter to shop_order
    global $post_type;
    if( $post_type == 'shop_order' ) {

        // function to grab all possible meta values of the chosen meta key in this case '_payment_method'
        $meta_values = get_meta_values('_payment_method', 'shop_order');

        // Generate select field from meta values
        echo '<select name="_payment_method" id="_payment_method">';

            $all_selected = sanitize_text_field($_GET['_payment_method']) == 'all' ? ' selected' : '';
            echo '<option value="all"'.$all_selected.'>All</option>';

            foreach ( $meta_values as $meta_value ) {
                $selected = sanitize_text_field($_GET['_payment_method']) == $meta_value ? ' selected' : '';
                echo '<option value="'.$meta_value.'"'.$selected.'>'.$meta_value.'</option>';

        echo '</select>';



// Hook parse_query to add new filter parameters

function filter_posts_per_meta_value( $query ) {

    global $pagenow, $post_type;
    // Only add parmeeters if on shop_order and if all is not selected
    if( $pagenow == 'edit.php' && $post_type == 'shop_order' && !empty($_GET['_payment_method']) && $_GET['_payment_method'] != 'all' && $GLOBALS['run_meta_filter_on_query'] ) {

        $query->query_vars['meta_query'][] = array(
            'key' => '_payment_method',
            'value' => $_GET['_payment_method'],
            'compare' => '=',



Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.