admin管理员组

文章数量:1289529

I added a filter by product to WooCommerce admin orders page, using:

function add_my_product_filter_to_orders() {
    global $typenow;

    if ($typenow !== 'shop_order') return;

    if (!current_user_can('administrator')) { return; }

    $args = array(
        'post_type'      => 'product',
        'posts_per_page' => -1,
        'tax_query'      => array(
            array(
                'taxonomy' => 'product_cat',
                'field'    => 'slug',
                'terms'    => 'test',
            ),
        ),
    );

    $products = get_posts($args);
    if (!$products) return;

    $selected_product = isset($_GET['my_product_filter']) ? esc_attr($_GET['my_product_filter']) : '';

    echo '<select name="my_product_filter">';
    echo '<option value="">filter by product</option>';
    
    foreach ($products as $product) {
        echo '<option value="' . $product->ID . '" ' . selected($selected_product, $product->ID, false) . '>' . get_the_title($product->ID) . '</option>';
    }

    echo '</select>';
}
add_action('restrict_manage_posts', 'add_my_product_filter_to_orders', 25);

We used pre_get_posts hook for filtration.

function filter_orders_by_my_product($query) {
    global $pagenow, $typenow, $wpdb;

    if ($pagenow !== 'edit.php' || $typenow !== 'shop_order' || empty($_GET['my_product_filter'])) {
        return;
    }

    $product_id = intval($_GET['my_product_filter']);

    $order_ids = $wpdb->get_col($wpdb->prepare("
        SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items oi
        JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
        WHERE oim.meta_key = '_product_id' AND oim.meta_value = %d
    ", $product_id));

    if (!empty($order_ids)) {
        $query->query_vars['post__in'] = $order_ids;
    } else {
        $query->query_vars['post__in'] = array(0);
    }
}
add_action('pre_get_posts', 'filter_orders_by_my_product');

On start the dropdown is displayed and everything is fine, but after filtering, it disappears and $products = get_posts($args) returns an empty array after filtration. How to fix that issue?

I added a filter by product to WooCommerce admin orders page, using:

function add_my_product_filter_to_orders() {
    global $typenow;

    if ($typenow !== 'shop_order') return;

    if (!current_user_can('administrator')) { return; }

    $args = array(
        'post_type'      => 'product',
        'posts_per_page' => -1,
        'tax_query'      => array(
            array(
                'taxonomy' => 'product_cat',
                'field'    => 'slug',
                'terms'    => 'test',
            ),
        ),
    );

    $products = get_posts($args);
    if (!$products) return;

    $selected_product = isset($_GET['my_product_filter']) ? esc_attr($_GET['my_product_filter']) : '';

    echo '<select name="my_product_filter">';
    echo '<option value="">filter by product</option>';
    
    foreach ($products as $product) {
        echo '<option value="' . $product->ID . '" ' . selected($selected_product, $product->ID, false) . '>' . get_the_title($product->ID) . '</option>';
    }

    echo '</select>';
}
add_action('restrict_manage_posts', 'add_my_product_filter_to_orders', 25);

We used pre_get_posts hook for filtration.

function filter_orders_by_my_product($query) {
    global $pagenow, $typenow, $wpdb;

    if ($pagenow !== 'edit.php' || $typenow !== 'shop_order' || empty($_GET['my_product_filter'])) {
        return;
    }

    $product_id = intval($_GET['my_product_filter']);

    $order_ids = $wpdb->get_col($wpdb->prepare("
        SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items oi
        JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
        WHERE oim.meta_key = '_product_id' AND oim.meta_value = %d
    ", $product_id));

    if (!empty($order_ids)) {
        $query->query_vars['post__in'] = $order_ids;
    } else {
        $query->query_vars['post__in'] = array(0);
    }
}
add_action('pre_get_posts', 'filter_orders_by_my_product');

On start the dropdown is displayed and everything is fine, but after filtering, it disappears and $products = get_posts($args) returns an empty array after filtration. How to fix that issue?

Share Improve this question edited Feb 21 at 17:48 LoicTheAztec 254k24 gold badges396 silver badges443 bronze badges asked Feb 20 at 15:10 DiasDias 1926 bronze badges 3
  • Your current provided code is incomplete, as alone it doesn't provoke the issue you describe. You need to provide in your question all the related code that is involved, including the missing function code used with pre_get_posts hook. Nobody can guess things by magic. – LoicTheAztec Commented Feb 20 at 16:27
  • i added but this part is useless... I solved it by replacing get_posts with a mysql query – Dias Commented Feb 20 at 17:08
  • It's not useless as we can't see the issue you describe, without it. see my answer below. – LoicTheAztec Commented Feb 20 at 19:28
Add a comment  | 

1 Answer 1

Reset to default 2

Instead of making an additional query to filter orders, it's much better and lightweight to change the original orders query using posts_request hook...

It also solves the issue where your custom filter dropdown disappears.

Try the following revised code:

add_action('restrict_manage_posts', 'add_custom_product_filter_to_orders', 25);
function add_custom_product_filter_to_orders() {
    global $typenow, $pagenow;

    if ( 'edit.php' === $pagenow && 'shop_order' === $typenow ) :

    $products = get_posts( array(
        'post_type'      => 'product',
        'posts_per_page' => -1,
        'tax_query'      => array( array(
            'taxonomy' => 'product_cat',
            'field'    => 'slug',
            'terms'    => 'test',
        ) ),
    ) );

    if ( !$products ) return;

    $selected_product = isset($_GET['product_id']) ? absint($_GET['product_id']) : '';

    printf('<select name="product_id">
    <option value="">%s</option>', esc_html__('All products') );
    
    foreach ($products as $product) {
        printf('<option value="%d" %s>%s</option>', $product->ID,  selected($selected_product, $product->ID, false), get_the_title($product->ID) );
    }

    echo '</select>';
    endif;
}

// Change the main SQL query
add_filter('posts_request', 'change_admin_orders_request_by_product', 20, 2);
function change_admin_orders_request_by_product($sql, $query) {
    global $typenow, $pagenow, $wpdb;

    if ( is_admin() && 'edit.php' === $pagenow && 'shop_order' === $typenow
    && isset($_GET['product_id']) && ! empty($_GET['product_id']) ) {
        $join = " INNER JOIN {$wpdb->prefix}woocommerce_order_items AS oi
            ON {$wpdb->prefix}posts.ID = oi.order_id
            INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS oim
            ON oi.order_item_id = oim.order_item_id";
        $and = $wpdb->prepare(" 
            AND oim.meta_key = '_product_id' AND oim.meta_value = %d", absint($_GET['product_id']) );
            
        $sql = str_replace( "FROM wp_posts", "FROM wp_posts  {$join}", $sql );
        $sql = str_replace( "WHERE 1=1  AND", "WHERE 1=1  {$and} AND", $sql );
    }
    return $sql;
}

Code goes in functions.php file of your child theme (or in a plugin). Tested and works.

Filtering orders for a product with results:

Filtering orders for a product without results:

The custom dropdown filter is still displayed.

Note: This code will not when High Performance Order Storage is enabled.

本文标签: phpCustom select disappears after filtering on WooCommerce admin orders pageStack Overflow