[COT] `query()`: add support for the `customer` query var (#34059)

* Make sure orders are saved with a default status

* Add support for ‘customer’ query var in COT

* Make PHPCS happy in COT datastore tests

* Add tests for ‘customer’ query var

* Add changelog
This commit is contained in:
Jorge A. Torres 2022-07-26 07:24:12 -03:00 committed by GitHub
parent 844faf2fa4
commit c97e1067c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 25 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: enhancement
Add support for the 'customer' query var to the COT datastore.

View File

@ -1031,7 +1031,7 @@ LEFT JOIN {$operational_data_clauses['join']}
// 'status' is a little special (for backwards compat.).
if ( 'status' === $column ) {
$changes['status'] = 'wc-' . str_replace( 'wc-', '', $changes['status'] );
$changes['status'] = 'wc-' . str_replace( 'wc-', '', $changes['status'] ? $changes['status'] : 'pending' );
}
$row[ $column ] = $this->database_util->format_object_value_for_db( $changes[ $details['name'] ], $details['type'] );

View File

@ -139,7 +139,7 @@ class OrdersTableQuery {
$this->args = $args;
// TODO: args to be implemented.
unset( $this->args['type'], $this->args['customer'], $this->args['customer_note'], $this->args['name'] );
unset( $this->args['type'], $this->args['customer_note'], $this->args['name'] );
$this->build_query();
$this->run_query();
@ -457,6 +457,49 @@ class OrdersTableQuery {
if ( $this->arg_isset( 'exclude' ) ) {
$this->where[] = $this->where( $this->tables['orders'], 'id', '!=', $this->args['exclude'], 'int' );
}
// 'customer' is a very special field.
if ( $this->arg_isset( 'customer' ) ) {
$customer_query = $this->generate_customer_query( $this->args['customer'] );
if ( $customer_query ) {
$this->where[] = $customer_query;
}
}
}
/**
* Generate SQL conditions for the 'customer' query.
*
* @param array $values List of customer ids or emails.
* @param string $relation 'OR' or 'AND' relation used to build the customer query.
* @return string SQL to be used in a WHERE clause.
*/
private function generate_customer_query( $values, string $relation = 'OR' ): string {
$values = is_array( $values ) ? $values : array( $values );
$ids = array();
$emails = array();
foreach ( $values as $value ) {
if ( is_array( $value ) ) {
$sql = $this->generate_customer_query( $value, 'AND' );
$pieces[] = $sql ? '(' . $sql . ')' : '';
} elseif ( is_numeric( $value ) ) {
$ids[] = absint( $value );
} elseif ( is_string( $value ) && is_email( $value ) ) {
$emails[] = sanitize_email( $value );
}
}
if ( $ids ) {
$pieces[] = $this->where( $this->tables['orders'], 'customer_id', '=', $ids, 'int' );
}
if ( $emails ) {
$pieces[] = $this->where( $this->tables['orders'], 'billing_email', '=', $emails, 'string' );
}
return $pieces ? implode( " $relation ", $pieces ) : '';
}
/**

View File

@ -251,12 +251,22 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
global $wpdb;
// Sync enabled implies a full post should be created.
add_filter( 'pre_option_' . DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION, function() { return 'yes'; } );
add_filter(
'pre_option_' . DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION,
function() {
return 'yes';
}
);
$order = $this->create_complex_cot_order();
$this->assertEquals( 1, (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE ID = %d AND post_type = %s", $order->get_id(), 'shop_order' ) ) );
// Sync disabled implies a placeholder post should be created.
add_filter( 'pre_option_' . DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION, function() { return 'no'; } );
add_filter(
'pre_option_' . DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION,
function() {
return 'no';
}
);
$order = $this->create_complex_cot_order();
$this->assertEquals( 1, (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE ID = %d AND post_type = %s", $order->get_id(), DataSynchronizer::PLACEHOLDER_ORDER_POST_TYPE ) ) );
}
@ -275,7 +285,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$order->delete();
$orders_table = $this->sut::get_orders_table_name();
$this->assertEquals( 'trash', $wpdb->get_var( $wpdb->prepare( "SELECT status FROM {$orders_table} WHERE id = %d", $order_id ) ) );
$this->assertEquals( 'trash', $wpdb->get_var( $wpdb->prepare( "SELECT status FROM {$orders_table} WHERE id = %d", $order_id ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// Make sure order data persists in the database.
$this->assertNotEmpty( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $order_id ) ) );
@ -285,7 +295,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
continue;
}
$this->assertNotEmpty( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE order_id = %d", $order_id ) ) );
$this->assertNotEmpty( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE order_id = %d", $order_id ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
}
@ -307,7 +317,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
foreach ( $this->sut->get_all_table_names() as $table ) {
$field_name = ( $table === $this->sut::get_orders_table_name() ) ? 'id' : 'order_id';
$this->assertEmpty( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE {$field_name} = %d", $order_id ) ) );
$this->assertEmpty( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE {$field_name} = %d", $order_id ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
}
@ -349,32 +359,32 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
// Get orders with a specific property.
$query_vars['prices_include_tax'] = 'no';
$query = new OrdersTableQuery( $query_vars );
$query = new OrdersTableQuery( $query_vars );
$this->assertEquals( 1, count( $query->orders ) );
$this->assertEquals( $query->orders[0], $order2->get_id() );
$query_vars['prices_include_tax'] = 'yes';
$query = new OrdersTableQuery( $query_vars );
$query = new OrdersTableQuery( $query_vars );
$this->assertEquals( 1, count( $query->orders ) );
$this->assertEquals( $query->orders[0], $order1->get_id() );
// Get orders with two specific properties.
$query_vars['total'] = '100.0';
$query = new OrdersTableQuery( $query_vars );
$query = new OrdersTableQuery( $query_vars );
$this->assertEquals( 1, count( $query->orders ) );
$this->assertEquals( $query->orders[0], $order1->get_id() );
// Limit results.
unset( $query_vars['total'], $query_vars['prices_include_tax'] );
$query_vars['limit'] = 1;
$query = new OrdersTableQuery( $query_vars );
$query_vars['limit'] = 1;
$query = new OrdersTableQuery( $query_vars );
$this->assertEquals( 1, count( $query->orders ) );
// By customer ID.
$query = new OrdersTableQuery(
array(
'status' => 'all',
'customer_id' => $user_id
'customer_id' => $user_id,
)
);
$this->assertEquals( 1, count( $query->orders ) );
@ -412,6 +422,8 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$order3->set_status( 'completed' );
$order3->save();
// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query,WordPress.DB.SlowDBQuery.slow_db_query_meta_key
// Orders with color=green.
$query = new OrdersTableQuery(
array(
@ -419,8 +431,8 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
array(
'key' => 'color',
'value' => 'green',
)
)
),
),
)
);
$this->assertEquals( 2, count( $query->orders ) );
@ -431,12 +443,12 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
'meta_query' => array(
array(
'key' => 'animal',
'value' => 'lion'
'value' => 'lion',
),
array(
'key' => 'movie',
)
)
'key' => 'movie',
),
),
)
);
$this->assertEquals( 2, count( $query->orders ) );
@ -452,7 +464,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
'value' => 'London',
'compare' => 'LIKE',
),
)
),
)
);
$this->assertEquals( 2, count( $query->orders ) );
@ -464,7 +476,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
array(
'meta_query' => array(
array(
'key' => 'animal',
'key' => 'animal',
),
array(
'relation' => 'OR',
@ -475,9 +487,9 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
array(
'key' => 'place',
'value' => 'Paris',
)
)
)
),
),
),
)
);
$this->assertEquals( 2, count( $query->orders ) );
@ -493,6 +505,81 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
);
$this->assertEquals( 1, count( $query->orders ) );
$this->assertContains( $order2->get_id(), $query->orders );
// phpcs:enable
}
/**
* Tests queries involving the 'customer' query var.
*
* @return void
*/
public function test_cot_query_customer() {
$user_email_1 = 'email1@example.com';
$user_email_2 = 'email2@example.com';
$user_id_1 = wp_insert_user(
array(
'user_login' => 'user_1',
'user_pass' => 'testing',
'user_email' => $user_email_1,
)
);
$user_id_2 = wp_insert_user(
array(
'user_login' => 'user_2',
'user_pass' => 'testing',
'user_email' => $user_email_2,
)
);
$order1 = new WC_Order();
$this->switch_data_store( $order1, $this->sut );
$order1->set_customer_id( $user_id_1 );
$order1->save();
$order2 = new WC_Order();
$this->switch_data_store( $order2, $this->sut );
$order2->set_customer_id( $user_id_2 );
$order2->save();
$order3 = new WC_Order();
$this->switch_data_store( $order3, $this->sut );
$order3->set_customer_id( $user_id_2 );
$order3->save();
// Search for orders of either user (by ID). Should return all orders.
$query = new OrdersTableQuery(
array(
'customer' => array( $user_id_1, $user_id_2 ),
)
);
$this->assertEquals( 3, $query->found_orders );
// Search for user 1 (by e-mail) and user 2 (by ID). Should return all orders.
$query = new OrdersTableQuery(
array(
'customer' => array( $user_email_1, $user_id_2 ),
)
);
$this->assertEquals( 3, $query->found_orders );
// Search for orders that match user 1 (email and ID). Should return order 1.
$query = new OrdersTableQuery(
array(
'customer' => array( array( $user_email_1, $user_id_1 ) ),
)
);
$this->assertEquals( 1, $query->found_orders );
$this->assertContains( $order1->get_id(), $query->orders );
// Search for orders that match user 1 (email) and user 2 (ID). Should return no order.
$query = new OrdersTableQuery(
array(
'customer' => array( array( $user_email_1, $user_id_2 ) ),
)
);
$this->assertEquals( 0, $query->found_orders );
}
/**
@ -518,6 +605,10 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$update_data_store_func->call( $order, $data_store );
}
/**
* Creates a complex COT order with address info, line items, etc.
* @return \WC_Order
*/
private function create_complex_cot_order() {
$order = new WC_Order();
$this->switch_data_store( $order, $this->sut );
@ -566,7 +657,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$order->get_data_store()->set_stock_reduced( $order, true, false );
$order->update_meta_data( 'my_meta', rand( 0, 255 ) );
$order->update_meta_data( 'my_meta', rand( 0, 255 ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand
$order->save();