status != 'auto-draft'
AND orders.id IS NULL";
break;
case self::ID_TYPE_MISSING_IN_POSTS_TABLE:
$sql = "
SELECT posts.ID FROM $wpdb->posts posts
INNER JOIN $orders_table orders ON posts.id=orders.id
WHERE posts.post_type = '" . self::PLACEHOLDER_ORDER_POST_TYPE . "'";
break;
case self::ID_TYPE_DIFFERENT_UPDATE_DATE:
$sql = "
SELECT orders.id FROM $orders_table orders
JOIN $wpdb->posts posts on posts.ID = orders.id
WHERE
posts.post_type = 'shop_order'
AND orders.date_updated_gmt != posts.post_modified_gmt";
break;
default:
throw new \Exception( 'Invalid $type, must be one of the ID_TYPE_... constants.' );
}
// phpcs:ignore WordPress.DB
return array_map( 'intval', $wpdb->get_col( $sql . " LIMIT $limit" ) );
}
/**
* Start an orders synchronization process if all the following is true:
*
* 1. Data synchronization is enabled.
* 2. Data synchronization isn't already in progress ($force can be used to bypass this).
* 3. There's at least one out of sync order.
*
* This will set up the appropriate status information and schedule the first synchronization batch.
*
* @param bool $force If true, (re)start the sync process even if it's already in progress.
*/
public function maybe_start_synchronizing_pending_orders( bool $force = false ) {
if ( ! $this->data_sync_is_enabled() || ( $this->pending_data_sync_is_in_progress() && ! $force ) ) {
return;
}
$initial_pending_count = $this->get_current_orders_pending_sync_count();
if ( 0 === $initial_pending_count ) {
return;
}
update_option( self::INITIAL_ORDERS_PENDING_SYNC_COUNT_OPTION, $initial_pending_count );
$queue = WC()->get_instance_of( \WC_Queue::class );
$queue->cancel_all( self::ORDERS_SYNC_SCHEDULED_ACTION_CALLBACK );
update_option( self::PENDING_SYNC_IS_IN_PROGRESS_OPTION, 'yes' );
$this->schedule_pending_orders_synchronization();
}
/**
* Schedule the next orders synchronization batch.
*/
private function schedule_pending_orders_synchronization() {
$queue = WC()->get_instance_of( \WC_Queue::class );
$queue->schedule_single(
WC()->call_function( 'time' ) + self::SECONDS_BETWEEN_BATCH_SYNCS,
self::ORDERS_SYNC_SCHEDULED_ACTION_CALLBACK,
array(),
'woocommerce-db-updates'
);
}
/**
* Run one orders synchronization batch.
*/
private function do_pending_orders_synchronization() {
if ( ! $this->pending_data_sync_is_in_progress() ) {
return;
}
// TODO: Remove the usage of the fake pending orders count once development of the feature is complete.
$fake_count = get_option( self::FAKE_ORDERS_PENDING_SYNC_COUNT_OPTION );
if ( false !== $fake_count ) {
update_option( 'woocommerce_fake_orders_pending_sync_count', (int) $fake_count - 1 );
} else {
$this->sync_next_batch();
}
if ( 0 === $this->get_current_orders_pending_sync_count() ) {
$this->cleanup_synchronization_state();
/**
* Hook to signal that the orders tables synchronization process has finished (nothing left to synchronize).
*
* @since 6.5.0
*/
do_action( self::PENDING_SYNCHRONIZATION_FINISHED_ACTION );
} else {
$this->schedule_pending_orders_synchronization();
}
}
/**
* Processes a batch of out of sync orders.
* First it synchronizes orders that don't exist in the backup table, and after that,
* it synchronizes orders that exist in both tables but have a different last update date.
*
* @return void
*/
private function sync_next_batch(): void {
/**
* Filter to customize the count of orders that will be synchronized in each step of the custom orders table to/from posts table synchronization process.
*
* @since 6.6.0
*
* @param int Default value for the count.
*/
$batch_size = apply_filters( 'woocommerce_orders_cot_and_posts_sync_step_size', self::ORDERS_SYNC_BATCH_SIZE );
if ( $this->custom_orders_table_is_authoritative() ) {
$order_ids = $this->get_ids_of_orders_pending_sync( self::ID_TYPE_MISSING_IN_POSTS_TABLE, $batch_size );
// TODO: Load $order_ids orders from the orders table and create them (by updating the corresponding placeholder record) in the posts table.
} else {
$order_ids = $this->get_ids_of_orders_pending_sync( self::ID_TYPE_MISSING_IN_ORDERS_TABLE, $batch_size );
$this->posts_to_cot_migrator->migrate_orders( $order_ids );
}
$batch_size -= count( $order_ids );
if ( 0 === $batch_size ) {
return;
}
$order_ids = $this->get_ids_of_orders_pending_sync( self::ID_TYPE_DIFFERENT_UPDATE_DATE, $batch_size );
if ( 0 === count( $order_ids ) ) {
return;
}
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
if ( $this->custom_orders_table_is_authoritative() ) {
// TODO: Load $order_ids orders from the orders table and update them in the posts table.
} else {
$this->posts_to_cot_migrator->migrate_orders( $order_ids );
}
}
/**
* Cleanup all the synchronization status information,
* because the process has been disabled by the user via settings,
* or because there's nothing left to syncrhonize.
*/
public function cleanup_synchronization_state() {
delete_option( self::INITIAL_ORDERS_PENDING_SYNC_COUNT_OPTION );
delete_option( self::PENDING_SYNC_IS_IN_PROGRESS_OPTION );
}
}