Change add to cart button text + url to My account Order view page when customer has previously bought the product

The idea is to change the add to cart button text + url on WooCommerce shop and archives pages and the single product page.

The add to cart url should point to the view order detail page if the customer has previously bought the product.

Based on Custom add to cart button, if the customer has bought previously the product answer code, I was able to write the following code:

add_filter( 'woocommerce_loop_add_to_cart_link', 'customizing_add_to_cart_button', 10, 2 );
function customizing_add_to_cart_button( $link, $product ){

    $bought = false;

    if( is_user_logged_in() ){

        $customer_orders = get_posts( array(
            'numberposts' => -1,
            'meta_key'    => '_customer_user',
            'meta_value'  => get_current_user_id(),
            'post_type'   => 'shop_order', // WC orders post type
            'post_status' => 'wc-completed' // Only orders with status "completed"
        ) );

        // Going through each current customer orders
        foreach ( $customer_orders as $customer_order ) {
            $order = wc_get_order( $customer_order->ID );
            // Going through each current customer order items
            foreach($order->get_items() as $item_id => $item_values){
                if($item_values['product_id'] == $product->id){
                    $bought = true;
                    break;
                }
            }
        }
    }

    if($bought){

        // ==> SET HERE YOUR
        // CUSTOM ADD TO CART text and link
        $add_to_cart_url = site_url('//*custom_link here i want set to open view order in my account/view order/{order_id} of bought product button view order now*/  /');
        $button_text =  __('View order now', 'woocommerce');

    } 
         // for the product ID 45 (for example)
        if( $product->id == 45 ){
            $add_to_cart_url = site_url('/custom-link/product-45/');
            $button_text =  __('View now product 45', 'woocommerce');
        }

else {

        $add_to_cart_url = $product->add_to_cart_url();
        $button_text =  $product->add_to_cart_text();

    }

    $link = sprintf( '<a href="%s" rel="nofollow" data-product_id="%s" data-product_sku="%s" data-quantity="%s" class="button product_type_%s">%s</a>',
        esc_url( $add_to_cart_url ),
        esc_attr( $product->id ),
        esc_attr( $product->get_sku() ),
        esc_attr( isset( $quantity ) ? $quantity : 1 ),
        esc_attr( $product->product_type ),
        esc_html( $button_text )
    );

    return $link;
}

What currently works:

  • Code check the logged-in user bought product.
  • The add to cart button text changed to "view order now"

What I need help with:

  • Obtaining the correct url so that the user able to download the file via the My account Order view page and don't worry by finding in the list of orders in the my account section.

I took help from WooCommerce account endpoint - view order, but I didn't figured out how to write. Any advice?


Solution 1:

To get the url from the My account Order view page you could use the get_view_order_url() function. However, since you then need access to the $order object I wrote a custom function that gets the order ID by the product ID

function get_order_id_by_product_id( $product_id ) {
    global $wpdb;
    
    // Get user ID
    $user_id = get_current_user_id();

    // Get order ID by product ID
    $order_id = $wpdb->get_var( "
        SELECT p.ID FROM {$wpdb->prefix}posts AS p
        INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
        INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
        WHERE p.post_status IN ( 'wc-completed' )
        AND pm.meta_key = '_customer_user'
        AND pm.meta_value = '$user_id'
        AND woim.meta_key IN ( '_product_id', '_variation_id' )
        AND woim.meta_value = '$product_id'
        LIMIT 1
    " );

    // Return
    return $order_id;
}
  • The advantage of this function is that you do not have to go through all orders and it is therefore much lighter and faster
  • This function assumes that a product only appears 1x in an order per customer, therefore also the use of LIMIT 1
  • Works for orders with status wc-completed

On WooCommerce shop and archives pages you can use the woocommerce_loop_add_to_cart_link filter hoook. This allows you to rewrite the output of the add to cart button based on certain conditions. Once we know the order ID via the custom function, we will create our url which will refer to the My account Order view page

// On WooCommerce shop and archives pages
function filter_woocommerce_loop_add_to_cart_link( $sprintf, $product, $args ) {
    // Only for logged in users
    if ( ! is_user_logged_in() ) return $sprintf;
    
    // Only for single type products
    if ( ! $product->is_type( 'simple' ) ) return $sprintf;
    
    // Call fuction and get order ID
    $order_id = get_order_id_by_product_id( $product->get_id() );
    
    // When NOT empty
    if ( ! empty( $order_id ) ) {
        // Setting
        $button_text_view_order = __( 'View order now', 'woocommerce' );
        
        // Get view order url
        $view_order_url = wc_get_endpoint_url( 'view-order', $order_id, wc_get_page_permalink( 'myaccount' ) );
        
        // New link + text
        $sprintf = sprintf(
            '<a href="%s" class="%s">%s</a>',
            esc_url( $view_order_url ),
            esc_attr( isset( $args['class'] ) ? $args['class'] : 'button' ),
            esc_html( $button_text_view_order )
        );
    }
    
    return $sprintf;
}
add_filter( 'woocommerce_loop_add_to_cart_link', 'filter_woocommerce_loop_add_to_cart_link', 10, 3 );

For the single product page, an overwrite of the template file would actually be necessary, since the add to cart button does not immediately contain a specific hook to overwrite it.

However, we can replace the default button with our own custom button via a workaround

// On single product page, replacing the single add to cart product button by a custom button
function action_woocommerce_single_product_summary() {
    global $product;

    // Only for logged in users
    if ( ! is_user_logged_in() ) return;
    
    // Only for single type products
    if ( ! $product->is_type( 'simple' ) ) return;
    
    // Call fuction and get order ID
    $order_id = get_order_id_by_product_id( $product->get_id() );
    
    // When NOT empty
    if ( ! empty( $order_id ) ) {
        // Remove default add to cart button and add a custom one
        remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
        
        // Add action, priority 30 and pass data to add action function
        add_action( 'woocommerce_single_product_summary', function() use ( $order_id ) {
            // Setting
            $button_text_view_order = __( 'View order now', 'woocommerce' );
            
            // Get view order url
            $view_order_url = wc_get_endpoint_url( 'view-order', $order_id, wc_get_page_permalink( 'myaccount' ) );
            
            echo '<a href="' . $view_order_url . '" class="single_add_to_cart_button button alt">' . $button_text_view_order . '</a>';
        }, 30, 0 );
    }
}
add_action( 'woocommerce_single_product_summary', 'action_woocommerce_single_product_summary', 1 );

This code works for products of type 'single' but can be further expanded to meet your needs