Home > Blockchain >  Hide orders that contains a specific product in WooCommmerce "My account" orders table
Hide orders that contains a specific product in WooCommmerce "My account" orders table

Time:10-06

My intention is to hide orders that contains a specific product id (5). This in the WooCommmerce "My account" orders table

What I have done are the steps as described in the /myaccount/orders.php template file. Namely copied the file to my theme folder. (mytheme/woocommerce/myaccount/orders.php.)

There I replaced

<tbody>
     <?php
     foreach ( $customer_orders->orders as $customer_order ) {
         $order      = wc_get_order( $customer_order ); // phpcs:ignore
WordPress.WP.GlobalVariablesOverride.Prohibited
         $item_count = $order->get_item_count() -
$order->get_item_count_refunded();
         ?>
         <tr class="woocommerce-orders-table__row
woocommerce-orders-table__row--status-<?php echo esc_attr(
$order->get_status() ); ?> order">
         ...
         </tr>
         <?php
     }
     ?>
</tbody>

With

<tbody>
     <?php
     foreach ( $customer_orders->orders as $customer_order ) {
         $order      = wc_get_order( $customer_order ); // phpcs:ignore
WordPress.WP.GlobalVariablesOverride.Prohibited
         $item_count = $order->get_item_count() -
$order->get_item_count_refunded();

         foreach( $order->get_items() as $item ) {
             $product_id = $item->get_product_id();

             if ( $product_id != 5 ) {
                 ?>
                 <tr class="woocommerce-orders-table__row
woocommerce-orders-table__row--status-<?php echo esc_attr(
$order->get_status() ); ?> order">
                 ...
                 </tr>
                 <?php
             }
         }
     }
     ?>
</tbody>

Although this has no error messages, it does not produce the desired result.

Can someone give some advice? oh, and if there is a solution via hooks instead of overwriting the template file I'd really appreciate it.

CodePudding user response:

To answer your question via hooks, you can use the woocommerce_my_account_my_orders_query filter hook.

The code below is copied from /includes/wc-template-functions.php line 3159 - 3185 @version 2.5.0

/**
 * My Account > Orders template.
 *
 * @param int $current_page Current page number.
 */
function woocommerce_account_orders( $current_page ) {
    $current_page    = empty( $current_page ) ? 1 : absint( $current_page );
    $customer_orders = wc_get_orders(
        apply_filters(
            'woocommerce_my_account_my_orders_query',
            array(
                'customer' => get_current_user_id(),
                'page'     => $current_page,
                'paginate' => true,
            )
        )
    );

    wc_get_template(
        'myaccount/orders.php',
        array(
            'current_page'    => absint( $current_page ),
            'customer_orders' => $customer_orders,
            'has_orders'      => 0 < $customer_orders->total,
        )
    );
}

As you can see, via the hook, you can modify the arguments used by the wc_get_orders function. Which in turn is used by the wc_get_template function. The function that calls the /myaccount/orders.php template file and pass the arguments.

wc_get_orders allows us to exclude orderIDs. But no productIDs. That's why we're going to use a workaround so we can still pass the necessary orderIDs to exclude as argument.

The function to get all orders IDs for a given product ID, processed in my answer is based on Woocommerce: Get all orders for a product answer code.


So to answer your question, you get:

function get_orders_ids_by_product_id( $product_id ) {
    global $wpdb;
    
    $statuses = array_keys( wc_get_order_statuses() );
    $statuses = implode( "','", $statuses );

    $results = $wpdb->get_col("
        SELECT order_items.order_id
        FROM {$wpdb->prefix}woocommerce_order_items as order_items
        LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
        LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
        WHERE posts.post_type = 'shop_order'
        AND posts.post_status IN ('$statuses')
        AND order_items.order_item_type = 'line_item'
        AND order_item_meta.meta_key = '_product_id'
        AND order_item_meta.meta_value = '$product_id'
    ");

    return $results;
}

function filter_woocommerce_my_account_my_orders_query( $args ) {   
    // Get all orders IDs for a given product ID
    $orders_ids = get_orders_ids_by_product_id( 5 );
    
    // Existing args
    $customer = $args['customer'];
    $current_page = $args['page'];
    $paginate = $args['paginate'];

    // Get orders that aren't the current order.
    $args = array(
        'customer' => $customer,
        'page'     => $current_page,
        'paginate' => $paginate,
        'exclude'  => $orders_ids,
    );
    
    return $args;
}
add_filter( 'woocommerce_my_account_my_orders_query', 'filter_woocommerce_my_account_my_orders_query', 10, 1 );

CodePudding user response:

If you look at WC_Order->get_items this retuns WC_Order_Item not always WC_Order_Item_Product (which has get_product_id). So I would try something like this:

foreach ($customer_orders as $customer_order) {
    $order      = wc_get_order($customer_order); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
    $item_count = $order->get_item_count();

    // Always show orders by default
    $show_order = true;

    // Check if order has specific product ids
    foreach ($order->get_items() as $item) {
        if (is_a($item, 'WC_Order_Item_Product')) {
            $product_id  = $item->get_product_id();
            $product_ids = array(5); // Enter product ids here

            if (in_array($product_id, $product_ids)) {
                // Hide order
                $show_order = false;
            }
        }
    }

    if ($show_order) {
        // <tr>
        // ...
        // </tr>
    }
}
  • Related