Automattic\WooCommerce\Internal\Admin\Schedulers
OrdersScheduler::process_pending_batch
Process pending orders in batch.
This method queries for orders updated since the last cursor position (compound cursor: date + ID) and imports them into the analytics tables.
Method of the class: OrdersScheduler{}
No Hooks.
Returns
null. Nothing (null).
Usage
$result = OrdersScheduler::process_pending_batch( $cursor_date, $cursor_id );
- $cursor_date(string|null)
- Cursor date in
'Y-m-d H:i:s'format. Orders after this date will be processed.
Default:null - $cursor_id(int|null)
- Cursor order ID. Combined with
$cursor_dateto form compound cursor.
Default:null
OrdersScheduler::process_pending_batch() OrdersScheduler::process pending batch code WC 10.5.0
public static function process_pending_batch( $cursor_date = null, $cursor_id = null ) {
$logger = wc_get_logger();
$context = array( 'source' => 'wc-analytics-order-import' );
if ( self::is_importing() ) {
// No need to process if an import is already in progress.
$logger->info( 'Import is already in progress, skipping batch import.', $context );
return;
}
// Load cursor position from options if not provided.
// If the cursor date is not provided, use the last 24 hours as the default since `action_scheduler_ensure_recurring_actions` runs daily so 24 hours is enough.
$default_cursor_date = gmdate( 'Y-m-d H:i:s', strtotime( '-24 hours' ) );
$cursor_date = $cursor_date ?? get_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, $default_cursor_date );
$cursor_id = $cursor_id ?? (int) get_option( self::LAST_PROCESSED_ORDER_ID_OPTION, 0 );
// Validate cursor date.
if ( ! $cursor_date || ! strtotime( $cursor_date ) ) {
$logger->error( 'Invalid cursor date: ' . $cursor_date, $context );
$cursor_date = $default_cursor_date;
}
$batch_size = self::get_batch_size( self::PROCESS_PENDING_ORDERS_BATCH_ACTION );
$logger->info(
sprintf( 'Starting batch import. Cursor: %s (ID: %d), batch size: %d', $cursor_date, $cursor_id, $batch_size ),
$context
);
$start_time = microtime( true );
// Get orders updated since the cursor position.
$orders = self::get_orders_since( $cursor_date, $cursor_id, $batch_size );
if ( empty( $orders ) ) {
$logger->info( 'No orders to process', $context );
// Update the cursor position to the start time of the batch so that the next batch will start from that point.
update_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, gmdate( 'Y-m-d H:i:s', (int) $start_time ), false );
update_option( self::LAST_PROCESSED_ORDER_ID_OPTION, 0, false );
return;
}
$processed_count = 0;
foreach ( $orders as $order ) {
try {
self::import( $order->id );
++$processed_count;
// Advance cursor after each successful import. Since orders are sorted by
// date ASC, id ASC, we can simply overwrite with the current order's values.
// If an error occurs, we break and save the last successful position.
$cursor_date = $order->date_updated_gmt;
$cursor_id = $order->id;
} catch ( \Exception $e ) {
$logger->error(
sprintf( 'Failed to import order %d: %s', $order->id, $e->getMessage() ),
$context
);
break;
}
}
// Save the updated cursor position.
update_option( self::LAST_PROCESSED_ORDER_DATE_OPTION, $cursor_date, false );
update_option( self::LAST_PROCESSED_ORDER_ID_OPTION, $cursor_id, false );
$elapsed_time = microtime( true ) - $start_time;
$logger->info(
sprintf(
'Batch import completed. Processed: %d orders in %.2f seconds. Cursor: %s (ID: %d)',
$processed_count,
$elapsed_time,
$cursor_date,
$cursor_id
),
$context
);
// If we got a full batch, there might be more orders to process.
// Schedule immediate next batch.
if ( $processed_count === $batch_size ) {
$logger->info( 'Full batch processed, scheduling next batch', $context );
self::schedule_action(
'process_pending_batch',
array( $cursor_date, $cursor_id )
);
}
}