Search code examples
phpwoocommercecustom-taxonomyproduct-variations

Product variation WP_Query with a product category in Woocommerce


With Woocommerce, I am trying to make a WP_Query for product variations post type with a product category 'Apple'.

$args = array(
    'product_cat'    => 'Apple',
    'post_type'      => array('product', 'product_variation'),
    'post_status'    => 'publish',
    'key'            => '_visibility',
    'value'          => 'visible',
    'posts_per_page' => 100,
    'taxonomy'       => 'pa_size',
    'meta_value'     => '39',
    'meta_query'     => array(
        array(
            'key'         => '_stock',
            'value'       => 0,
            'compare'     => '>'
        )
    )
);

But I can't get it work in this query. If I remove 'product_cat' => 'Apple', the query works. Why?


Solution

  • There is many mistakes in this WP_Query regarding Woocommerce products:

    • For product category and product attribute you should better use normally a tax_query instead.
    • For Product visibility, since Woocommerce 3, it's handled by product_visibility taxonomy for 'exclude-from-search' and 'exclude-from-catalog' terms.

    Important notes about Product variations:

    • Product categories (or product tags) are not handled by Product variations, but by the parent Variable product
    • Product attributes for variations are handled as post meta data with a meta_key prepended by "attribute_" and a met_value that is the term slug.
    • Product visibility is not handled in product variations, as they are not displayed in archives pages.
    • They are not displayed in archive pages (as mentioned before).

    So when using a WP_Query, you can NOT query at the same time "product" post type and "product_variation" post type as they are really different.

    To make your query work for "product_variation" post type, you need a little utility function that will get the parent variable product for a product category (or any custom taxonomy as Product tags…):

    // Utility function to get the parent variable product IDs for a any term of a taxonomy
    function get_variation_parent_ids_from_term( $term, $taxonomy, $type ){
        global $wpdb;
    
        return $wpdb->get_col( "
            SELECT DISTINCT p.ID
            FROM {$wpdb->prefix}posts as p
            INNER JOIN {$wpdb->prefix}posts as p2 ON p2.post_parent = p.ID
            INNER JOIN {$wpdb->prefix}term_relationships as tr ON p.ID = tr.object_id
            INNER JOIN {$wpdb->prefix}term_taxonomy as tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
            INNER JOIN {$wpdb->prefix}terms as t ON tt.term_id = t.term_id
            WHERE p.post_type = 'product'
            AND p.post_status = 'publish'
            AND p2.post_status = 'publish'
            AND tt.taxonomy = '$taxonomy'
            AND t.$type = '$term'
        " );
    }
    

    Code goes in function.php file of your active child theme (or active theme). Tested and works. Necessary for the WP_Query below


    Here WP_Query code for Product variations (only) related to a specific product category and specific variation attribute values:

    // Settings
    $cat_name = 'Apple'; // Product category name
    $attr_taxonomy = 'pa_size'; // Product attribute
    $attribute_term_slugs = array('39'); // <== Need to be term SLUGs
    
    $query = new WP_Query( array(
        'post_type'       => 'product_variation',
        'post_status'     => 'publish',
        'posts_per_page'  => 100,
        'post_parent__in' => get_variation_parent_ids_from_term( $cat_name, 'product_cat', 'name' ), // Variations
        'meta_query'      => array(
            'relation'    => 'AND',
            array(
                'key'     => '_stock',
                'value'   => 0,
                'compare' => '>'
            ),
            array( 
                'key'     => 'attribute_'.$attr_taxonomy, // Product variation attribute
                'value'   => $attribute_term_slugs, // Term slugs only
                'compare' => 'IN',
            ),
        ),
    ) );
    
    // Display the queried products count
    echo '<p>Product count: ' . $query->post_count . '<p>';
    
    // Displaying raw output for posts
    print_pr($query->posts);
    

    Tested and works.