phpwordpresswoocommerceproductcategories

Allow customers to buy only one product from defined WooCommerce product category


I want to let customers buy only one product from a defined category. All products are priced as 0 (zero). Only delivery fee is charged.

I referred to the same scenario and tried this code. But this code fails for not logged in users. When I try multiple purchases I have no matter of buying. Want to limit after the first successful attempt of a purchase. I am using the child theme functions.php file.

Note: customers do not need to register in the website before doing a purchase.

add_filter('woocommerce_add_to_cart_validation','filter_add_to_cart_validation',20, 2);
function filter_add_to_cart_validation($valid, $product_id){
    $current_user = wp_get_current_user();
    if ( wc_customer_bought_product( $current_user->user_email, $current_user->ID, $product_id) && has_term( array('free-giveaway'), 'product_cat', $product_id ) ) {
        wc_add_notice( __( 'You already bought an item. Let others to buy as well.', 'woocommerce' ), 'error' );
        $valid = false;
    }
    return $valid;
}

Solution

  • Explanation/comments related to the answer:

    So you get:

    function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
        // Set (multiple) categories
        $categories = array ( 'free-giveaway', 'categorie-1' );
        
        // If passed & has category
        if ( $passed && has_term( $categories, 'product_cat', $product_id ) ) {
            // Initialize
            $value = '';
    
            // User logged in
            if ( is_user_logged_in() ) {
                // Get the current user's ID 
                $value = get_current_user_id();
            } else {
                // Get billing_email
                $value = WC()->customer->get_billing_email();
    
                // When empty
                if ( empty ( $value ) ) {
                    // Get account email
                    $value = WC()->customer->get_email();   
                }
            }
    
            // NOT empty
            if ( ! empty ( $value ) ) {
                if ( has_bought( $value ) ) {
                    // Display an error message
                    wc_add_notice( __( 'My custom error message', 'woocommerce' ), 'error' );
    
                    // False
                    $passed = false;
                }
            }       
        }
    
        return $passed;
    }
    add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
    
    // Based partially on wc_customer_bought_product(), will return a boolean value based on orders count (false for O orders and true when there is at least one paid order)
    function has_bought( $value = 0 ) {
        if ( ! is_user_logged_in() && $value === 0 ) {
            return false;
        }
    
        global $wpdb;
        
        // Based on user ID (registered users)
        if ( is_numeric( $value ) ) { 
            $meta_key   = '_customer_user';
            $meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
        } 
        // Based on billing email (Guest users)
        else { 
            $meta_key   = '_billing_email';
            $meta_value = sanitize_email( $value );
        }
        
        $paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
    
        $count = $wpdb->get_var( $wpdb->prepare("
            SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
            INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
            WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
            AND p.post_type LIKE 'shop_order'
            AND pm.meta_key = '%s'
            AND pm.meta_value = %s
            LIMIT 1
        ", $meta_key, $meta_value ) );
    
        // Return a boolean value based on orders count
        return $count > 0;
    }