Merge pull request #16291 from woocommerce/improvement/13645

Product search helpers.
This commit is contained in:
Mike Jolley 2017-08-03 08:03:37 +02:00 committed by GitHub
commit cbb8d48f5f
6 changed files with 842 additions and 173 deletions

View File

@ -0,0 +1,74 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class for parameter-based Product querying.
* Args and usage: https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
*
* @version 3.2.0
* @since 3.2.0
* @package WooCommerce/Classes
* @category Class
*/
class WC_Product_Query extends WC_Object_Query {
/**
* Valid query vars for products.
* @return array
*/
protected function get_default_query_vars() {
return array_merge(
parent::get_default_query_vars(),
array(
'status' => array( 'draft', 'pending', 'private', 'publish' ),
'type' => array_merge( array_keys( wc_get_product_types() ) ),
'limit' => get_option( 'posts_per_page' ),
'include' => array(),
'date_created' => '',
'date_modified' => '',
'featured' => '',
'visibility' => '',
'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' => '',
'reviews_allowed' => '',
'virtual' => '',
'downloadable' => '',
'category' => array(),
'tag' => array(),
'shipping_class' => array(),
'download_limit' => '',
'download_expiry' => '',
'average_rating' => '',
'review_count' => '',
)
);
}
/**
* Get products matching the current query vars.
* @return array of WC_Product objects
*/
public function get_products() {
$args = apply_filters( 'woocommerce_product_query_args', $this->get_query_vars() );
$results = WC_Data_Store::load( 'product' )->query( $args );
return apply_filters( 'woocommerce_product_query', $results, $args );
}
}

View File

@ -322,6 +322,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.

View File

@ -45,6 +45,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'_product_attributes',
'_virtual',
'_downloadable',
'_download_limit',
'_download_expiry',
'_featured',
'_downloadable_files',
'_wc_rating_count',
@ -1168,124 +1170,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
* @return array|object
*/
public function get_products( $args = array() ) {
/**
* Generate WP_Query args.
*/
$wp_query_args = array(
'post_status' => $args['status'],
'posts_per_page' => $args['limit'],
'meta_query' => array(),
'orderby' => $args['orderby'],
'order' => $args['order'],
'tax_query' => array(),
);
if ( 'variation' === $args['type'] ) {
$wp_query_args['post_type'] = 'product_variation';
} elseif ( is_array( $args['type'] ) && in_array( 'variation', $args['type'] ) ) {
$wp_query_args['post_type'] = array( 'product_variation', 'product' );
$wp_query_args['tax_query'][] = array(
'relation' => 'OR',
array(
'taxonomy' => 'product_type',
'field' => 'slug',
'terms' => $args['type'],
),
array(
'taxonomy' => 'product_type',
'field' => 'id',
'operator' => 'NOT EXISTS',
),
);
} else {
$wp_query_args['post_type'] = 'product';
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_type',
'field' => 'slug',
'terms' => $args['type'],
);
}
// Do not load unnecessary post data if the user only wants IDs.
if ( 'ids' === $args['return'] ) {
$wp_query_args['fields'] = 'ids';
}
if ( ! empty( $args['sku'] ) ) {
$wp_query_args['meta_query'][] = array(
'key' => '_sku',
'value' => $args['sku'],
'compare' => 'LIKE',
);
}
if ( ! empty( $args['category'] ) ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $args['category'],
);
}
if ( ! empty( $args['tag'] ) ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => $args['tag'],
);
}
if ( ! empty( $args['shipping_class'] ) ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_shipping_class',
'field' => 'slug',
'terms' => $args['shipping_class'],
);
}
if ( ! is_null( $args['parent'] ) ) {
$wp_query_args['post_parent'] = absint( $args['parent'] );
}
if ( ! is_null( $args['offset'] ) ) {
$wp_query_args['offset'] = absint( $args['offset'] );
} else {
$wp_query_args['paged'] = absint( $args['page'] );
}
if ( ! empty( $args['include'] ) ) {
$wp_query_args['post__in'] = array_map( 'absint', $args['include'] );
}
if ( ! empty( $args['exclude'] ) ) {
$wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
}
if ( ! $args['paginate'] ) {
$wp_query_args['no_found_rows'] = true;
}
// Get results.
$products = new WP_Query( $wp_query_args );
if ( 'objects' === $args['return'] ) {
// Prime caches before grabbing objects.
update_post_caches( $products->posts, array( 'product', 'product_variation' ) );
$return = array_filter( array_map( 'wc_get_product', $products->posts ) );
} else {
$return = $products->posts;
}
if ( $args['paginate'] ) {
return (object) array(
'products' => $return,
'total' => $products->found_posts,
'max_num_pages' => $products->max_num_pages,
);
} else {
return $return;
}
$query = new WC_Product_Query( $args );
return $query->get_products();
}
/**
@ -1372,4 +1258,295 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
return false;
}
}
/**
* Add ability to get products by 'reviews_allowed' in WC_Product_Query.
*
* @since 3.2.0
* @param string $where where clause
* @param WP_Query $wp_query
*/
public function reviews_allowed_query_where( $where, $wp_query ) {
global $wpdb;
if ( isset( $wp_query->query_vars['reviews_allowed'] ) && is_bool( $wp_query->query_vars['reviews_allowed'] ) ) {
if ( $wp_query->query_vars['reviews_allowed'] ) {
$where .= " AND $wpdb->posts.comment_status = 'open'";
} else {
$where .= " AND $wpdb->posts.comment_status = 'closed'";
}
}
return $where;
}
/**
* 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',
'include' => 'post__in',
'stock_quantity' => 'stock',
'average_rating' => 'wc_average_rating',
'review_count' => 'wc_review_count',
);
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 ] );
}
}
// Map boolean queries that are stored as 'yes'/'no' in the DB to 'yes' or 'no'.
$boolean_queries = array(
'virtual',
'downloadable',
'sold_individually',
'manage_stock',
);
foreach ( $boolean_queries as $boolean_query ) {
if ( isset( $query_vars[ $boolean_query ] ) && '' !== $query_vars[ $boolean_query ] ) {
$query_vars[ $boolean_query ] = $query_vars[ $boolean_query ] ? 'yes' : 'no';
}
}
// These queries cannot be auto-generated so we have to remove them and build them manually.
$manual_queries = array(
'sku' => '',
'featured' => '',
'visibility' => '',
);
foreach ( $manual_queries as $key => $manual_query ) {
if ( isset( $query_vars[ $key ] ) ) {
$manual_queries[ $key ] = $query_vars[ $key ];
unset( $query_vars[ $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();
}
// Handle product types.
if ( 'variation' === $query_vars['type'] ) {
$wp_query_args['post_type'] = 'product_variation';
} elseif ( is_array( $query_vars['type'] ) && in_array( 'variation', $query_vars['type'] ) ) {
$wp_query_args['post_type'] = array( 'product_variation', 'product' );
$wp_query_args['tax_query'][] = array(
'relation' => 'OR',
array(
'taxonomy' => 'product_type',
'field' => 'slug',
'terms' => $query_vars['type'],
),
array(
'taxonomy' => 'product_type',
'field' => 'id',
'operator' => 'NOT EXISTS',
),
);
} else {
$wp_query_args['post_type'] = 'product';
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_type',
'field' => 'slug',
'terms' => $query_vars['type'],
);
}
// Handle product categories.
if ( ! empty( $query_vars['category'] ) ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $query_vars['category'],
);
}
// Handle product tags.
if ( ! empty( $query_vars['tag'] ) ) {
unset( $wp_query_args['tag'] );
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => $query_vars['tag'],
);
}
// Handle shipping classes.
if ( ! empty( $query_vars['shipping_class'] ) ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_shipping_class',
'field' => 'slug',
'terms' => $query_vars['shipping_class'],
);
}
// Handle total_sales.
// This query doesn't get auto-generated since the meta key doesn't have the underscore prefix.
if ( isset( $query_vars['total_sales'] ) && '' !== $query_vars['total_sales'] ) {
$wp_query_args['meta_query'][] = array(
'key' => 'total_sales',
'value' => absint( $query_vars['total_sales'] ),
'compare' => '=',
);
}
// Handle SKU.
if ( $manual_queries['sku'] ) {
$wp_query_args['meta_query'][] = array(
'key' => '_sku',
'value' => $manual_queries['sku'],
'compare' => 'LIKE',
);
}
// Handle featured.
if ( '' !== $manual_queries['featured'] ) {
$product_visibility_term_ids = wc_get_product_visibility_term_ids();
if ( $manual_queries['featured'] ) {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => array( $product_visibility_term_ids['featured'] ),
);
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => array( $product_visibility_term_ids['exclude-from-catalog'] ),
'operator' => 'NOT IN',
);
} else {
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => array( $product_visibility_term_ids['featured'] ),
'operator' => 'NOT IN',
);
}
}
// Handle visibility.
if ( $manual_queries['visibility'] ) {
switch ( $manual_queries['visibility'] ) {
case 'search':
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'slug',
'terms' => array( 'exclude-from-search' ),
'operator' => 'NOT IN',
);
break;
case 'catalog':
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'slug',
'terms' => array( 'exclude-from-catalog' ),
'operator' => 'NOT IN',
);
break;
case 'visible':
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'slug',
'terms' => array( 'exclude-from-catalog', 'exclude-from-search' ),
'operator' => 'NOT IN',
);
break;
case 'hidden':
$wp_query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'slug',
'terms' => array( 'exclude-from-catalog', 'exclude-from-search' ),
'operator' => 'AND',
);
break;
}
}
// Handle date queries.
$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 );
}
}
// Handle paginate.
if ( ! isset( $query_vars['paginate'] ) || ! $query_vars['paginate'] ) {
$wp_query_args['no_found_rows'] = true;
}
// Handle reviews_allowed.
if ( isset( $query_vars['reviews_allowed'] ) && is_bool( $query_vars['reviews_allowed'] ) ) {
add_filter( 'posts_where', array( $this, 'reviews_allowed_query_where' ), 10, 2 );
}
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 );
}
if ( isset( $query_vars['return'] ) && 'objects' === $query_vars['return'] && ! empty( $query->posts ) ) {
// Prime caches before grabbing objects.
update_post_caches( $products->posts, array( 'product', 'product_variation' ) );
}
$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;
}
}

View File

@ -15,35 +15,12 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
/**
* Products wrapper for get_posts.
* Standard way of retrieving products based on certain parameters.
*
* This function should be used for product retrieval so that we have a data agnostic
* way to get a list of products.
*
* Args:
* status array|string List of statuses to find. Default: any. Options: any, draft, pending, private and publish.
* type array|string Product type, e.g. Default: all. Options: all, simple, external, variable, variation, grouped.
* parent int post/product parent
* sku string Limit result set to products with specific SKU.
* category array Limit result set to products assigned to specific categories by slug
* e.g. array('hoodie', 'cap', 't-shirt').
* tag array Limit result set to products assigned to specific tags (by slug)
* e.g. array('funky', 'retro', 'designer')
* shipping_class array Limit results set to products in specific shipping classes (by slug)
* e.g. array('standard', 'next-day')
* limit int Maximum of products to retrieve.
* offset int Offset of products to retrieve.
* page int Page of products to retrieve. Ignored when using the 'offset' arg.
* exclude array Product IDs to exclude from the query.
* orderby string Order by date, title, id, modified, rand etc
* order string ASC or DESC
* return string Type of data to return. Allowed values:
* ids array of Product ids
* objects array of product objects (default)
* paginate bool If true, the return value will be an array with values:
* 'products' => array of data (return value above),
* 'total' => total number of products matching the query
* 'max_num_pages' => max number of pages found
* Args and usage: https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
*
* @since 3.0.0
* @param array $args Array of args (above)
@ -51,25 +28,6 @@ if ( ! defined( 'ABSPATH' ) ) {
* paginate is true, or just an array of values.
*/
function wc_get_products( $args ) {
$args = wp_parse_args( $args, array(
'status' => array( 'draft', 'pending', 'private', 'publish' ),
'type' => array_merge( array_keys( wc_get_product_types() ) ),
'parent' => null,
'sku' => '',
'category' => array(),
'tag' => array(),
'limit' => get_option( 'posts_per_page' ),
'offset' => null,
'page' => 1,
'include' => array(),
'exclude' => array(),
'orderby' => 'date',
'order' => 'DESC',
'return' => 'objects',
'paginate' => false,
'shipping_class' => array(),
) );
// Handle some BW compatibility arg names where wp_query args differ in naming.
$map_legacy = array(
'numberposts' => 'limit',
@ -85,7 +43,8 @@ function wc_get_products( $args ) {
}
}
return WC_Data_Store::load( 'product' )->get_products( $args );
$query = new WC_Product_Query( $args );
return $query->get_products();
}
/**

View File

@ -51,29 +51,33 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case {
$this->assertEquals( 9, count( wc_get_products( array( 'return' => 'ids' ) ) ) );
// test status
// Test status.
$products = wc_get_products( array( 'return' => 'ids', 'status' => 'draft' ) );
$this->assertEquals( array( $draft->get_id() ), $products );
// test type
// Test type.
$products = wc_get_products( array( 'return' => 'ids', 'type' => 'variation' ) );
$this->assertEquals( 2, count( $products ) );
// test parent
// Test parent.
$products = wc_get_products( array( 'return' => 'ids', 'type' => 'variation', 'parent' => $variation->get_id() ) );
$this->assertEquals( 2, count( $products ) );
// test skus
// Test parent_exclude.
$products = wc_get_products( array( 'return' => 'ids', 'type' => 'variation', 'parent_exclude' => array( $variation->get_id() ) ) );
$this->assertEquals( 0, count( $products ) );
// Test skus.
$products = wc_get_products( array( 'return' => 'ids', 'sku' => 'GET TEST SKU' ) );
$this->assertEquals( 2, count( $products ) );
$this->assertContains( $product->get_id(), $products );
$this->assertContains( $external->get_id(), $products );
// test categories
// Test categories.
$products = wc_get_products( array( 'return' => 'ids', 'category' => array( $term_cat_1->slug ) ) );
$this->assertEquals( 3, count( $products ) );
// test tags
// Test tags.
$products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $term_tag_1->slug ) ) );
$this->assertEquals( 2, count( $products ) );
@ -83,31 +87,336 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case {
$products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $term_tag_1->slug, $term_tag_2->slug ) ) );
$this->assertEquals( 3, count( $products ) );
// test limit
// Test limit.
$products = wc_get_products( array( 'return' => 'ids', 'limit' => 5 ) );
$this->assertEquals( 5, count( $products ) );
// test offset
// Test offset.
$products = wc_get_products( array( 'return' => 'ids', 'limit' => 2 ) );
$products_offset = wc_get_products( array( 'return' => 'ids', 'limit' => 2, 'offset' => 2 ) );
$this->assertEquals( 2, count( $products ) );
$this->assertEquals( 2, count( $products_offset ) );
$this->assertNotEquals( $products, $products_offset );
// test page
// Test page.
$products_page_1 = wc_get_products( array( 'return' => 'ids', 'limit' => 2 ) );
$products_page_2 = wc_get_products( array( 'return' => 'ids', 'limit' => 2, 'page' => 2 ) );
$this->assertEquals( 2, count( $products_page_1 ) );
$this->assertEquals( 2, count( $products_page_2 ) );
$this->assertNotEquals( $products_page_1, $products_page_2 );
// test exclude
// Test exclude.
$products = wc_get_products( array( 'return' => 'ids', 'limit' => 200, 'exclude' => array( $product->get_id() ) ) );
$this->assertNotContains( $product->get_id(), $products );
// Test include.
$products = wc_get_products( array( 'return' => 'ids', 'include' => array( $product->get_id() ) ) );
$this->assertContains( $product->get_id(), $products );
// Test order and orderby.
$products = wc_get_products( array( 'return' => 'ids', 'order' => 'ASC', 'orderby' => 'ID', 'limit' => 2 ) );
$this->assertEquals( array( $product->get_id(), $product_2->get_id() ), $products );
// Test paginate.
$products = wc_get_products( array( 'paginate' => true ) );
$this->assertGreaterThan( 0, $products->total );
$this->assertGreaterThan( 0, $products->max_num_pages );
$this->assertNotEmpty( $products->products );
$variation->delete( true );
}
/**
* Tests wc_get_products() with dimension parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_dimensions() {
$product_1 = new WC_Product_Simple;
$product_1->set_width( '12.5' );
$product_1->set_height( '5' );
$product_1->set_weight( '11.4' );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_width( '10' );
$product_2->set_height( '5' );
$product_2->set_weight( '15' );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'width' => 12.5 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'height' => 5.0 ) );
sort( $products );
$this->assertEquals( array( $product_1->get_id(), $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'weight' => 15 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with price parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_price() {
$product_1 = new WC_Product_Simple;
$product_1->set_regular_price( '12.5' );
$product_1->set_price( '12.5' );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_regular_price( '14' );
$product_2->set_sale_price( '12.5' );
$product_2->set_price( '12.5' );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'regular_price' => 12.5 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'sale_price' => 12.5 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'price' => 12.5 ) );
sort( $products );
$this->assertEquals( array( $product_1->get_id(), $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with total_sales parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_total_sales() {
$product_1 = new WC_Product_Simple;
$product_1->set_total_sales( 4 );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_total_sales( 2 );
$product_2->save();
$product_3 = new WC_Product_Simple;
$product_3->save();
$products = wc_get_products( array( 'return' => 'ids', 'total_sales' => 4 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
}
/**
* Tests wc_get_products() with boolean parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_booleans() {
$product_1 = new WC_Product_Simple;
$product_1->set_virtual( true );
$product_1->set_downloadable( true );
$product_1->set_featured( true );
$product_1->set_sold_individually( true );
$product_1->set_backorders( 'no' );
$product_1->set_manage_stock( false );
$product_1->set_reviews_allowed( true );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_virtual( false );
$product_2->set_downloadable( false );
$product_2->set_featured( false );
$product_2->set_sold_individually( false );
$product_2->set_backorders( 'notify' );
$product_2->set_manage_stock( true );
$product_2->set_reviews_allowed( false );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'virtual' => true ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'virtual' => false ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'downloadable' => false ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'featured' => true ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'featured' => false ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'sold_individually' => true ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'backorders' => 'notify' ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'manage_stock' => true ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'reviews_allowed' => true ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'reviews_allowed' => false ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with visibility parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_visibility() {
$product_1 = new WC_Product_Simple;
$product_1->set_catalog_visibility( 'visible' );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_catalog_visibility( 'hidden' );
$product_2->save();
$product_3 = new WC_Product_Simple;
$product_3->set_catalog_visibility( 'search' );
$product_3->save();
$products = wc_get_products( array( 'return' => 'ids', 'visibility' => 'visible' ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'visibility' => 'hidden' ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'visibility' => 'search' ) );
sort( $products );
$this->assertEquals( array( $product_1->get_id(), $product_3->get_id() ), $products );
}
/**
* Tests wc_get_products() with stock parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_stock() {
$product_1 = new WC_Product_Simple;
$product_1->set_manage_stock( true );
$product_1->set_stock_status( 'instock' );
$product_1->set_stock_quantity( 5 );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_manage_stock( true );
$product_2->set_stock_status( 'outofstock' );
$product_2->set_stock_quantity( 0 );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'stock_quantity' => 5 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'stock_quantity' => 0 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'stock_status' => 'outofstock' ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with tax parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_tax() {
$product_1 = new WC_Product_Simple;
$product_1->set_tax_status( 'taxable' );
$product_1->set_tax_class( 'reduced-rate' );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_tax_status( 'none' );
$product_2->set_tax_class( 'standard' );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'tax_status' => 'taxable' ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'tax_status' => 'none' ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'tax_class' => 'reduced-rate' ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
}
/**
* Tests wc_get_products() with shipping parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_shipping_class() {
$shipping_class_1 = wp_insert_term( 'Bulky', 'product_shipping_class' );
$shipping_class_2 = wp_insert_term( 'Standard', 'product_shipping_class' );
$product_1 = new WC_Product_Simple;
$product_1->set_shipping_class_id( $shipping_class_1['term_id'] );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_shipping_class_id( $shipping_class_2['term_id'] );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'shipping_class' => 'bulky' ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'shipping_class' => 'standard' ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with download parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_download() {
$product_1 = new WC_Product_Simple;
$product_1->set_downloadable( true );
$product_1->set_download_limit( 5 );
$product_1->set_download_expiry( 90 );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_downloadable( true );
$product_2->set_download_limit( -1 );
$product_2->set_download_expiry( -1 );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'download_limit' => 5 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'download_limit' => -1 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'download_expiry' => 90 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'download_expiry' => -1 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Tests wc_get_products() with reviews parameters.
*
* @since 3.2.0
*/
public function test_wc_get_products_reviews() {
$product_1 = new WC_Product_Simple;
$product_1->set_average_rating( 5.0 );
$product_1->set_review_count( 5 );
$product_1->save();
$product_2 = new WC_Product_Simple;
$product_2->set_average_rating( 3.0 );
$product_2->set_review_count( 1 );
$product_2->save();
$products = wc_get_products( array( 'return' => 'ids', 'average_rating' => 5.0 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'average_rating' => 3.0 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'review_count' => 5 ) );
$this->assertEquals( array( $product_1->get_id() ), $products );
$products = wc_get_products( array( 'return' => 'ids', 'review_count' => 1 ) );
$this->assertEquals( array( $product_2->get_id() ), $products );
}
/**
* Test wc_get_product().
*

View File

@ -0,0 +1,149 @@
<?php
/**
* Test WC_Product_Query
* @package WooCommerce\Tests\Product
*/
class WC_Tests_WC_Product_Query extends WC_Unit_Test_Case {
/**
* Test basic methods on a new WC_Product_Query.
*
* @since 3.2
*/
public function test_product_query_new() {
$query = new WC_Product_Query();
$this->assertEquals( '', $query->get( 'weight' ) );
$types = $query->get( 'type' );
sort( $types );
$this->assertEquals( array( 'external', 'grouped', 'simple', 'variable' ), $types );
}
/**
* Test querying by product properties.
*
* @since 3.2
*/
public function test_product_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_Grouped();
$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 ) );
}
/**
* Test querying by product date properties for dates stored in metadata.
*
* @since 3.2
*/
public function test_product_query_meta_date_queries() {
$now = current_time( 'mysql', true );
$now_stamp = strtotime( $now );
$now_date = date( 'Y-m-d', $now_stamp );
$past_stamp = $now_stamp - DAY_IN_SECONDS;
$past = date( 'Y-m-d', $past_stamp );
$future_stamp = $now_stamp + DAY_IN_SECONDS;
$future = date( 'Y-m-d', $future_stamp );
$product = new WC_Product_Simple();
$product->set_date_on_sale_from( $now_stamp );
$product->save();
// Check WC_DateTime support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => $product->get_date_on_sale_from(),
) );
$products = $query->get_products();
$this->assertEquals( 1, count( $products ) );
// Check date support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => $now_date,
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $past );
$this->assertEquals( 0, count( $query->get_products() ) );
// Check timestamp support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => $product->get_date_on_sale_from()->getTimestamp(),
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $future_stamp );
$this->assertEquals( 0, count( $query->get_products() ) );
// Check comparison support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => '>' . $past,
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', '<' . $past );
$this->assertEquals( 0, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', '>=' . $now_date );
$this->assertEquals( 1, count( $query->get_products() ) );
// Check timestamp comparison support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => '<' . $future_stamp,
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', '<' . $past_stamp );
$this->assertEquals( 0, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', '>=' . $now_stamp );
// Check date range support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => $past . '...' . $future,
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $now_date . '...' . $future );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $future . '...' . $now_date );
$this->assertEquals( 0, count( $query->get_products() ) );
// Check timestamp range support.
$query = new WC_Product_Query( array(
'date_on_sale_from' => $past_stamp . '...' . $future_stamp,
) );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $now_stamp . '...' . $future_stamp );
$this->assertEquals( 1, count( $query->get_products() ) );
$query->set( 'date_on_sale_from', $future_stamp . '...' . $now_stamp );
$this->assertEquals( 0, count( $query->get_products() ) );
}
}