Skip to content

Commit

Permalink
add profile data to REST response
Browse files Browse the repository at this point in the history
  • Loading branch information
kilbot committed Jun 8, 2024
1 parent 7a49ddb commit f5b7664
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 80 deletions.
109 changes: 80 additions & 29 deletions includes/API/Customers_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque
* Optimised query for getting all customer IDs.
*/
if ( $request->get_param( 'posts_per_page' ) == -1 && $request->get_param( 'fields' ) !== null ) {
return $this->wcpos_get_all_posts( $request->get_param( 'fields' ) );
return $this->wcpos_get_all_posts( $request );
}

return $dispatch_result;
Expand Down Expand Up @@ -236,17 +236,23 @@ function ( $meta ) {
* multisite would return all users from all sites, not just the current site.
* Also, querying by role is not as simple as querying by post type.
*
* @param array $fields Fields to return. Default is ID.
* @param WP_REST_Request $request Full details about the request.
*
* @return array|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
public function wcpos_get_all_posts( $request ) {
global $wpdb;

// Start timing execution
$start_time = microtime( true );

$modified_after = $request->get_param( 'modified_after' );
$dates_are_gmt = true;
$fields = $request->get_param( 'fields' );
$id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields;

$args = array(
'fields' => array( 'ID', 'registered' ), // Return only the ID and registered date.
'fields' => array( 'ID', 'user_registered' ), // Return only the ID and registered date.
// 'role__in' => 'all', // @TODO: could be an array of roles, like ['customer', 'cashier']
);

Expand All @@ -262,36 +268,81 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
$last_updates = array();

if ( $id_with_modified_date ) {
$last_update_results = $wpdb->get_results(
"
SELECT user_id, meta_value
FROM $wpdb->usermeta
WHERE meta_key = 'last_update'",
OBJECT_K
);

foreach ( $last_update_results as $user_id => $meta ) {
$last_updates[ $user_id ] = $meta->meta_value;
$query = "
SELECT user_id, meta_value
FROM $wpdb->usermeta
WHERE meta_key = 'last_update'
";

// If modified_after param is set, add the condition to the query
if ( $modified_after ) {
$modified_after_timestamp = strtotime( $modified_after );
$query .= $wpdb->prepare( ' AND meta_value > %d', $modified_after_timestamp );
}

$last_update_results = $wpdb->get_results( $query );

// Manually create the associative array of user_id => last_update
foreach ( $last_update_results as $result ) {
$last_updates[ $result->user_id ] = is_numeric( $result->meta_value ) ? gmdate( 'Y-m-d\TH:i:s', (int) $result->meta_value ) : null;
}
}

// Merge user IDs with their corresponding 'last_updated' values or fallback to user_registered.
$user_data = array_map(
function ( $user ) use ( $last_updates, $id_with_modified_date ) {
$user_info = array( 'id' => (int) $user->ID );
if ( $id_with_modified_date ) {
if ( isset( $last_updates[ $user->ID ] ) ) {
$user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $last_updates[ $user->ID ] );
} else {
$user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $user->user_registered );
/**
* Performance notes:
* - Using a generator is faster than array_map when dealing with large datasets.
* - If date is in the format 'Y-m-d H:i:s' we just do preg_replace to 'Y-m-d\TH:i:s',
* rather than using wc_rest_prepare_date_response
*
* This resulted in execution time of 10% of the original time.
*
* If the modified_after param is set, we don't need to loop through the entire user list.
* The last_update_results array will only contain the users that have been modified after the given date.
* We just need to check they are valid user ids, this sucks, but there could be orphaned last_update meta values.
*/
$formatted_results = array();

if ( $modified_after ) {
foreach ( $users as $user ) {
if ( isset( $last_updates[ $user->ID ] ) ) {
$user_info = array( 'id' => (int) $user->ID );
if ( $id_with_modified_date ) {
$user_info['date_modified_gmt'] = $last_updates[ $user->ID ];
}
$formatted_results[] = $user_info;
}
return $user_info;
},
$users
);
}
} else {
function format_results( $users, $last_updates, $id_with_modified_date ) {
foreach ( $users as $user ) {
$user_info = array( 'id' => (int) $user->ID );
if ( $id_with_modified_date ) {
if ( isset( $last_updates[ $user->ID ] ) && ! empty( $last_updates[ $user->ID ] ) ) {
$user_info['date_modified_gmt'] = $last_updates[ $user->ID ];
} else {
$user_info['date_modified_gmt'] = null; // users can have null date_modified_gmt
}
}
yield $user_info;
}
}

$formatted_results = iterator_to_array( format_results( $users, $last_updates, $id_with_modified_date ) );
}

// Get the total number of orders for the given criteria.
$total = count( $formatted_results );

// Collect execution time and server load.
$execution_time = microtime( true ) - $start_time;
$server_load = sys_getloadavg();

$response = rest_ensure_response( $formatted_results );
$response->header( 'X-WP-Total', (int) $total );
$response->header( 'X-Execution-Time', $execution_time );
$response->header( 'X-Server-Load', json_encode( $server_load ) );

return $user_data;
return $response;
} catch ( Exception $e ) {
Logger::log( 'Error fetching order IDs: ' . $e->getMessage() );

Expand Down
47 changes: 40 additions & 7 deletions includes/API/Orders_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use WP_REST_Response;
use WP_REST_Server;
use Automattic\WooCommerce\Utilities\OrderUtil;
use WCPOS\WooCommercePOS\Services\Cache;
use WP_Error;

use const WCPOS\WooCommercePOS\PLUGIN_NAME;
Expand Down Expand Up @@ -101,7 +102,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque
* Optimised query for getting all order IDs.
*/
if ( $request->get_param( 'posts_per_page' ) == -1 && $request->get_param( 'fields' ) !== null ) {
return $this->wcpos_get_all_posts( $request->get_param( 'fields' ) );
return $this->wcpos_get_all_posts( $request );
}

return $dispatch_result;
Expand Down Expand Up @@ -220,7 +221,7 @@ public function create_item( $request ) {
Logger::log( 'UUID already in use, return existing order.', $ids[0] );

// Create a new WP_REST_Request object for the GET request.
$get_request = new WP_REST_Request( 'GET', '/wc/v3/orders/' . $ids[0] );
$get_request = new WP_REST_Request( 'GET', $this->namespace . '/' . $this->rest_base . '/' . $ids[0] );
$get_request->set_param( 'id', $ids[0] );

return $this->get_item( $get_request );
Expand Down Expand Up @@ -669,13 +670,19 @@ public function wcpos_hpos_orders_table_query_clauses( array $clauses, $query, a
/**
* Returns array of all order ids.
*
* @param array $fields Fields to return.
* @param WP_REST_Request $request Full details about the request.
*
* @return array|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
public function wcpos_get_all_posts( $request ) {
global $wpdb;

// Start timing execution.
$start_time = microtime( true );

$modified_after = $request->get_param( 'modified_after' );
$dates_are_gmt = true; // Dates are always in GMT.
$fields = $request->get_param( 'fields' );
$id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields;

// Check if HPOS is enabled and custom orders table is used.
Expand All @@ -694,20 +701,46 @@ function ( $status ) {
$sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->prefix}wc_orders WHERE type = 'shop_order'";
$sql .= ' AND status IN (' . implode( ',', $statuses ) . ')';

// Add modified_after condition if provided.
if ( $modified_after ) {
$modified_after_date = date( 'Y-m-d H:i:s', strtotime( $modified_after ) );
$sql .= $wpdb->prepare( ' AND date_updated_gmt > %s', $modified_after_date );
}

// Order by date_created_gmt DESC to maintain order consistency.
$sql .= " ORDER BY {$wpdb->prefix}wc_orders.date_created_gmt DESC";
} else {
$select_fields = $id_with_modified_date ? 'ID as id, post_modified_gmt as date_modified_gmt' : 'ID as id';
$sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->posts} WHERE post_type = 'shop_order'";
$sql .= ' AND post_status IN (' . implode( ',', $statuses ) . ')';

// Add modified_after condition if provided.
if ( $modified_after ) {
$modified_after_date = date( 'Y-m-d H:i:s', strtotime( $modified_after ) );
$sql .= $wpdb->prepare( ' AND post_modified_gmt > %s', $modified_after_date );
}

// Order by post_date DESC to maintain order consistency.
$sql .= " ORDER BY {$wpdb->posts}.post_date DESC";
}

try {
$results = $wpdb->get_results( $sql );
return $this->wcpos_format_all_posts_response( $results );
$results = $wpdb->get_results( $sql, ARRAY_A );
$formatted_results = $this->wcpos_format_all_posts_response( $results );

// Get the total number of orders for the given criteria.
$total = count( $formatted_results );

// Collect execution time and server load.
$execution_time = microtime( true ) - $start_time;
$server_load = sys_getloadavg();

$response = rest_ensure_response( $formatted_results );
$response->header( 'X-WP-Total', (int) $total );
$response->header( 'X-Execution-Time', $execution_time );
$response->header( 'X-Server-Load', json_encode( $server_load ) );

return $response;
} catch ( Exception $e ) {
Logger::log( 'Error fetching order data: ' . $e->getMessage() );
return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching order data.', array( 'status' => 500 ) );
Expand Down
31 changes: 24 additions & 7 deletions includes/API/Product_Categories_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque
* Optimised query for getting all category IDs.
*/
if ( $request->get_param( 'posts_per_page' ) == -1 && $request->get_param( 'fields' ) !== null ) {
return $this->wcpos_get_all_posts( $request->get_param( 'fields' ) );
return $this->wcpos_get_all_posts( $request );
}

return $dispatch_result;
Expand Down Expand Up @@ -141,11 +141,14 @@ public function wcpos_terms_clauses_include_exclude( array $clauses, array $taxo
/**
* Returns array of all product category ids.
*
* @param array $fields
* @param WP_REST_Request $request Full details about the request.
*
* @return array|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
public function wcpos_get_all_posts( $request ) {
// Start timing execution.
$start_time = microtime( true );

$args = array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
Expand All @@ -156,12 +159,26 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
$results = get_terms( $args );

// Format the response.
return array_map(
function ( $item ) {
return array( 'id' => (int) $item );
$formatted_results = array_map(
function ( $id ) {
return array( 'id' => (int) $id );
},
$results
);

// Get the total number of orders for the given criteria.
$total = count( $formatted_results );

// Collect execution time and server load.
$execution_time = microtime( true ) - $start_time;
$server_load = sys_getloadavg();

$response = rest_ensure_response( $formatted_results );
$response->header( 'X-WP-Total', (int) $total );
$response->header( 'X-Execution-Time', $execution_time );
$response->header( 'X-Server-Load', json_encode( $server_load ) );

return $response;
} catch ( Exception $e ) {
Logger::log( 'Error fetching product category IDs: ' . $e->getMessage() );

Expand Down
31 changes: 24 additions & 7 deletions includes/API/Product_Tags_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque
* Optimised query for getting all tag IDs.
*/
if ( $request->get_param( 'posts_per_page' ) == -1 && $request->get_param( 'fields' ) !== null ) {
return $this->wcpos_get_all_posts( $request->get_param( 'fields' ) );
return $this->wcpos_get_all_posts( $request );
}

return $dispatch_result;
Expand Down Expand Up @@ -141,11 +141,14 @@ public function wcpos_terms_clauses_include_exclude( array $clauses, array $taxo
/**
* Returns array of all product tag ids.
*
* @param array $fields
* @param WP_REST_Request $request Full details about the request.
*
* @return array|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
public function wcpos_get_all_posts( $request ): array {
// Start timing execution.
$start_time = microtime( true );

$args = array(
'taxonomy' => 'product_tag',
'hide_empty' => false,
Expand All @@ -156,12 +159,26 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
$results = get_terms( $args );

// Format the response.
return array_map(
function ( $item ) {
return array( 'id' => (int) $item );
$formatted_results = array_map(
function ( $id ) {
return array( 'id' => (int) $id );
},
$results
);

// Get the total number of orders for the given criteria.
$total = count( $formatted_results );

// Collect execution time and server load.
$execution_time = microtime( true ) - $start_time;
$server_load = sys_getloadavg();

$response = rest_ensure_response( $formatted_results );
$response->header( 'X-WP-Total', (int) $total );
$response->header( 'X-Execution-Time', $execution_time );
$response->header( 'X-Server-Load', json_encode( $server_load ) );

return $response;
} catch ( Exception $e ) {
Logger::log( 'Error fetching product tags IDs: ' . $e->getMessage() );

Expand Down
Loading

0 comments on commit f5b7664

Please sign in to comment.