rebase on oct-10-2023
This commit is contained in:
@@ -168,19 +168,21 @@ class CLIRunner {
|
||||
return WP_CLI::warning( __( 'There are no orders to sync, aborting.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args,
|
||||
array(
|
||||
'batch-size' => 500,
|
||||
)
|
||||
);
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Sync', $order_count / $batch_size );
|
||||
$processed = 0;
|
||||
$batch_count = 1;
|
||||
$total_time = 0;
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Sync', $order_count / $batch_size );
|
||||
$processed = 0;
|
||||
$batch_count = 1;
|
||||
$total_time = 0;
|
||||
$orders_remaining = true;
|
||||
|
||||
while ( $order_count > 0 ) {
|
||||
while ( $order_count > 0 || $orders_remaining ) {
|
||||
$remaining_count = $order_count;
|
||||
|
||||
WP_CLI::debug(
|
||||
sprintf(
|
||||
@@ -213,11 +215,8 @@ class CLIRunner {
|
||||
|
||||
$progress->tick();
|
||||
|
||||
$remaining_count = $this->count_unmigrated( array(), array( 'log' => false ) );
|
||||
if ( $remaining_count === $order_count ) {
|
||||
return WP_CLI::error( __( 'Infinite loop detected, aborting.', 'woocommerce' ) );
|
||||
}
|
||||
$order_count = $remaining_count;
|
||||
$orders_remaining = count( $this->synchronizer->get_next_batch_to_process( 1 ) ) > 0;
|
||||
$order_count = $remaining_count - $batch_size;
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
@@ -300,6 +299,16 @@ class CLIRunner {
|
||||
* default: false
|
||||
* ---
|
||||
*
|
||||
* [--order-types]
|
||||
* : Comma seperated list of order types that needs to be verified. For example, --order-types=shop_order,shop_order_refund
|
||||
* ---
|
||||
* default: Output of function `wc_get_order_types( 'cot-migration' )`
|
||||
*
|
||||
* [--re-migrate]
|
||||
* : Attempt to re-migrate orders that failed verification. You should only use this option when you have never run the site with HPOS as authoritative source of order data yet, or you have manually checked the reported errors, otherwise, you risk stale data overwriting the more recent data.
|
||||
* This option can only be enabled when --verbose flag is also set.
|
||||
* default: false
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* # Verify migrated order data, 500 orders at a time.
|
||||
@@ -318,10 +327,12 @@ class CLIRunner {
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args,
|
||||
array(
|
||||
'batch-size' => 500,
|
||||
'start-from' => 0,
|
||||
'end-at' => -1,
|
||||
'verbose' => false,
|
||||
'batch-size' => 500,
|
||||
'start-from' => 0,
|
||||
'end-at' => - 1,
|
||||
'verbose' => false,
|
||||
'order-types' => '',
|
||||
're-migrate' => false,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -332,9 +343,28 @@ class CLIRunner {
|
||||
$order_id_start = (int) $assoc_args['start-from'];
|
||||
$order_id_end = (int) $assoc_args['end-at'];
|
||||
$order_id_end = -1 === $order_id_end ? PHP_INT_MAX : $order_id_end;
|
||||
$order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false );
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$verbose = (bool) $assoc_args['verbose'];
|
||||
$order_types = wc_get_order_types( 'cot-migration' );
|
||||
$remigrate = (bool) $assoc_args['re-migrate'];
|
||||
if ( ! empty( $assoc_args['order-types'] ) ) {
|
||||
$passed_order_types = array_map( 'trim', explode( ',', $assoc_args['order-types'] ) );
|
||||
$order_types = array_intersect( $order_types, $passed_order_types );
|
||||
}
|
||||
|
||||
if ( 0 === count( $order_types ) ) {
|
||||
return WP_CLI::error(
|
||||
sprintf(
|
||||
/* Translators: %s is the comma seperated list of order types. */
|
||||
__( 'Passed order type does not match any registered order types. Following order types are registered: %s', 'woocommerce' ),
|
||||
implode( ',', wc_get_order_types( 'cot-migration' ) )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$order_types_pl = implode( ',', array_fill( 0, count( $order_types ), '%s' ) );
|
||||
|
||||
$order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, $order_types, false );
|
||||
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Verification', $order_count / $batch_size );
|
||||
|
||||
@@ -352,14 +382,21 @@ class CLIRunner {
|
||||
)
|
||||
);
|
||||
|
||||
$order_ids = $wpdb->get_col(
|
||||
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Inputs are prepared.
|
||||
$order_ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID >= %d AND ID <= %d ORDER BY ID ASC LIMIT %d",
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
$batch_size
|
||||
"SELECT ID FROM $wpdb->posts WHERE post_type in ( $order_types_pl ) AND ID >= %d AND ID <= %d ORDER BY ID ASC LIMIT %d",
|
||||
array_merge(
|
||||
$order_types,
|
||||
array(
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
$batch_size,
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
$batch_start_time = microtime( true );
|
||||
$failed_ids_in_current_batch = $this->post_to_cot_migrator->verify_migrated_orders( $order_ids );
|
||||
$failed_ids_in_current_batch = $this->verify_meta_data( $order_ids, $failed_ids_in_current_batch );
|
||||
@@ -370,8 +407,7 @@ class CLIRunner {
|
||||
$total_time += $batch_total_time;
|
||||
|
||||
if ( $verbose && count( $failed_ids_in_current_batch ) > 0 ) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- This is a CLI command and debugging code is intended.
|
||||
$errors = print_r( $failed_ids_in_current_batch, true );
|
||||
$errors = wp_json_encode( $failed_ids_in_current_batch, JSON_PRETTY_PRINT );
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
/* Translators: %1$d is number of errors and %2$s is the formatted array of order IDs. */
|
||||
@@ -385,6 +421,35 @@ class CLIRunner {
|
||||
$errors
|
||||
)
|
||||
);
|
||||
if ( $remigrate ) {
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
__( 'Attempting to remigrate...', 'woocommerce' )
|
||||
)
|
||||
);
|
||||
$failed_ids = array_keys( $failed_ids_in_current_batch );
|
||||
$this->synchronizer->process_batch( $failed_ids );
|
||||
$errors_in_remigrate_batch = $this->post_to_cot_migrator->verify_migrated_orders( $failed_ids );
|
||||
$errors_in_remigrate_batch = $this->verify_meta_data( $failed_ids, $errors_in_remigrate_batch );
|
||||
if ( count( $errors_in_remigrate_batch ) > 0 ) {
|
||||
$formatted_errors = wp_json_encode( $errors_in_remigrate_batch, JSON_PRETTY_PRINT );
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
/* Translators: %1$d is number of errors and %2$s is the formatted array of order IDs. */
|
||||
_n(
|
||||
'%1$d error found: %2$s when re-migrating order. Please review the error above.',
|
||||
'%1$d errors found: %2$s when re-migrating orders. Please review the errors above.',
|
||||
count( $errors_in_remigrate_batch ),
|
||||
'woocommerce'
|
||||
),
|
||||
count( $errors_in_remigrate_batch ),
|
||||
$formatted_errors
|
||||
)
|
||||
);
|
||||
} else {
|
||||
WP_CLI::warning( 'Re-migration successful.', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$progress->tick();
|
||||
@@ -400,7 +465,7 @@ class CLIRunner {
|
||||
);
|
||||
|
||||
$order_id_start = max( $order_ids ) + 1;
|
||||
$remaining_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false );
|
||||
$remaining_count = $this->get_verify_order_count( $order_id_start, $order_id_end, $order_types, false );
|
||||
if ( $remaining_count === $order_count ) {
|
||||
return WP_CLI::error( __( 'Infinite loop detected, aborting. No errors found.', 'woocommerce' ) );
|
||||
}
|
||||
@@ -429,7 +494,7 @@ class CLIRunner {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$errors = print_r( $failed_ids, true );
|
||||
$errors = wp_json_encode( $failed_ids, JSON_PRETTY_PRINT );
|
||||
|
||||
return WP_CLI::error(
|
||||
sprintf(
|
||||
@@ -464,22 +529,32 @@ class CLIRunner {
|
||||
/**
|
||||
* Helper method to get count for orders needing verification.
|
||||
*
|
||||
* @param int $order_id_start Order ID to start from.
|
||||
* @param int $order_id_end Order ID to end at.
|
||||
* @param bool $log Whether to also log an error message.
|
||||
* @param int $order_id_start Order ID to start from.
|
||||
* @param int $order_id_end Order ID to end at.
|
||||
* @param array $order_types List of order types to verify.
|
||||
* @param bool $log Whether to also log an error message.
|
||||
*
|
||||
* @return int Order count.
|
||||
*/
|
||||
private function get_verify_order_count( int $order_id_start, int $order_id_end, $log = true ) : int {
|
||||
private function get_verify_order_count( int $order_id_start, int $order_id_end, array $order_types, bool $log = true ) : int {
|
||||
global $wpdb;
|
||||
|
||||
$order_types_placeholder = implode( ',', array_fill( 0, count( $order_types ), '%s' ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Inputs are prepared.
|
||||
$order_count = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID >= %d AND ID <= %d",
|
||||
$order_id_start,
|
||||
$order_id_end
|
||||
"SELECT COUNT(*) FROM $wpdb->posts WHERE post_type in ($order_types_placeholder) AND ID >= %d AND ID <= %d",
|
||||
array_merge(
|
||||
$order_types,
|
||||
array(
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
if ( $log ) {
|
||||
WP_CLI::log(
|
||||
@@ -517,8 +592,8 @@ class CLIRunner {
|
||||
$order_ids_placeholder = implode( ', ', array_fill( 0, count( $order_ids ), '%d' ) );
|
||||
$meta_table = OrdersTableDataStore::get_meta_table_name();
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$query = $wpdb->prepare(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$query = $wpdb->prepare(
|
||||
"
|
||||
SELECT {$wpdb->postmeta}.post_id as entity_id, {$wpdb->postmeta}.meta_key, {$wpdb->postmeta}.meta_value
|
||||
FROM $wpdb->postmeta
|
||||
@@ -537,7 +612,7 @@ ORDER BY {$wpdb->postmeta}.post_id ASC, {$wpdb->postmeta}.meta_key ASC;
|
||||
|
||||
$normalized_source_data = $this->normalize_raw_meta_data( $source_data );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$migrated_query = $wpdb->prepare(
|
||||
"
|
||||
SELECT $meta_table.order_id as entity_id, $meta_table.meta_key, $meta_table.meta_value
|
||||
@@ -556,17 +631,17 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
|
||||
foreach ( $normalized_source_data as $order_id => $meta ) {
|
||||
foreach ( $meta as $meta_key => $values ) {
|
||||
$migrated_meta_values = isset( $normalized_migrated_meta_data[ $order_id ][ $meta_key ] ) ? $normalized_migrated_meta_data[ $order_id ][ $meta_key ] : array();
|
||||
$diff = array_diff( $values, $migrated_meta_values );
|
||||
$diff = array_diff( $values, $migrated_meta_values );
|
||||
|
||||
if ( count( $diff ) ) {
|
||||
if ( ! isset( $failed_ids[ $order_id ] ) ) {
|
||||
$failed_ids[ $order_id ] = array();
|
||||
}
|
||||
$failed_ids[ $order_id ][] = array(
|
||||
'order_id' => $order_id,
|
||||
'meta_key' => $meta_key,
|
||||
'order_id' => $order_id,
|
||||
'meta_key' => $meta_key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a meta query.
|
||||
'orig_meta_values' => $values,
|
||||
'new_meta_values' => $migrated_meta_values,
|
||||
'new_meta_values' => $migrated_meta_values,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -589,7 +664,7 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
|
||||
$clubbed_data[ $row['entity_id'] ] = array();
|
||||
}
|
||||
if ( ! isset( $clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] ) ) {
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] = array();
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a meta query.
|
||||
}
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ][] = $row['meta_value'];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToMetaTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post meta table
|
||||
@@ -39,13 +40,6 @@ class PostMetaToOrderMetaMigrator extends MetaToMetaTableMigrator {
|
||||
*/
|
||||
protected function get_meta_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
@@ -57,15 +51,15 @@ class PostMetaToOrderMetaMigrator extends MetaToMetaTableMigrator {
|
||||
'meta_value_column' => 'meta_value',
|
||||
),
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'source_id_column' => 'id',
|
||||
'id_column' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'source_id_column' => 'ID',
|
||||
'id_column' => 'ID',
|
||||
),
|
||||
'excluded_keys' => $this->excluded_columns,
|
||||
),
|
||||
'destination' => array(
|
||||
'meta' => array(
|
||||
'table_name' => $this->table_names['meta'],
|
||||
'table_name' => OrdersTableDataStore::get_meta_table_name(),
|
||||
'entity_id_column' => 'order_id',
|
||||
'meta_key_column' => 'meta_key',
|
||||
'meta_value_column' => 'meta_value',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToCustomTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post table
|
||||
@@ -38,21 +39,14 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'meta_rel_column' => 'id',
|
||||
'destination_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'meta_rel_column' => 'ID',
|
||||
'destination_rel_column' => 'ID',
|
||||
'primary_key' => 'ID',
|
||||
),
|
||||
'meta' => array(
|
||||
'table_name' => $wpdb->postmeta,
|
||||
@@ -63,7 +57,7 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['addresses'],
|
||||
'table_name' => OrdersTableDataStore::get_addresses_table_name(),
|
||||
'source_rel_column' => 'order_id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
@@ -80,7 +74,7 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
$type = $this->type;
|
||||
|
||||
return array(
|
||||
'id' => array(
|
||||
'ID' => array(
|
||||
'type' => 'int',
|
||||
'destination' => 'order_id',
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToCustomTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post table
|
||||
@@ -22,21 +23,14 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'meta_rel_column' => 'id',
|
||||
'destination_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'meta_rel_column' => 'ID',
|
||||
'destination_rel_column' => 'ID',
|
||||
'primary_key' => 'ID',
|
||||
),
|
||||
'meta' => array(
|
||||
'table_name' => $wpdb->postmeta,
|
||||
@@ -47,7 +41,7 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['op_data'],
|
||||
'table_name' => OrdersTableDataStore::get_operational_data_table_name(),
|
||||
'source_rel_column' => 'order_id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
@@ -63,7 +57,7 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_core_column_mapping(): array {
|
||||
return array(
|
||||
'id' => array(
|
||||
'ID' => array(
|
||||
'type' => 'int',
|
||||
'destination' => 'order_id',
|
||||
),
|
||||
|
||||
@@ -22,7 +22,7 @@ class PostToOrderTableMigrator extends MetaToCustomTableMigrator {
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
|
||||
$this->table_names = array(
|
||||
$table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
@@ -46,7 +46,7 @@ class PostToOrderTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'table_name' => $table_names['orders'],
|
||||
'source_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
|
||||
@@ -26,7 +26,7 @@ class PostsToOrdersMigrationController {
|
||||
/**
|
||||
* Array of objects used to perform the migration.
|
||||
*
|
||||
* @var array
|
||||
* @var TableMigrator[]
|
||||
*/
|
||||
private $all_migrators;
|
||||
|
||||
@@ -40,13 +40,13 @@ class PostsToOrdersMigrationController {
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$this->all_migrators = array();
|
||||
$this->all_migrators[] = new PostToOrderTableMigrator();
|
||||
$this->all_migrators[] = new PostToOrderAddressTableMigrator( 'billing' );
|
||||
$this->all_migrators[] = new PostToOrderAddressTableMigrator( 'shipping' );
|
||||
$this->all_migrators[] = new PostToOrderOpTableMigrator();
|
||||
$this->all_migrators[] = new PostMetaToOrderMetaMigrator( $this->get_migrated_meta_keys() );
|
||||
$this->error_logger = wc_get_logger();
|
||||
$this->all_migrators = array();
|
||||
$this->all_migrators['order'] = new PostToOrderTableMigrator();
|
||||
$this->all_migrators['order_address_billing'] = new PostToOrderAddressTableMigrator( 'billing' );
|
||||
$this->all_migrators['order_address_shipping'] = new PostToOrderAddressTableMigrator( 'shipping' );
|
||||
$this->all_migrators['order_operational_data'] = new PostToOrderOpTableMigrator();
|
||||
$this->all_migrators['order_meta'] = new PostMetaToOrderMetaMigrator( $this->get_migrated_meta_keys() );
|
||||
$this->error_logger = wc_get_logger();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ class PostsToOrdersMigrationController {
|
||||
*/
|
||||
public function get_migrated_meta_keys() {
|
||||
$migrated_meta_keys = array();
|
||||
foreach ( $this->all_migrators as $migrator ) {
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
if ( method_exists( $migrator, 'get_meta_column_config' ) ) {
|
||||
$migrated_meta_keys = array_merge( $migrated_meta_keys, $migrator->get_meta_column_config() );
|
||||
}
|
||||
@@ -72,60 +72,111 @@ class PostsToOrdersMigrationController {
|
||||
public function migrate_orders( array $order_post_ids ): void {
|
||||
$this->error_logger = WC()->call_function( 'wc_get_logger' );
|
||||
|
||||
$using_transactions = $this->maybe_start_transaction();
|
||||
if ( null === $using_transactions ) {
|
||||
$data = array();
|
||||
try {
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
$data[ $name ] = $migrator->fetch_sanitized_migration_data( $order_post_ids );
|
||||
if ( ! empty( $data[ $name ]['errors'] ) ) {
|
||||
$this->handle_migration_error( $order_post_ids, $data[ $name ]['errors'], null, null, $name );
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$this->handle_migration_error( $order_post_ids, $data, $e, null, 'Fetching data' );
|
||||
return;
|
||||
}
|
||||
|
||||
$errors_were_logged = false;
|
||||
$using_transactions = $this->maybe_start_transaction();
|
||||
|
||||
foreach ( $this->all_migrators as $migrator ) {
|
||||
$errors_were_logged = $this->do_orders_migration_step( $migrator, $order_post_ids );
|
||||
if ( $errors_were_logged && $using_transactions ) {
|
||||
$this->rollback_transaction();
|
||||
break;
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
$results = $migrator->process_migration_data( $data[ $name ] );
|
||||
$errors = array_unique( $results['errors'] );
|
||||
$exception = $results['exception'];
|
||||
|
||||
if ( null === $exception && empty( $errors ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->handle_migration_error( $order_post_ids, $errors, $exception, $using_transactions, $name );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $errors_were_logged && $using_transactions ) {
|
||||
if ( $using_transactions ) {
|
||||
$this->commit_transaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Log migration errors if any.
|
||||
*
|
||||
* @param array $order_post_ids List of post IDs of the orders to migrate.
|
||||
* @param array $errors List of errors to log.
|
||||
* @param \Exception|null $exception Exception to log.
|
||||
* @param bool|null $using_transactions Whether transactions were used.
|
||||
* @param string $name Name of the migrator.
|
||||
*/
|
||||
private function handle_migration_error( array $order_post_ids, array $errors, ?\Exception $exception, ?bool $using_transactions, string $name ) {
|
||||
$batch = ArrayUtil::to_ranges_string( $order_post_ids );
|
||||
|
||||
if ( null !== $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
"$name: when processing ids $batch: ($exception_class) {$exception->getMessage()}, {$exception->getTraceAsString()}",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'exception' => $exception,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $errors as $error ) {
|
||||
$this->error_logger->error(
|
||||
"$name: when processing ids $batch: $error",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'error' => $error,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $using_transactions ) {
|
||||
$this->rollback_transaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a database transaction if the configuration mandates so.
|
||||
*
|
||||
* @return bool|null True if transaction started, false if transactions won't be used, null if transaction failed to start.
|
||||
*
|
||||
* @throws \Exception If the transaction isolation level is invalid.
|
||||
*/
|
||||
private function maybe_start_transaction(): ?bool {
|
||||
if ( 'yes' !== get_option( CustomOrdersTableController::USE_DB_TRANSACTIONS_OPTION ) ) {
|
||||
return false;
|
||||
|
||||
$use_transactions = get_option( CustomOrdersTableController::USE_DB_TRANSACTIONS_OPTION, 'yes' );
|
||||
if ( 'yes' !== $use_transactions ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transaction_isolation_level = get_option( CustomOrdersTableController::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION, CustomOrdersTableController::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL );
|
||||
$valid_transaction_isolation_levels = array( 'READ UNCOMMITTED', 'READ COMMITTED', 'REPEATABLE READ', 'SERIALIZABLE' );
|
||||
if ( ! in_array( $transaction_isolation_level, $valid_transaction_isolation_levels, true ) ) {
|
||||
throw new \Exception( "Invalid database transaction isolation level name $transaction_isolation_level" );
|
||||
}
|
||||
|
||||
$transaction_isolation_level = get_option( CustomOrdersTableController::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION, CustomOrdersTableController::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL );
|
||||
$this->verify_transaction_isolation_level( $transaction_isolation_level );
|
||||
$set_transaction_isolation_level_command = "SET TRANSACTION ISOLATION LEVEL $transaction_isolation_level";
|
||||
|
||||
if ( ! $this->db_query( $set_transaction_isolation_level_command ) ) {
|
||||
// We suppress errors in transaction isolation level setting because it's not supported by all DB engines, additionally, this might be executing in context of another transaction with a different isolation level.
|
||||
if ( ! $this->db_query( $set_transaction_isolation_level_command, true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db_query( 'START TRANSACTION' ) ? true : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a given database transaction isolation level name is valid, and throw an exception if not.
|
||||
*
|
||||
* @param string $transaction_isolation_level Transaction isolation level name to check.
|
||||
* @return void
|
||||
* @throws \Exception Invalid transaction isolation level name.
|
||||
*/
|
||||
private function verify_transaction_isolation_level( string $transaction_isolation_level ): void {
|
||||
if ( ! in_array( $transaction_isolation_level, CustomOrdersTableController::get_valid_transaction_isolation_levels(), true ) ) {
|
||||
throw new \Exception( 'Invalid database transaction isolation level name ' . $transaction_isolation_level );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the current database transaction.
|
||||
*
|
||||
@@ -147,15 +198,23 @@ class PostsToOrdersMigrationController {
|
||||
/**
|
||||
* Execute a database query and log any errors.
|
||||
*
|
||||
* @param string $query The SQL query to execute.
|
||||
* @param string $query The SQL query to execute.
|
||||
* @param bool $supress_errors Whether to suppress errors.
|
||||
*
|
||||
* @return bool True if the query succeeded, false if there were errors.
|
||||
*/
|
||||
private function db_query( string $query ): bool {
|
||||
private function db_query( string $query, bool $supress_errors = false ): bool {
|
||||
$wpdb = WC()->get_global( 'wpdb' );
|
||||
|
||||
try {
|
||||
if ( $supress_errors ) {
|
||||
$suppress = $wpdb->suppress_errors( true );
|
||||
}
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( $query );
|
||||
if ( $supress_errors ) {
|
||||
$wpdb->suppress_errors( $suppress );
|
||||
}
|
||||
} catch ( \Exception $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
@@ -183,52 +242,6 @@ class PostsToOrdersMigrationController {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs one step of the migration for a set of order posts using one given migration class.
|
||||
* All database errors and exceptions are logged.
|
||||
*
|
||||
* @param object $migration_class The migration class to use, must have a `process_migration_batch_for_ids(array of ids)` method.
|
||||
* @param array $order_post_ids List of post IDs of the orders to migrate.
|
||||
* @return bool True if errors were logged, false otherwise.
|
||||
*/
|
||||
private function do_orders_migration_step( object $migration_class, array $order_post_ids ): bool {
|
||||
$result = $migration_class->process_migration_batch_for_ids( $order_post_ids );
|
||||
|
||||
$errors = array_unique( $result['errors'] );
|
||||
$exception = $result['exception'];
|
||||
if ( null === $exception && empty( $errors ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration_class_name = ( new \ReflectionClass( $migration_class ) )->getShortName();
|
||||
$batch = ArrayUtil::to_ranges_string( $order_post_ids );
|
||||
|
||||
if ( null !== $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
"$migration_class_name: when processing ids $batch: ($exception_class) {$exception->getMessage()}, {$exception->getTraceAsString()}",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'exception' => $exception,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $errors as $error ) {
|
||||
$this->error_logger->error(
|
||||
"$migration_class_name: when processing ids $batch: $error",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'error' => $error,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify whether the given order IDs were migrated properly or not.
|
||||
*
|
||||
|
||||
@@ -193,16 +193,20 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
$columns[] = $schema['destination'];
|
||||
$placeholders[] = MigrationHelper::get_wpdb_placeholder_for_type( $schema['type'] );
|
||||
}
|
||||
$placeholders = "'" . implode( "', '", $placeholders ) . "'";
|
||||
|
||||
$values = array();
|
||||
foreach ( array_values( $batch ) as $row ) {
|
||||
$query_params = array();
|
||||
foreach ( $columns as $column ) {
|
||||
$query_params[] = $row[ $column ] ?? null;
|
||||
$row_values = array();
|
||||
foreach ( $columns as $index => $column ) {
|
||||
if ( ! isset( $row[ $column ] ) || is_null( $row[ $column ] ) ) {
|
||||
$row_values[] = 'NULL';
|
||||
} else {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- $placeholders is a placeholder.
|
||||
$row_values[] = $wpdb->prepare( $placeholders[ $index ], $row[ $column ] );
|
||||
}
|
||||
}
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $placeholders can only contain combination of placeholders described in MigrationHelper::get_wpdb_placeholder_for_type
|
||||
$value_string = '(' . $wpdb->prepare( $placeholders, $query_params ) . ')';
|
||||
|
||||
$value_string = '(' . implode( ',', $row_values ) . ')';
|
||||
$values[] = $value_string;
|
||||
}
|
||||
|
||||
@@ -214,13 +218,14 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return void
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
public function fetch_sanitized_migration_data( $entity_ids ) {
|
||||
$this->clear_errors();
|
||||
$data = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
|
||||
foreach ( $data['errors'] as $entity_id => $errors ) {
|
||||
@@ -228,25 +233,59 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
$this->add_error( "Error importing data for post with id $entity_id: column $column_name: $error_message" );
|
||||
}
|
||||
}
|
||||
return array(
|
||||
'data' => $data['data'],
|
||||
'errors' => $this->get_errors(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
$data = $this->fetch_sanitized_migration_data( $entity_ids );
|
||||
$this->process_migration_data( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
$this->clear_errors();
|
||||
$exception = null;
|
||||
|
||||
if ( count( $data['data'] ) === 0 ) {
|
||||
return;
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => null,
|
||||
);
|
||||
}
|
||||
|
||||
$entity_ids = array_keys( $data['data'] );
|
||||
$existing_records = $this->get_already_existing_records( $entity_ids );
|
||||
try {
|
||||
$entity_ids = array_keys( $data['data'] );
|
||||
$existing_records = $this->get_already_existing_records( $entity_ids );
|
||||
|
||||
$to_insert = array_diff_key( $data['data'], $existing_records );
|
||||
$this->process_insert_batch( $to_insert );
|
||||
$to_insert = array_diff_key( $data['data'], $existing_records );
|
||||
$this->process_insert_batch( $to_insert );
|
||||
|
||||
$existing_records = array_filter(
|
||||
$existing_records,
|
||||
function( $record_data ) {
|
||||
return '1' === $record_data->modified;
|
||||
}
|
||||
$to_update = array_intersect_key( $data['data'], $existing_records );
|
||||
$this->process_update_batch( $to_update, $existing_records );
|
||||
} catch ( \Exception $e ) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => $exception,
|
||||
);
|
||||
$to_update = array_intersect_key( $data['data'], $existing_records );
|
||||
$this->process_update_batch( $to_update, $existing_records );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,38 +392,13 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
|
||||
$entity_id_placeholder = implode( ',', array_fill( 0, count( $entity_ids ), '%d' ) );
|
||||
|
||||
// Additional SQL to check if the row needs update according to the column mapping.
|
||||
// The IFNULL and CHAR(0) "hack" is needed because NULLs can't be directly compared in SQL.
|
||||
$modified_selector = array();
|
||||
$core_column_mapping = array_filter(
|
||||
$this->core_column_mapping,
|
||||
function( $mapping ) {
|
||||
return ! isset( $mapping['select_clause'] );
|
||||
}
|
||||
);
|
||||
foreach ( $core_column_mapping as $column_name => $mapping ) {
|
||||
if ( $column_name === $source_primary_key_column ) {
|
||||
continue;
|
||||
}
|
||||
$modified_selector[] =
|
||||
"IFNULL(source.$column_name,CHAR(0)) != IFNULL(destination.{$mapping['destination']},CHAR(0))"
|
||||
. ( 'string' === $mapping['type'] ? ' COLLATE ' . $wpdb->collate : '' );
|
||||
}
|
||||
|
||||
if ( empty( $modified_selector ) ) {
|
||||
$modified_selector = ', 1 AS modified';
|
||||
} else {
|
||||
$modified_selector = trim( implode( ' OR ', $modified_selector ) );
|
||||
$modified_selector = ", if( $modified_selector, 1, 0 ) AS modified";
|
||||
}
|
||||
|
||||
$additional_where = $this->get_additional_where_clause_for_get_data_to_insert_or_update( $entity_ids );
|
||||
|
||||
$already_migrated_entity_ids = $this->db_get_results(
|
||||
$wpdb->prepare(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- All columns and table names are hardcoded.
|
||||
"
|
||||
SELECT source.`$source_primary_key_column` as source_id, destination.`$destination_primary_key_column` as destination_id $modified_selector
|
||||
SELECT source.`$source_primary_key_column` as source_id, destination.`$destination_primary_key_column` as destination_id
|
||||
FROM `$destination_table` destination
|
||||
JOIN `$source_table` source ON source.`$source_destination_join_column` = destination.`$destination_source_join_column`
|
||||
WHERE source.`$source_primary_key_column` IN ( $entity_id_placeholder ) $additional_where
|
||||
@@ -567,7 +581,7 @@ WHERE
|
||||
private function validate_data( $value, string $type ) {
|
||||
switch ( $type ) {
|
||||
case 'decimal':
|
||||
$value = wc_format_decimal( $value, false, true );
|
||||
$value = wc_format_decimal( floatval( $value ), false, true );
|
||||
break;
|
||||
case 'int':
|
||||
$value = (int) $value;
|
||||
@@ -832,12 +846,15 @@ WHERE $where_clause
|
||||
if ( ! isset( $row[ $alias ] ) ) {
|
||||
$row[ $alias ] = $this->get_type_defaults( $schema['type'] );
|
||||
}
|
||||
if ( in_array( $schema['type'], array( 'int', 'decimal' ), true ) ) {
|
||||
if ( is_null( $row[ $destination_alias ] ) ) {
|
||||
$row[ $destination_alias ] = $this->get_type_defaults( $schema['type'] );
|
||||
}
|
||||
if ( in_array( $schema['type'], array( 'int', 'decimal', 'float' ), true ) ) {
|
||||
if ( '' === $row[ $alias ] || null === $row[ $alias ] ) {
|
||||
$row[ $alias ] = 0; // $wpdb->prepare forces empty values to 0.
|
||||
}
|
||||
$row[ $alias ] = wc_format_decimal( $row[ $alias ], false, true );
|
||||
$row[ $destination_alias ] = wc_format_decimal( $row[ $destination_alias ], false, true );
|
||||
$row[ $alias ] = wc_format_decimal( floatval( $row[ $alias ] ), false, true );
|
||||
$row[ $destination_alias ] = wc_format_decimal( floatval( $row[ $destination_alias ] ), false, true );
|
||||
}
|
||||
if ( 'bool' === $schema['type'] ) {
|
||||
$row[ $alias ] = wc_string_to_bool( $row[ $alias ] );
|
||||
@@ -867,6 +884,7 @@ WHERE $where_clause
|
||||
switch ( $type ) {
|
||||
case 'float':
|
||||
case 'int':
|
||||
case 'decimal':
|
||||
return 0;
|
||||
case 'string':
|
||||
return '';
|
||||
|
||||
@@ -60,34 +60,78 @@ abstract class MetaToMetaTableMigrator extends TableMigrator {
|
||||
$this->schema_config = $this->get_meta_config();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
public function fetch_sanitized_migration_data( $entity_ids ) {
|
||||
$this->clear_errors();
|
||||
$to_migrate = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
if ( empty( $to_migrate ) ) {
|
||||
return array(
|
||||
'data' => array(),
|
||||
'errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$already_migrated = $this->get_already_migrated_records( array_keys( $to_migrate ) );
|
||||
|
||||
return array(
|
||||
'data' => $this->classify_update_insert_records( $to_migrate, $already_migrated ),
|
||||
'errors' => $this->get_errors(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities ro migrate.
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
$to_migrate = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
if ( empty( $to_migrate ) ) {
|
||||
return;
|
||||
$sanitized_data = $this->fetch_sanitized_migration_data( $entity_ids );
|
||||
$this->process_migration_data( $sanitized_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
if ( isset( $data['data'] ) ) {
|
||||
$data = $data['data'];
|
||||
}
|
||||
$this->clear_errors();
|
||||
$exception = null;
|
||||
|
||||
$already_migrated = $this->get_already_migrated_records( array_keys( $to_migrate ) );
|
||||
|
||||
$data = $this->classify_update_insert_records( $to_migrate, $already_migrated );
|
||||
$to_insert = $data[0];
|
||||
$to_update = $data[1];
|
||||
|
||||
if ( ! empty( $to_insert ) ) {
|
||||
$insert_queries = $this->generate_insert_sql_for_batch( $to_insert );
|
||||
$processed_rows_count = $this->db_query( $insert_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'insert', $processed_rows_count );
|
||||
try {
|
||||
if ( ! empty( $to_insert ) ) {
|
||||
$insert_queries = $this->generate_insert_sql_for_batch( $to_insert );
|
||||
$processed_rows_count = $this->db_query( $insert_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'insert', $processed_rows_count );
|
||||
}
|
||||
|
||||
if ( ! empty( $to_update ) ) {
|
||||
$update_queries = $this->generate_update_sql_for_batch( $to_update );
|
||||
$processed_rows_count = $this->db_query( $update_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'update', $processed_rows_count );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
if ( ! empty( $to_update ) ) {
|
||||
$update_queries = $this->generate_update_sql_for_batch( $to_update );
|
||||
$processed_rows_count = $this->db_query( $update_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'update', $processed_rows_count );
|
||||
}
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => $exception,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +223,7 @@ abstract class MetaToMetaTableMigrator extends TableMigrator {
|
||||
* ...,
|
||||
* )
|
||||
*/
|
||||
private function fetch_data_for_migration_for_ids( array $entity_ids ): array {
|
||||
public function fetch_data_for_migration_for_ids( array $entity_ids ): array {
|
||||
if ( empty( $entity_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ abstract class TableMigrator {
|
||||
* @return void
|
||||
*/
|
||||
protected function add_error( string $error ): void {
|
||||
if ( is_null( $this->errors ) ) {
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
if ( ! in_array( $error, $this->errors, true ) ) {
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
@@ -94,6 +98,8 @@ abstract class TableMigrator {
|
||||
*
|
||||
* @param array $entity_ids Order ids to migrate.
|
||||
* @return array An array containing the keys 'errors' (array of strings) and 'exception' (exception object or null).
|
||||
*
|
||||
* @deprecated 8.0.0 Use `fetch_sanitized_migration_data` and `process_migration_data` instead.
|
||||
*/
|
||||
public function process_migration_batch_for_ids( array $entity_ids ): array {
|
||||
$this->clear_errors();
|
||||
@@ -111,12 +117,38 @@ abstract class TableMigrator {
|
||||
);
|
||||
}
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.InvalidNoReturn, Squiz.Commenting.FunctionCommentThrowTag.Missing -- Methods are not marked abstract for back compat.
|
||||
/**
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
public function fetch_sanitized_migration_data( array $entity_ids ) {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* The core method that actually performs the migration for the supplied batch of order ids.
|
||||
* It doesn't need to deal with database errors nor with exceptions.
|
||||
*
|
||||
* @param array $entity_ids Order ids to migrate.
|
||||
* @return void
|
||||
*
|
||||
* @deprecated 8.0.0 Use `fetch_sanitized_migration_data` and `process_migration_data` instead.
|
||||
*/
|
||||
abstract protected function process_migration_batch_for_ids_core( array $entity_ids ): void;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user