diff --git a/includes/class-wc-product-query.php b/includes/class-wc-product-query.php index 25db7087051..b06c5fe6d09 100644 --- a/includes/class-wc-product-query.php +++ b/includes/class-wc-product-query.php @@ -13,47 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { * @category Class */ class WC_Product_Query extends WC_Object_Query { -/* - '_visibility', - '_sku', - '_price', - '_regular_price', - '_sale_price', - '_sale_price_dates_from', - '_sale_price_dates_to', - 'total_sales', - '_tax_status', - '_tax_class', - '_manage_stock', - '_stock', - '_stock_status', - '_backorders', - '_sold_individually', - '_weight', - '_length', - '_width', - '_height', - '_upsell_ids', - '_crosssell_ids', - '_purchase_note', - '_default_attributes', - '_product_attributes', - '_virtual', - '_downloadable', - '_featured', - '_downloadable_files', - '_wc_rating_count', - '_wc_average_rating', - '_wc_review_count', - '_variation_description', - '_thumbnail_id', - '_file_paths', - '_product_image_gallery', - '_product_version', - '_wp_old_slug', - '_edit_last', - '_edit_lock', -*/ + /** * Valid query vars for products. * @return array @@ -62,10 +22,51 @@ class WC_Product_Query extends WC_Object_Query { return array_merge( parent::get_default_query_vars(), array( - 'status' => 'publish', - 'type' => array( 'product', 'product_variation' ), - - + 'status' => 'publish', + 'type' => array( 'product', 'product_variation' ), + 'slug' => '', + 'date_created' => '', + 'date_modified' => '', + 'featured' => '', + 'catalog_visibility' => '', + 'description' => '', + 'short_description' => '', + 'sku' => '', + 'price' => '', + 'regular_price' => '', + 'sale_price' => '', + 'date_on_sale_from' => '', + 'date_on_sale_to' => '', + 'total_sales' => '', + 'tax_status' => '', + 'tax_class' => '', + 'manage_stock' => '', + 'stock_quantity' => '', + 'stock_status' => '', + 'backorders' => '', + 'sold_individually' => '', + 'weight' => '', + 'length' => '', + 'width' => '', + 'height' => '', + 'upsell_ids' => array(), + 'cross_sell_ids' => array(), + 'reviews_allowed' => '', + 'purchase_note' => '', + 'attributes' => array(), + 'default_attributes' => array(), + 'menu_order' => '', + 'virtual' => '', + 'downloadable' => '', + 'category_ids' => array(), + 'tag_ids' => array(), + 'shipping_class_id' => '', + 'image_id' => '', + 'download_limit' => '', + 'download_expiry' => '', + 'rating_counts' => array(), + 'average_rating' => '', + 'review_count' => '', ) ); } @@ -76,7 +77,7 @@ class WC_Product_Query extends WC_Object_Query { */ public function get_products() { $args = apply_filters( 'woocommerce_product_query_args', $this->get_query_vars() ); - $results = array();//WC_Data_Store::load( 'product' )->query( $args ); + $results = WC_Data_Store::load( 'product' )->query( $args ); return apply_filters( 'woocommerce_product_query', $results, $args ); } } diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 5fa2b9e0d4d..fab4b883a31 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1372,4 +1372,97 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da return false; } } + + + /** + * Get valid WP_Query args from a WC_Product_Query's query variables. + * + * @since 3.2.0 + * @param array $query_vars query vars from a WC_Product_Query + * @return array + */ + protected function get_wp_query_args( $query_vars ) { + + // Map query vars to ones that get_wp_query_args or WP_Query recognize. + $key_mapping = array( + 'status' => 'post_status', + 'page' => 'paged', + ); + + foreach ( $key_mapping as $query_key => $db_key ) { + if ( isset( $query_vars[ $query_key ] ) ) { + $query_vars[ $db_key ] = $query_vars[ $query_key ]; + unset( $query_vars[ $query_key ] ); + } + } + + $wp_query_args = parent::get_wp_query_args( $query_vars ); + + if ( ! isset( $wp_query_args['date_query'] ) ) { + $wp_query_args['date_query'] = array(); + } + if ( ! isset( $wp_query_args['meta_query'] ) ) { + $wp_query_args['meta_query'] = array(); + } + + $date_queries = array( + 'date_created' => 'post_date', + 'date_modified' => 'post_modified', + 'date_on_sale_from' => '_sale_price_dates_from', + 'date_on_sale_to' => '_sale_price_dates_to', + ); + foreach ( $date_queries as $query_var_key => $db_key ) { + if ( isset( $query_vars[ $query_var_key ] ) && '' !== $query_vars[ $query_var_key ] ) { + + // Remove any existing meta queries for the same keys to prevent conflicts. + $existing_queries = wp_list_pluck( $wp_query_args['meta_query'], 'key', true ); + foreach ( $existing_queries as $query_index => $query_contents ) { + unset( $wp_query_args['meta_query'][ $query_index ] ); + } + + $wp_query_args = $this->parse_date_for_wp_query( $query_vars[ $query_var_key ], $db_key, $wp_query_args ); + } + } + + if ( ! isset( $query_vars['paginate'] ) || ! $query_vars['paginate'] ) { + $wp_query_args['no_found_rows'] = true; + } + + return apply_filters( 'woocommerce_product_data_store_cpt_get_products_query', $wp_query_args, $query_vars, $this ); + } + + /** + * Query for Products matching specific criteria. + * + * @since 3.2.0 + * + * @param array $query_vars query vars from a WC_Product_Query + * + * @return array|object + */ + public function query( $query_vars ) { + $args = $this->get_wp_query_args( $query_vars ); + + if ( ! empty( $args['errors'] ) ) { + $query = (object) array( + 'posts' => array(), + 'found_posts' => 0, + 'max_num_pages' => 0, + ); + } else { + $query = new WP_Query( $args ); + } + + $products = ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) ? $query->posts : array_filter( array_map( 'wc_get_product', $query->posts ) ); + + if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) { + return (object) array( + 'products' => $products, + 'total' => $query->found_posts, + 'max_num_pages' => $query->max_num_pages, + ); + } + + return $products; + } } diff --git a/tests/unit-tests/product/query.php b/tests/unit-tests/product/query.php new file mode 100644 index 00000000000..f1d497bb43a --- /dev/null +++ b/tests/unit-tests/product/query.php @@ -0,0 +1,66 @@ +assertEquals( '', $query->get( 'weight' ) ); + $this->assertEquals( array( 'product', 'product_variation' ), $query->get( 'type' ) ); + } + + /** + * Test querying by product properties. + * + * @since 3.2 + */ + public function test_order_query_standard() { + $product1 = new WC_Product_Simple(); + $product1->set_sku( 'sku1' ); + $product1->set_regular_price( '10.00' ); + $product1->set_sale_price( '5.00' ); + $product1->save(); + + $product2 = new WC_Product_Variation(); + $product2->set_sku( 'sku2' ); + $product2->set_regular_price( '12.50' ); + $product2->set_sale_price( '5.00' ); + $product2->save(); + + // Just get some products. + $query = new WC_Product_Query(); + $results = $query->get_products(); + $this->assertEquals( 2, count( $results ) ); + + // Get products with a specific property.. + $query->set( 'sku', 'sku2' ); + $results = $query->get_products(); + $this->assertEquals( 1, count( $results ) ); + $this->assertEquals( $results[0]->get_id(), $product2->get_id() ); + + // Get products with two specific properties. + $query->set( 'regular_price', '12.50' ); + $results = $query->get_products(); + $this->assertEquals( 1, count( $results ) ); + $this->assertEquals( $results[0]->get_id(), $product2->get_id() ); + + // Get multiple products that have the same specific property. + $query = new WC_Product_Query(); + $query->set( 'sale_price', '5.00' ); + $results = $query->get_products(); + $this->assertEquals( 2, count( $results ) ); + + // Limit to one result. + $query->set( 'limit', 1 ); + $results = $query->get_products(); + $this->assertEquals( 1, count( $results ) ); + } +} diff --git a/woocommerce.php b/woocommerce.php index f46d0e3fce7..3dbafb11820 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -327,6 +327,7 @@ final class WooCommerce { include_once( WC_ABSPATH . 'includes/class-wc-order-factory.php' ); // Order factory include_once( WC_ABSPATH . 'includes/class-wc-order-query.php' ); // Order query include_once( WC_ABSPATH . 'includes/class-wc-product-factory.php' ); // Product factory + include_once( WC_ABSPATH . 'includes/class-wc-product-query.php' ); // Product query include_once( WC_ABSPATH . 'includes/class-wc-payment-tokens.php' ); // Payment tokens controller include_once( WC_ABSPATH . 'includes/class-wc-shipping-zone.php' ); include_once( WC_ABSPATH . 'includes/gateways/class-wc-payment-gateway-cc.php' ); // CC Payment Gateway