Add a filter to OrdersTableQuery to allow overriding of HPOS queries (#39945)
* Add a filter to OrdersTableQuery to allow overriding of HPOS queries * add changelog file * address review comments * move `woocommerce_hpos_pre_query` filter into `maybe_override_query` method * specify and ensure that the 3-tuple param / return value can also be null * document the $sql param * remove debugging echo * tweak wording Co-authored-by: Vedanshu Jain <vedanshu.jain.2012@gmail.com> * remove unused variable --------- Co-authored-by: Vedanshu Jain <vedanshu.jain.2012@gmail.com>
This commit is contained in:
parent
deee539245
commit
4a45c956ab
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add a filter to OrdersTableQuery to allow overriding of HPOS queries.
|
|
@ -199,7 +199,52 @@ class OrdersTableQuery {
|
|||
unset( $this->args['customer_note'], $this->args['name'] );
|
||||
|
||||
$this->build_query();
|
||||
$this->run_query();
|
||||
if ( ! $this->maybe_override_query() ) {
|
||||
$this->run_query();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets the `woocommerce_hpos_pre_query` filter override the query.
|
||||
*
|
||||
* @return boolean Whether the query was overridden or not.
|
||||
*/
|
||||
private function maybe_override_query(): bool {
|
||||
/**
|
||||
* Filters the orders array before the query takes place.
|
||||
*
|
||||
* Return a non-null value to bypass the HPOS default order queries.
|
||||
*
|
||||
* If the query includes limits via the `limit`, `page`, or `offset` arguments, we
|
||||
* encourage the `found_orders` and `max_num_pages` properties to also be set.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param array|null $order_data {
|
||||
* An array of order data.
|
||||
* @type int[] $orders Return an array of order IDs data to short-circuit the HPOS query,
|
||||
* or null to allow HPOS to run its normal query.
|
||||
* @type int $found_orders The number of orders found.
|
||||
* @type int $max_num_pages The number of pages.
|
||||
* }
|
||||
* @param OrdersTableQuery $query The OrdersTableQuery instance.
|
||||
* @param string $sql The OrdersTableQuery instance.
|
||||
*/
|
||||
list( $this->orders, $this->found_orders, $this->max_num_pages ) = apply_filters( 'woocommerce_hpos_pre_query', null, $this, $this->sql );
|
||||
// If the filter set the orders, make sure the others values are set as well and skip running the query.
|
||||
if ( is_array( $this->orders ) ) {
|
||||
if ( ! is_int( $this->found_orders ) || $this->found_orders < 1 ) {
|
||||
$this->found_orders = count( $this->orders );
|
||||
}
|
||||
if ( ! is_int( $this->max_num_pages ) || $this->max_num_pages < 1 ) {
|
||||
if ( ! $this->arg_isset( 'limit' ) || ! is_int( $this->args['limit'] ) || $this->args['limit'] < 1 ) {
|
||||
$this->args['limit'] = 10;
|
||||
}
|
||||
$this->max_num_pages = (int) ceil( $this->found_orders / $this->args['limit'] );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery;
|
||||
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
|
||||
use Automattic\WooCommerce\RestApi\UnitTests\HPOSToggleTrait;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
|
@ -235,4 +236,162 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
|
|||
remove_all_filters( 'woocommerce_orders_table_query_clauses' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox The pre-query escape hook allows replacing the order query. The callback does not return pagination information.
|
||||
*/
|
||||
public function test_pre_query_escape_hook_simple() {
|
||||
$order1 = new \WC_Order();
|
||||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$order2 = new \WC_Order();
|
||||
$order2->save();
|
||||
|
||||
$query = new OrdersTableQuery( array() );
|
||||
$this->assertCount( 2, $query->orders );
|
||||
$this->assertEquals( 2, $query->found_orders );
|
||||
$this->assertEquals( 0, $query->max_num_pages );
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$this->assertNull( $result );
|
||||
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
|
||||
$this->assertStringContainsString( 'SELECT ', $sql );
|
||||
|
||||
// Only return one of the orders to show that we are replacing the query result.
|
||||
// Do not return found_orders or max_num_pages to show we're setting defaults.
|
||||
$order_ids = array( $order1->get_id() );
|
||||
return array( $order_ids, null, null );
|
||||
};
|
||||
add_filter( 'woocommerce_hpos_pre_query', $callback, 10, 3 );
|
||||
|
||||
$query = new OrdersTableQuery( array() );
|
||||
$this->assertCount( 1, $query->orders );
|
||||
$this->assertEquals( 1, $query->found_orders );
|
||||
$this->assertEquals( 1, $query->max_num_pages );
|
||||
$this->assertEquals( $order1->get_id(), $query->orders[0] );
|
||||
|
||||
$orders = wc_get_orders( array() );
|
||||
$this->assertCount( 1, $orders );
|
||||
$this->assertEquals( $order1->get_id(), $orders[0]->get_id() );
|
||||
|
||||
remove_all_filters( 'woocommerce_hpos_pre_query' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox The pre-query escape hook allows replacing the order query. The callback returns pagination information.
|
||||
*/
|
||||
public function test_pre_query_escape_hook_with_pagination() {
|
||||
$order1 = new \WC_Order();
|
||||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$order2 = new \WC_Order();
|
||||
$order2->save();
|
||||
|
||||
$query = new OrdersTableQuery( array() );
|
||||
$this->assertCount( 2, $query->orders );
|
||||
$this->assertEquals( 2, $query->found_orders );
|
||||
$this->assertEquals( 0, $query->max_num_pages );
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
$this->assertNull( $result );
|
||||
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
|
||||
$this->assertStringContainsString( 'SELECT ', $sql );
|
||||
|
||||
// Only return one of the orders to show that we are replacing the query result.
|
||||
$order_ids = array( $order1->get_id() );
|
||||
// These are made up to show that we are actually replacing the values.
|
||||
$found_orders = 17;
|
||||
$max_num_pages = 23;
|
||||
return array( $order_ids, $found_orders, $max_num_pages );
|
||||
};
|
||||
add_filter( 'woocommerce_hpos_pre_query', $callback, 10, 3 );
|
||||
|
||||
$query = new OrdersTableQuery( array() );
|
||||
$this->assertCount( 1, $query->orders );
|
||||
$this->assertEquals( 17, $query->found_orders );
|
||||
$this->assertEquals( 23, $query->max_num_pages );
|
||||
$this->assertEquals( $order1->get_id(), $query->orders[0] );
|
||||
|
||||
$orders = wc_get_orders( array() );
|
||||
$this->assertCount( 1, $orders );
|
||||
$this->assertEquals( $order1->get_id(), $orders[0]->get_id() );
|
||||
|
||||
remove_all_filters( 'woocommerce_hpos_pre_query' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox The pre-query escape hook uses the limit arg if it is set.
|
||||
*/
|
||||
public function test_pre_query_escape_hook_pass_limit() {
|
||||
$order1 = new \WC_Order();
|
||||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
// Do not return found_orders or max_num_pages so as to provoke a warning.
|
||||
$order_ids = array( $order1->get_id() );
|
||||
return array( $order_ids, 10, null );
|
||||
};
|
||||
add_filter( 'woocommerce_hpos_pre_query', $callback, 10, 3 );
|
||||
|
||||
$query = new OrdersTableQuery(
|
||||
array(
|
||||
'limit' => 5,
|
||||
)
|
||||
);
|
||||
$this->assertCount( 1, $query->orders );
|
||||
$this->assertEquals( 10, $query->found_orders );
|
||||
$this->assertEquals( 2, $query->max_num_pages );
|
||||
|
||||
remove_all_filters( 'woocommerce_hpos_pre_query' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox A regular query will still work even if the pre-query escape hook returns null for the whole 3-tuple.
|
||||
*/
|
||||
public function test_pre_query_escape_hook_return_null() {
|
||||
$order1 = new \WC_Order();
|
||||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
// Just return null.
|
||||
return null;
|
||||
};
|
||||
add_filter( 'woocommerce_hpos_pre_query', $callback, 10, 3 );
|
||||
|
||||
$query = new OrdersTableQuery();
|
||||
$this->assertCount( 1, $query->orders );
|
||||
$this->assertEquals( 1, $query->found_orders );
|
||||
$this->assertEquals( null, $query->max_num_pages );
|
||||
|
||||
remove_all_filters( 'woocommerce_hpos_pre_query' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox A regular query with a limit will still work even if the pre-query escape hook returns null for the whole 3-tuple.
|
||||
*/
|
||||
public function test_pre_query_escape_hook_return_null_limit() {
|
||||
$order1 = new \WC_Order();
|
||||
$order1->set_date_created( time() - HOUR_IN_SECONDS );
|
||||
$order1->save();
|
||||
|
||||
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
|
||||
// Just return null.
|
||||
return null;
|
||||
};
|
||||
add_filter( 'woocommerce_hpos_pre_query', $callback, 10, 3 );
|
||||
|
||||
$query = new OrdersTableQuery(
|
||||
array(
|
||||
'limit' => 5,
|
||||
)
|
||||
);
|
||||
$this->assertCount( 1, $query->orders );
|
||||
$this->assertEquals( 1, $query->found_orders );
|
||||
$this->assertEquals( 1, $query->max_num_pages );
|
||||
|
||||
remove_all_filters( 'woocommerce_hpos_pre_query' );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue