diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index 7886a3a9b11..ba787df3b99 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -44,7 +44,7 @@ class WC_Query { add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 ); add_action( 'parse_request', array( $this, 'parse_request' ), 0 ); add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); - add_filter( 'the_posts', array( $this, 'remove_product_query_filters' ) ); + add_filter( 'the_posts', array( $this, 'handle_get_posts' ) ); add_filter( 'found_posts', array( $this, 'adjust_posts_count' ), 10, 2 ); add_filter( 'get_pagenum_link', array( $this, 'remove_add_to_cart_pagination' ), 10, 1 ); } @@ -352,6 +352,32 @@ class WC_Query { $this->product_query( $q ); } + /** + * Handler for the 'the_posts' WP filter. + * + * @param array $posts Posts from WP Query. + * + * @return array + */ + public function handle_get_posts( $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 * all custom filters are removed. @@ -376,13 +402,12 @@ class WC_Query { * We also cache the post visibility so that it isn't checked again when displaying the posts list. * * @since 4.4.0 - * @param int $count Original posts count, as supplied by the found_posts filter. + * @param int $count Original posts count, as supplied by the found_posts filter. * @param WP_Query $query The current WP_Query object. * * @return int Adjusted posts count. */ public function adjust_posts_count( $count, $query ) { - if ( ! $query->get( 'wc_query' ) ) { return $count; } diff --git a/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php b/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php index bfe6025f23d..b7202b2115e 100644 --- a/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php +++ b/tests/legacy/unit-tests/util/class-wc-tests-wc-query.php @@ -492,6 +492,8 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case { * [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' ); @@ -499,7 +501,8 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case { $products[1]->set_stock_status( 'outofstock' ); $products[1]->save(); - $this->assertEquals( 32, $sut->adjust_posts_count( 34 ) ); + $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() ) ); @@ -512,6 +515,8 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case { * @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' ) ) @@ -519,18 +524,37 @@ class WC_Tests_WC_Query extends WC_Unit_Test_Case { $sut->method( 'get_current_posts' )->willReturn( null ); - $this->assertEquals( 34, $sut->adjust_posts_count( 34 ) ); + $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(); - $this->assertEquals( 34, $sut->adjust_posts_count( 34 ) ); + $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 ) ); } }