Merge pull request #27625 from woocommerce/revert-improved-filtering-for-variations

Revert improved filtering for variations
This commit is contained in:
jonathansadowski 2020-09-10 12:42:15 -05:00 committed by GitHub
commit 2bcdbacc98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 32 additions and 584 deletions

View File

@ -593,94 +593,6 @@ class WC_Product_Variable extends WC_Product {
return true;
}
/**
* Returns whether or not the product is visible in the catalog (doesn't trigger filters).
*
* @return bool
*/
protected function is_visible_core() {
if ( ! $this->parent_is_visible_core() ) {
return false;
}
$query_filters = $this->get_layered_nav_chosen_attributes();
if ( empty( $query_filters ) ) {
return true;
}
/**
* If there are attribute filters in the request, a variable product will be visible
* if at least one of the available variations matches the filters.
*/
$attributes_with_terms = array();
array_walk(
$query_filters,
function( $value, $key ) use ( &$attributes_with_terms ) {
$attributes_with_terms[ $key ] = $value['terms'];
}
);
$variations = $this->get_available_variations( 'objects' );
foreach ( $variations as $variation ) {
if ( $this->variation_matches_filters( $variation, $attributes_with_terms ) ) {
return true;
}
}
return false;
}
/**
* Checks if a given variation matches the active attribute filters.
*
* @param WC_Product_Variation $variation The variation to check.
* @param array $query_filters The active filters as an array of attribute_name => [term1, term2...].
*
* @return bool True if the variation matches the active attribute filters.
*/
private function variation_matches_filters( WC_Product_Variation $variation, array $query_filters ) {
// Get the variation attributes as an array of attribute_name => attribute_value.
// The array_filter will filter out attributes having a value of '', these correspond
// to "Any..." variations that don't participate in filtering.
$variation_attributes = array_filter( $variation->get_variation_attributes( false ) );
$variation_attribute_names_in_filters = array_intersect( array_keys( $query_filters ), array_keys( $variation_attributes ) );
if ( empty( $variation_attribute_names_in_filters ) ) {
// The variation doesn't have any attribute that participates in filtering so we consider it a match.
return true;
}
foreach ( $variation_attribute_names_in_filters as $attribute_name ) {
if ( ! in_array( $variation_attributes[ $attribute_name ], $query_filters[ $attribute_name ], true ) ) {
// Multiple filters interact with AND logic, so as soon as one of them
// doesn't match then the variation doesn't match.
return false;
}
}
return true;
}
/**
* What does is_visible_core in the parent class say?
* This method exists to ease unit testing.
*
* @return bool
*/
protected function parent_is_visible_core() {
return parent::is_visible_core();
}
/**
* Get an array of attributes and terms selected with the layered nav widget.
* This method exists to ease unit testing.
*
* @return array
*/
protected function get_layered_nav_chosen_attributes() {
return WC()->query::get_layered_nav_chosen_attributes();
}
/*
|--------------------------------------------------------------------------

View File

@ -362,23 +362,10 @@ class WC_Query {
if ( 'product_query' !== $query->get( 'wc_query' ) ) {
return $posts;
}
$this->adjust_total_pages();
$this->remove_product_query_filters( $posts );
return $posts;
}
/**
* The 'adjust_posts_count' method that handles the 'found_posts' filter indirectly initializes
* the loop properties with a call to 'wc_setup_loop'. This includes setting 'total_pages' to
* '$GLOBALS['wp_query']->max_num_pages', which at that point has a value of zero.
* Thus we need to set the real value from the 'the_posts' filter, where $GLOBALS['wp_query']->max_num_pages'
* will aready have been initialized.
*/
private function adjust_total_pages() {
if ( 0 === wc_get_loop_prop( 'total_pages' ) ) {
wc_set_loop_prop( 'total_pages', $GLOBALS['wp_query']->max_num_pages );
}
}
/**
* Pre_get_posts above may adjust the main query to add WooCommerce logic. When this query is done, we need to ensure
@ -396,12 +383,9 @@ class WC_Query {
}
/**
* When we are listing products and the request is filtering by attributes via layered nav plugin
* we need to adjust the total posts count to account for variable products having stock
* in some variations but not in others.
* We do that by just checking if each product is visible.
*
* We also cache the post visibility so that it isn't checked again when displaying the posts list.
* This function used to be hooked to found_posts and adjust the posts count when the filtering by attribute
* widget was used and variable products were present. Now it isn't hooked anymore and does nothing but return
* the input unchanged, since the pull request in which it was introduced has been reverted.
*
* @since 4.4.0
* @param int $count Original posts count, as supplied by the found_posts filter.
@ -410,35 +394,6 @@ class WC_Query {
* @return int Adjusted posts count.
*/
public function adjust_posts_count( $count, $query ) {
if ( ! $query->get( 'wc_query' ) ) {
return $count;
}
$posts = $this->get_current_posts();
if ( is_null( $posts ) ) {
return $count;
}
foreach ( $posts as $post ) {
if ( is_object( $post ) && 'product' !== $post->post_type ) {
continue;
}
$product_id = is_object( $post ) ? $post->ID : $post;
$product = wc_get_product( $product_id );
if ( ! is_object( $product ) ) {
continue;
}
if ( $product->is_visible() ) {
wc_set_loop_product_visibility( $product_id, true );
} else {
wc_set_loop_product_visibility( $product_id, false );
$count--;
}
}
wc_set_loop_prop( 'total', $count );
return $count;
}
@ -514,7 +469,7 @@ class WC_Query {
// Additonal hooks to change WP Query.
add_filter( 'posts_clauses', array( $this, 'price_filter_post_clauses' ), 10, 2 );
add_filter( 'the_posts', array( $this, 'handle_get_posts' ), 10, 2 );
add_filter( 'found_posts', array( $this, 'adjust_posts_count' ), 10, 2 );
do_action( 'woocommerce_product_query', $q, $this );
}

View File

@ -344,93 +344,50 @@ class WC_Widget_Layered_Nav extends WC_Widget {
protected function get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type ) {
global $wpdb;
$main_tax_query = $this->get_main_tax_query();
$meta_query = $this->get_main_meta_query();
$tax_query = $this->get_main_tax_query();
$meta_query = $this->get_main_meta_query();
$non_variable_tax_query_sql = array( 'where' => '' );
$is_and_query = 'and' === $query_type;
foreach ( $main_tax_query as $key => $query ) {
if ( is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
if ( $is_and_query ) {
$non_variable_tax_query_sql = $this->convert_tax_query_to_sql( array( $query ) );
if ( 'or' === $query_type ) {
foreach ( $tax_query as $key => $query ) {
if ( is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
unset( $tax_query[ $key ] );
}
unset( $main_tax_query[ $key ] );
}
}
$exclude_variable_products_tax_query_sql = $this->get_extra_tax_query_sql( 'product_type', array( 'variable' ), 'NOT IN' );
$meta_query = new WP_Meta_Query( $meta_query );
$tax_query = new WP_Tax_Query( $tax_query );
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
$term_ids_sql = '(' . implode( ',', array_map( 'absint', $term_ids ) ) . ')';
$meta_query_sql = ( new WP_Meta_Query( $meta_query ) )->get_sql( 'post', $wpdb->posts, 'ID' );
$main_tax_query_sql = $this->convert_tax_query_to_sql( $main_tax_query );
$term_ids_sql = '(' . implode( ',', array_map( 'absint', $term_ids ) ) . ')';
// Generate the first part of the query.
// This one will return non-variable products and variable products with concrete values for the attributes.
// Generate query.
$query = array();
$query['select'] = "SELECT IF({$wpdb->posts}.post_type='product_variation', {$wpdb->posts}.post_parent, {$wpdb->posts}.ID) AS product_id, terms.term_id AS term_count_id";
$query['select'] = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) AS term_count, terms.term_id AS term_count_id";
$query['from'] = "FROM {$wpdb->posts}";
$query['join'] = "
INNER JOIN {$wpdb->term_relationships} AS tr ON {$wpdb->posts}.ID = tr.object_id
INNER JOIN {$wpdb->term_relationships} AS term_relationships ON {$wpdb->posts}.ID = term_relationships.object_id
INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy USING( term_taxonomy_id )
INNER JOIN {$wpdb->terms} AS terms USING( term_id )
{$main_tax_query_sql['join']} {$meta_query_sql['join']}"; // Not an omission, really no more JOINs required.
$variable_where_part = "
OR ({$wpdb->posts}.post_type = 'product_variation'
AND NOT EXISTS (
SELECT ID FROM {$wpdb->posts} AS parent
WHERE parent.ID = {$wpdb->posts}.post_parent AND parent.post_status NOT IN ('publish')
))
";
$search_sql = '';
$search = $this->get_main_search_query_sql();
if ( $search ) {
$search_sql = ' AND ' . $search;
}
" . $tax_query_sql['join'] . $meta_query_sql['join'];
$query['where'] = "
WHERE
{$wpdb->posts}.post_status = 'publish'
{$main_tax_query_sql['where']} {$meta_query_sql['where']}
AND (
(
{$wpdb->posts}.post_type = 'product'
{$exclude_variable_products_tax_query_sql['where']}
{$non_variable_tax_query_sql['where']}
)
{$variable_where_part}
)
AND terms.term_id IN {$term_ids_sql}
{$search_sql}";
WHERE {$wpdb->posts}.post_type IN ( 'product' )
AND {$wpdb->posts}.post_status = 'publish'
{$tax_query_sql['where']} {$meta_query_sql['where']}
AND terms.term_id IN $term_ids_sql";
$search = $this->get_main_search_query_sql();
if ( $search ) {
$query['where'] .= ' AND ' . $search;
}
$query = apply_filters( 'woocommerce_get_filtered_term_product_counts_query', $query );
$main_query_sql = implode( ' ', $query );
$query['group_by'] = 'GROUP BY terms.term_id';
$query = apply_filters( 'woocommerce_get_filtered_term_product_counts_query', $query );
$query_sql = implode( ' ', $query );
// Generate the second part of the query.
// This one will return products having "Any..." as the value of the attribute.
$query_sql_for_attributes_with_any_value = "
SELECT {$wpdb->posts}.ID AS product_id, {$wpdb->term_relationships}.term_taxonomy_id as term_count_id FROM {$wpdb->posts}
JOIN {$wpdb->posts} variations ON variations.post_parent = {$wpdb->posts}.ID
LEFT JOIN {$wpdb->postmeta} ON variations.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = 'attribute_$taxonomy'
JOIN {$wpdb->term_relationships} ON {$wpdb->term_relationships}.object_id = {$wpdb->posts}.ID
WHERE ( {$wpdb->postmeta}.meta_key IS NULL OR {$wpdb->postmeta}.meta_value = '')
AND {$wpdb->posts}.post_type = 'product'
AND {$wpdb->posts}.post_status = 'publish'
AND variations.post_status = 'publish'
AND variations.post_type = 'product_variation'
AND {$wpdb->term_relationships}.term_taxonomy_id in $term_ids_sql
{$main_tax_query_sql['where']}";
// We have two queries - let's see if cached results of this query already exist.
$query_hash = md5( $main_query_sql . $query_sql_for_attributes_with_any_value );
// We have a query - let's see if cached results of this query already exist.
$query_hash = md5( $query_sql );
// Maybe store a transient of the count values.
$cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true );
@ -441,7 +398,9 @@ class WC_Widget_Layered_Nav extends WC_Widget {
}
if ( ! isset( $cached_counts[ $query_hash ] ) ) {
$counts = $this->get_term_product_counts_from_queries( $main_query_sql, $query_sql_for_attributes_with_any_value );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $query_sql, ARRAY_A );
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
$cached_counts[ $query_hash ] = $counts;
if ( true === $cache ) {
set_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ), $cached_counts, DAY_IN_SECONDS );
@ -451,30 +410,6 @@ class WC_Widget_Layered_Nav extends WC_Widget {
return array_map( 'absint', (array) $cached_counts[ $query_hash ] );
}
/**
* Get the count of terms for products, using a set of SQL queries that are return pairs of product id - term id.
*
* @param string $main_query_sql The SQL query to use in order to count products with concrete values for attributes, must return a "product_id" column and a "terms_count_id" column.
* @param string $query_sql_for_attributes_with_any_value The SQL query to use in order to count products with "Any" values for attributes, must return a "product_id" column and a "terms_count_id" column.
*
* @return array An array where the keys are term ids, and the values are term counts.
*/
private function get_term_product_counts_from_queries( $main_query_sql, $query_sql_for_attributes_with_any_value ) {
global $wpdb;
$total_counts = null;
$query = "
SELECT COUNT(DISTINCT(product_id)) AS term_count, term_count_id FROM (
{$main_query_sql}
UNION ALL
{$query_sql_for_attributes_with_any_value}
) AS x GROUP BY term_count_id";
$results = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
}
/**
* Wrapper for WC_Query::get_main_tax_query() to ease unit testing.
*
@ -505,45 +440,6 @@ class WC_Widget_Layered_Nav extends WC_Widget {
return WC_Query::get_main_meta_query();
}
/**
* Get a tax query SQL for a given set of taxonomy, terms and operator.
* Uses an intermediate WP_Tax_Query object.
*
* @since 4.4.0
* @param string $taxonomy Taxonomy name.
* @param array $terms Terms to include in the query.
* @param string $operator Query operator, as supported by WP_Tax_Query; e.g. "NOT IN".
*
* @return array
*/
private function get_extra_tax_query_sql( $taxonomy, $terms, $operator ) {
$query = array(
array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $terms,
'operator' => $operator,
'include_children' => false,
),
);
return $this->convert_tax_query_to_sql( $query );
}
/**
* Convert a tax query array to SQL using an intermediate WP_Tax_Query object.
*
* @since 4.4.0
* @param array $query Query array in the same format accepted by WP_Tax_Query constructor.
*
* @return array Query SQL as returned by WP_Tax_Query->get_sql.
*/
private function convert_tax_query_to_sql( $query ) {
global $wpdb;
return ( new WP_Tax_Query( $query ) )->get_sql( $wpdb->posts, 'ID' );
}
/**
* Show list based layered nav.
*

View File

@ -20,7 +20,7 @@ defined( 'ABSPATH' ) || exit;
global $product;
// Ensure visibility.
if ( empty( $product ) || false === wc_get_loop_product_visibility( $product->get_id() ) || ! $product->is_visible() ) {
if ( empty( $product ) || ! $product->is_visible() ) {
return;
}
?>

View File

@ -176,199 +176,4 @@ class WC_Tests_Product_Variable extends WC_Unit_Test_Case {
$this->assertEquals( $expected_stock_status, $product->get_stock_status() );
}
/**
* Setup for a test for is_visible.
*
* @param array $filtering_attributes Simulated filtering attributes as an array of attribute_name => [term1, term2...].
* @param bool $hide_out_of_stock_products Should the woocommerce_hide_out_of_stock_items option be set?.
* @param bool $is_visible_from_parent Return value of is_visible from base class.
*
* @return WC_Product_Variable A properly configured instance of WC_Product_Variable to test.
*/
private function prepare_visibility_test( $filtering_attributes, $hide_out_of_stock_products = true, $is_visible_from_parent = true ) {
foreach ( $filtering_attributes as $attribute_name => $terms ) {
$filtering_attributes[ $attribute_name ]['query_type'] = 'ANY_QUERY_TYPE';
$filtering_attributes[ $attribute_name ]['terms'] = $terms;
}
update_option( 'woocommerce_hide_out_of_stock_items', $hide_out_of_stock_products ? 'yes' : 'no' );
$sut = $this
->getMockBuilder( WC_Product_Variable::class )
->setMethods( array( 'parent_is_visible_core', 'get_layered_nav_chosen_attributes' ) )
->getMock();
$sut = WC_Helper_Product::create_variation_product( $sut, true );
$sut->save();
$sut->method( 'parent_is_visible_core' )->willReturn( $is_visible_from_parent );
$sut->method( 'get_layered_nav_chosen_attributes' )->willReturn( $filtering_attributes );
return $sut;
}
/**
* Configure the stock status for the attribute-based variations of a product.
*
* @param WC_Product_Variable $product Product with the variations to configure.
* @param array $attributes An array of attribute_name => [attribute_values], only the matching variations will have stock.
*/
private function set_variations_with_stock( $product, $attributes ) {
$variation_ids = $product->get_children();
foreach ( $variation_ids as $id ) {
$variation = wc_get_product( $id );
$attribute_matches = true;
foreach ( $attributes as $name => $values ) {
if ( ! in_array( $variation->get_attribute( $name ), $values, true ) ) {
$attribute_matches = false;
}
}
$variation->set_stock_status( $attribute_matches ? 'instock' : 'outofstock' );
$variation->save();
}
}
/**
* @testdox The product should be invisible when the parent 'is_visible' method returns false.
*/
public function test_is_invisible_when_parent_is_visible_returns_false() {
$sut = $this->prepare_visibility_test( array(), '', false, false );
$this->assertFalse( $sut->is_visible() );
}
/**
* @testdox The product should be visible when no nav filtering is supplied if at least one variation has stock.
*
* Note that if no variations have stock the base is_visible will already return false.
*/
public function test_is_visible_when_no_filtering_supplied_and_at_least_one_variation_has_stock() {
$sut = $this->prepare_visibility_test( array(), '' );
$this->set_variations_with_stock( $sut, array( 'pa_size' => array( 'small' ) ) );
$this->assertTrue( $sut->is_visible() );
}
/**
* @testdox Test product visibility when the variation requested in nav filtering has no stock, result depends on woocommerce_hide_out_of_stock_items option.
*
* @param bool $hide_out_of_stock Value for woocommerce_hide_out_of_stock_items.
* @param bool $expected_visibility Expected value of is_visible for the tested product.
*
* @testWith [true, false]
* [false, true]
*/
public function test_visibility_when_supplied_filter_has_no_stock( $hide_out_of_stock, $expected_visibility ) {
$sut = $this->prepare_visibility_test( array( 'pa_size' => array( 'large' ) ), $hide_out_of_stock );
$this->set_variations_with_stock( $sut, array( 'pa_size' => array( 'small' ) ) );
$this->assertEquals( $expected_visibility, $sut->is_visible() );
}
/**
* @testdox Product should always be visible when only one of the variations requested in nav filtering has stock.
*
* @param bool $hide_out_of_stock Value for woocommerce_hide_out_of_stock_items.
*
* @testWith [true]
* [false]
*/
public function test_visibility_when_multiple_filter_values_supplied_and_only_one_has_stock( $hide_out_of_stock ) {
$sut = $this->prepare_visibility_test( array( 'pa_size' => array( 'small', 'large' ) ), $hide_out_of_stock );
$this->set_variations_with_stock( $sut, array( 'pa_size' => array( 'small' ) ) );
$this->assertTrue( $sut->is_visible() );
}
/**
* @testdox Product should be visible when all of the variations requested in nav filtering have stock.
*
* @param bool $hide_out_of_stock Value for woocommerce_hide_out_of_stock_items.
*
* @testWith [true]
* [false]
*/
public function test_visibility_when_multiple_filter_values_supplied_and_all_of_them_have_stock( $hide_out_of_stock ) {
$sut = $this->prepare_visibility_test( array( 'pa_size' => array( 'small', 'large' ) ), $hide_out_of_stock );
$this->set_variations_with_stock( $sut, array( 'pa_size' => array( 'small', 'large' ) ) );
$this->assertTrue( $sut->is_visible() );
}
/**
* @testdox Product should be visible when multiple filters are present, and there's a variation matching all of them.
*/
public function test_visibility_when_multiple_filters_are_used_and_all_of_them_match() {
$sut = $this->prepare_visibility_test(
array(
'pa_size' => array( 'huge' ),
'pa_colour' => array( 'blue' ),
),
true
);
$this->set_variations_with_stock(
$sut,
array(
'pa_size' => array( 'huge' ),
'pa_colour' => array( 'blue' ),
'pa_number' => array( '2' ),
)
);
$this->assertTrue( $sut->is_visible() );
}
/**
* @testdox Product should not be visible when multiple filters are present, and there are no variations matching all of them.
*/
public function test_visibility_when_multiple_filters_are_used_and_one_of_them_does_not_match() {
$sut = $this->prepare_visibility_test(
array(
'pa_size' => array( 'small', 'huge' ),
'pa_colour' => array( 'red' ),
),
true
);
$this->set_variations_with_stock(
$sut,
array(
'pa_size' => array( 'huge' ),
'pa_colour' => array( 'blue' ),
'pa_number' => array( '2' ),
)
);
$this->assertFalse( $sut->is_visible() );
}
/**
* @testdox Attributes having "Any..." as value should not count when searching for matching attributes.
*/
public function test_visibility_when_multiple_filters_are_used_and_an_attribute_has_any_value() {
$sut = $this->prepare_visibility_test(
array(
'pa_size' => array( 'huge' ),
'pa_number' => array( '34' ),
),
true
);
$this->set_variations_with_stock(
$sut,
array(
'pa_size' => array( 'huge' ),
'pa_colour' => array( 'blue' ),
'pa_number' => array( '' ),
)
);
$this->assertTrue( $sut->is_visible() );
}
}

View File

@ -437,124 +437,4 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case {
WC()->query->remove_ordering_args();
}
/**
* Setup for a test for adjust_posts.
*
* @param bool $with_nav_filtering_data Should WC_Query::get_layered_nav_chosen_attributes return filtering data?.
* @param bool $use_objects If true, get_current_posts will return objects with an ID property; if false, it will returns the ids.
* @param string $post_type The value of the 'post_type' property for the objects generated when $use_objects is true.
*
* @return array An array where the first element is the instance of WC_Query, and the second is an array of sample products created.
*/
private function setup_adjust_posts_test( $with_nav_filtering_data, $use_objects, $post_type = 'product' ) {
update_option( 'woocommerce_hide_out_of_stock_items', 'yes' );
if ( $with_nav_filtering_data ) {
$nav_filtering_data = array( 'pa_something' => array( 'terms' => array( 'foo', 'bar' ) ) );
} else {
$nav_filtering_data = array();
}
$products = array();
$posts = array();
for ( $i = 0; $i < 5; $i++ ) {
$product = WC_Helper_Product::create_simple_product();
array_push( $products, $product );
$post = $use_objects ? (object) array(
'ID' => $product->get_id(),
'post_type' => $post_type,
) : $product->get_id();
array_push( $posts, $post );
}
$products[0]->set_stock_status( 'outofstock' );
$sut = $this
->getMockBuilder( WC_Query::class )
->setMethods( array( 'get_current_posts', 'get_layered_nav_chosen_attributes_inst' ) )
->getMock();
$sut->method( 'get_current_posts' )->willReturn( $posts );
$sut->method( 'get_layered_nav_chosen_attributes_inst' )->willReturn( $nav_filtering_data );
return array( $sut, $products );
}
/**
* @param bool $with_nav_filtering_data Should WC_Query::get_layered_nav_chosen_attributes return filtering data?.
* @param bool $use_objects If true, get_current_posts will return objects with an ID property; if false, it will returns the ids.
*
* @testdox adjust_posts should return the number of visible products and create product visibility loop variables
* @testWith [true, true]
* [false, false]
* [true, false]
* [false, true]
*/
public function test_adjust_posts_count_with_nav_filtering_attributes( $with_nav_filtering_data, $use_objects ) {
global $wp_query;
list($sut, $products) = $this->setup_adjust_posts_test( $with_nav_filtering_data, $use_objects );
$products[0]->set_stock_status( 'outofstock' );
$products[0]->save();
$products[1]->set_stock_status( 'outofstock' );
$products[1]->save();
$wp_query->set( 'wc_query', 'product_query' );
$this->assertEquals( 32, $sut->adjust_posts_count( 34, $wp_query ) );
$this->assertEquals( 32, wc_get_loop_prop( 'total' ) );
$this->assertEquals( false, wc_get_loop_product_visibility( $products[0]->get_id() ) );
$this->assertEquals( false, wc_get_loop_product_visibility( $products[1]->get_id() ) );
foreach ( array_slice( $products, 2 ) as $product ) {
$this->assertEquals( true, wc_get_loop_product_visibility( $product->get_id() ) );
}
}
/**
* @testdox adjust_posts should return the input unmodified if get_current_posts returns null.
*/
public function test_adjust_posts_count_when_there_are_no_posts() {
global $wp_query;
$sut = $this
->getMockBuilder( WC_Query::class )
->setMethods( array( 'get_current_posts', 'get_layered_nav_chosen_attributes_inst' ) )
->getMock();
$sut->method( 'get_current_posts' )->willReturn( null );
$wp_query->set( 'wc_query', 'product_query' );
$this->assertEquals( 34, $sut->adjust_posts_count( 34, $wp_query ) );
}
/**
* @testdox adjust_posts should return the input unmodified if the posts do not represent products.
*/
public function test_adjust_posts_count_when_the_posts_are_not_products() {
global $wp_query;
list( $sut, $products ) = $this->setup_adjust_posts_test( true, true, 'page' );
$products[0]->set_stock_status( 'outofstock' );
$products[0]->save();
$wp_query->set( 'wc_query', 'product_query' );
$this->assertEquals( 34, $sut->adjust_posts_count( 34, $wp_query ) );
}
/**
* @testdox adjust_posts should return the input unmodified if not in the main product query.
*/
public function test_adjust_posts_count_when_not_in_the_main_product_query() {
global $wp_query;
list( $sut, $products ) = $this->setup_adjust_posts_test( true, true );
$products[0]->set_stock_status( 'outofstock' );
$products[0]->save();
$wp_query->set( 'wc_query', null );
$this->assertEquals( 34, $sut->adjust_posts_count( 34, $wp_query ) );
}
}