Applied coding standards.

This commit is contained in:
vedanshujain 2022-03-31 19:44:29 +05:30
parent 02376e6ec5
commit d5b164622f
1 changed files with 143 additions and 70 deletions

View File

@ -35,34 +35,72 @@ abstract class MetaToCustomTableMigrator {
*/ */
protected $core_column_mapping; protected $core_column_mapping;
/**
* Store errors along with entity IDs from migrations.
*
* @var array
*/
protected $errors; protected $errors;
/** /**
* MetaToCustomTableMigrator constructor. * MetaToCustomTableMigrator constructor.
*/
public function __construct() {
$this->schema_config = MigrationHelper::escape_schema_for_backtick( $this->get_schema_config() );
$this->meta_column_mapping = $this->get_meta_column_config();
$this->core_column_mapping = $this->get_core_column_mapping();
$this->errors = array();
}
/**
* Specify schema config the source and destination table.
* *
* @param array $schema_config This parameters provides general but essential information about tables under migrations. Must be of the form- * @return array Schema, must of the form:
* array( * array(
* 'entity_schema' => 'source' => array(
* array ( 'entity' => array(
* 'primary_id' => 'primary_id column name of source table', 'table_name' => $source_table_name,
* 'table_name' => 'name of the source table'. 'meta_rel_column' => $column_meta, Name of column in source table which is referenced by meta table.
* ), 'destination_rel_column' => $column_dest, Name of column in source table which is refenced by destination table,
* 'entity_meta_schema' => 'primary_key' => $primary_key, Primary key of the source table
* array ( ),
* 'meta_key_column' => 'name of meta_key column in source meta table', 'meta' => array(
* 'meta_value_column' => 'name of meta_value column in source meta table', 'table' => $meta_table_name,
* 'table_name' => 'name of source meta table', 'meta_key_column' => $meta_key_column_name,
* ), 'meta_value_column' => $meta_value_column_name,
* 'destination_table' => 'name of destination custom table', 'entity_id_column' => $entity_id_column, Name of the column having entity IDs.
* 'entity_meta_relation' => ),
* array ( ),
* 'entity' => 'name of column in source table which is used in source meta table', 'destination' => array(
* 'meta' => 'name of column in source meta table which contains key of records in source table', 'table_name' => $table_name, Name of destination table,
* ) 'source_rel_column' => $column_source_id, Name of the column in destination table which is referenced by source table.
* ) 'primary_key' => $table_primary_key,
* ). 'primary_key_type' => $type bool|int|string|decimal
)
*/
abstract public function get_schema_config();
/**
* Specify column config from the source table.
* *
* @param array $meta_column_mapping Mapping information of keys in source meta table. Must be of the form: * @return array Config, must be of the form:
* array(
* '$source_column_name_1' => array( // $source_column_name_1 is column name in source table, or a select statement.
* 'type' => 'type of value, could be string/int/date/float.',
* 'destination' => 'name of the column in column name where this data should be inserted in.',
* ),
* '$source_column_name_2' => array(
* ......
* ),
* ....
* ).
*/
abstract public function get_core_column_mapping();
/**
* Specify meta keys config from source meta table.
*
* @return array Config, must be of the form.
* array( * array(
* '$meta_key_1' => array( // $meta_key_1 is the name of meta_key in source meta table. * '$meta_key_1' => array( // $meta_key_1 is the name of meta_key in source meta table.
* 'type' => 'type of value, could be string/int/date/float', * 'type' => 'type of value, could be string/int/date/float',
@ -73,42 +111,16 @@ abstract class MetaToCustomTableMigrator {
* ), * ),
* .... * ....
* ). * ).
*
* @param array $core_column_mapping Mapping of keys in source table, similar to meta_column_mapping param, must be of the form:
* array(
* '$source_column_name_1' => array( // $source_column_name_1 is column name in source table.
* 'type' => 'type of value, could be string/int/date/float.',
* 'destination' => 'name of the column in column name where this data should be inserted in.',
* ),
* '$source_column_name_2' => array(
* ......
* ),
* ....
* ).
*/ */
public function __construct() { abstract public function get_meta_column_config();
// TODO: Add code to validate params.
$this->schema_config = MigrationHelper::escape_schema_for_backtick( $this->get_schema_config() );
$this->meta_column_mapping = $this->get_meta_column_config();
$this->core_column_mapping = $this->get_core_column_mapping();
$this->errors = array();
}
abstract function get_schema_config();
abstract function get_core_column_mapping();
abstract function get_meta_column_config();
/** /**
* Generate SQL for data insertion. * Generate SQL for data insertion.
* *
* @param array $batch Data to generate queries for. Will be 'data' array returned by `$this->fetch_data_for_migration()` method. * @param array $batch Data to generate queries for. Will be 'data' array returned by `$this->fetch_data_for_migration_for_ids()` method.
* @param string $insert_switch Insert command to use in generating queries, could be insert, insert_ignore, or replace.
* *
* @return string Generated queries for insertion for this batch, would be of the form: * @return string Generated queries for insertion for this batch, would be of the form:
* INSERT/INSERT IGNORE/REPLACE INTO $table_name ($columns) values * INSERT IGNORE INTO $table_name ($columns) values
* ($value for row 1) * ($value for row 1)
* ($value for row 2) * ($value for row 2)
* ... * ...
@ -118,10 +130,26 @@ abstract class MetaToCustomTableMigrator {
list( $value_sql, $column_sql ) = $this->generate_column_clauses( array_merge( $this->core_column_mapping, $this->meta_column_mapping ), $batch ); list( $value_sql, $column_sql ) = $this->generate_column_clauses( array_merge( $this->core_column_mapping, $this->meta_column_mapping ), $batch );
return "INSERT IGNORE INTO $table (`$column_sql`) VALUES $value_sql;"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, -- $insert_query is hardcoded, $value_sql is already escaped. return "INSERT IGNORE INTO $table (`$column_sql`) VALUES $value_sql;"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, -- $insert_query is hardcoded, $value_sql is already escaped.
} }
/**
* Generate SQL for data updating.
*
* @param array $batch Data to generate queries for. Will be `data` array returned by fetch_data_for_migration_for_ids() method.
*
* @param array $entity_row_mapping Maps rows to update data with their original IDs. Will be returned by `generate_update_sql_for_batch`.
*
* @return string Generated queries for batch update. Would be of the form:
* INSERT INTO $table ( $columns ) VALUES
* ($value for row 1)
* ($valye for row 2)
* ...
* ON DUPLICATE KEY UPDATE
* $column1 = VALUES($column1)
* $column2 = VALUES($column2)
* ...
*/
public function generate_update_sql_for_batch( $batch, $entity_row_mapping ) { public function generate_update_sql_for_batch( $batch, $entity_row_mapping ) {
$table = $this->schema_config['destination']['table_name']; $table = $this->schema_config['destination']['table_name'];
@ -140,6 +168,11 @@ abstract class MetaToCustomTableMigrator {
return "INSERT INTO $table (`$column_sql`) VALUES $value_sql $duplicate_update_key_statement;"; return "INSERT INTO $table (`$column_sql`) VALUES $value_sql $duplicate_update_key_statement;";
} }
/**
* Generate schema for primary ID column of destination table.
*
* @return array[] Schema for primary ID column.
*/
protected function get_destination_table_primary_id_schema() { protected function get_destination_table_primary_id_schema() {
return array( return array(
'destination_primary_key' => array( 'destination_primary_key' => array(
@ -149,6 +182,14 @@ abstract class MetaToCustomTableMigrator {
); );
} }
/**
* Generate values and columns clauses to be used in INSERT and INSERT..ON DUPLICATE KEY UPDATE statements.
*
* @param array $columns_schema Columns config for destination table.
* @param array $batch Actual data to migrate as returned by `data` in `fetch_data_for_migration_for_ids` method.
*
* @return array SQL clause for values, columns placeholders, and columns.
*/
protected function generate_column_clauses( $columns_schema, $batch ) { protected function generate_column_clauses( $columns_schema, $batch ) {
global $wpdb; global $wpdb;
@ -178,8 +219,15 @@ abstract class MetaToCustomTableMigrator {
return array( $value_sql, $column_sql, $columns ); return array( $value_sql, $column_sql, $columns );
} }
/**
* Generates ON DUPLICATE KEY UPDATE clause to be used in migration.
*
* @param array $columns List of column names.
*
* @return string SQL clause for INSERT...ON DUPLICATE KEY UPDATE
*/
private function generate_on_duplicate_statement_clause( $columns ) { private function generate_on_duplicate_statement_clause( $columns ) {
$update_value_statements = []; $update_value_statements = array();
foreach ( $columns as $column ) { foreach ( $columns as $column ) {
$update_value_statements[] = "$column = VALUES( $column )"; $update_value_statements[] = "$column = VALUES( $column )";
} }
@ -191,15 +239,15 @@ abstract class MetaToCustomTableMigrator {
/** /**
* Process next migration batch, uses option `wc_cot_migration` to checkpoints of what have been processed so far. * Process next migration batch, uses option `wc_cot_migration` to checkpoints of what have been processed so far.
* *
* @param int $batch_size Batch size of records to migrate. * @param array $entity_ids List of entity IDs to perform migrations for.
* *
* @return array True if migration is completed, false if there are still records to process. * @return array List of errors happened during migration.
*/ */
public function process_migration_batch_for_ids( $entity_ids ) { public function process_migration_batch_for_ids( $entity_ids ) {
$data = $this->fetch_data_for_migration_for_ids( $entity_ids ); $data = $this->fetch_data_for_migration_for_ids( $entity_ids );
foreach ( $data['errors'] as $entity_id => $error ) { foreach ( $data['errors'] as $entity_id => $error ) {
$this->errors[ $entity_id ] = "Error in importing post id $entity_id: " . print_r( $error, true ); $this->errors[ $entity_id ] = "Error in importing post id $entity_id: " . $error->get_message();
} }
if ( count( $data['data'] ) === 0 ) { if ( count( $data['data'] ) === 0 ) {
@ -216,32 +264,45 @@ abstract class MetaToCustomTableMigrator {
$this->process_update_batch( $to_update, $already_migrated ); $this->process_update_batch( $to_update, $already_migrated );
return array( return array(
'errors' => $this->errors 'errors' => $this->errors,
); );
} }
/**
* Process batch for insertion into destination table.
*
* @param array $batch Data to insert, will be of the form as returned by `data` in `fetch_data_for_migration_for_ids`.
*/
protected function process_insert_batch( $batch ) { protected function process_insert_batch( $batch ) {
global $wpdb; global $wpdb;
if ( 0 === count( $batch ) ) { if ( 0 === count( $batch ) ) {
return; return;
} }
$queries = $this->generate_insert_sql_for_batch( $batch ); $queries = $this->generate_insert_sql_for_batch( $batch );
$result = $wpdb->query( $queries ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Queries should already be prepared.
$wpdb->query( "COMMIT;" ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration? $result = $wpdb->query( $queries );
$wpdb->query( 'COMMIT;' ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration?
if ( count( $batch ) !== $result ) { if ( count( $batch ) !== $result ) {
// Some rows were not inserted. // Some rows were not inserted.
// TODO: Find and log the entity ids that were not inserted. // TODO: Find and log the entity ids that were not inserted.
} }
} }
/**
* Process batch for update into destination table.
*
* @param array $batch Data to insert, will be of the form as returned by `data` in `fetch_data_for_migration_for_ids`.
* @param array $already_migrated Maps rows to update data with their original IDs.
*/
protected function process_update_batch( $batch, $already_migrated ) { protected function process_update_batch( $batch, $already_migrated ) {
global $wpdb; global $wpdb;
if ( 0 === count( $batch ) ) { if ( 0 === count( $batch ) ) {
return; return;
} }
$queries = $this->generate_update_sql_for_batch( $batch, $already_migrated ); $queries = $this->generate_update_sql_for_batch( $batch, $already_migrated );
$result = $wpdb->query( $queries ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Queries should already be prepared.
$wpdb->query( "COMMIT;" ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration? $result = $wpdb->query( $queries );
$wpdb->query( 'COMMIT;' ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration?
if ( count( $batch ) !== $result ) { if ( count( $batch ) !== $result ) {
// Some rows were not inserted. // Some rows were not inserted.
// TODO: Find and log the entity ids that were not updateed. // TODO: Find and log the entity ids that were not updateed.
@ -252,9 +313,7 @@ abstract class MetaToCustomTableMigrator {
/** /**
* Fetch data for migration. * Fetch data for migration.
* *
* @param string $where_clause Where conditions to use while selecting data from source table. * @param array $entity_ids Entity IDs to fetch data for.
* @param string $batch_size Batch size, will be used in LIMIT clause.
* @param string $order_by Will be used in ORDER BY clause.
* *
* @return array[] Data along with errors (if any), will of the form: * @return array[] Data along with errors (if any), will of the form:
* array( * array(
@ -295,6 +354,20 @@ abstract class MetaToCustomTableMigrator {
return $this->process_and_sanitize_data( $entity_data, $meta_data ); return $this->process_and_sanitize_data( $entity_data, $meta_data );
} }
/**
* Fetch id mappings for records that are already inserted, or can be considered duplicates.
*
* @param array $entity_ids List of entity IDs to verify.
*
* @return array Already migrated entities, would be of the form
* array(
* '$source_id1' => array(
* 'source_id' => $source_id1,
* 'destination_id' => $destination_id1,
* ),
* ...
* )
*/
public function get_already_migrated_records( $entity_ids ) { public function get_already_migrated_records( $entity_ids ) {
global $wpdb; global $wpdb;
$source_table = $this->schema_config['source']['entity']['table_name']; $source_table = $this->schema_config['source']['entity']['table_name'];
@ -309,6 +382,7 @@ abstract class MetaToCustomTableMigrator {
$already_migrated_entity_ids = $wpdb->get_results( $already_migrated_entity_ids = $wpdb->get_results(
$wpdb->prepare( $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 SELECT source.`$source_primary_key_column` as source_id, destination.`$destination_primary_key_column` as destination_id
FROM `$destination_table` destination FROM `$destination_table` destination
@ -317,6 +391,7 @@ WHERE source.`$source_primary_key_column` IN ( $entity_id_placeholder )
", ",
$entity_ids $entity_ids
) )
// phpcs:enable
); );
return array_column( $already_migrated_entity_ids, null, 'source_id' ); return array_column( $already_migrated_entity_ids, null, 'source_id' );
@ -326,9 +401,7 @@ WHERE source.`$source_primary_key_column` IN ( $entity_id_placeholder )
/** /**
* Helper method to build query used to fetch data from core source table. * Helper method to build query used to fetch data from core source table.
* *
* @param string $where_clause Where conditions to use while selecting data from source table. * @param array $entity_ids List of entity IDs to fetch.
* @param string $batch_size Batch size, will be used in LIMIT clause.
* @param string $order_by Will be used in ORDER BY clause.
* *
* @return string Query that can be used to fetch data. * @return string Query that can be used to fetch data.
*/ */
@ -349,7 +422,7 @@ WHERE source.`$source_primary_key_column` IN ( $entity_id_placeholder )
} }
} }
$entity_column_string = implode( ', ', $entity_keys ); $entity_column_string = implode( ', ', $entity_keys );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $source_meta_rel_id_column, $source_destination_rel_id_column etc is escaped for backticks. $where clause and $order_by should already be escaped. // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $source_meta_rel_id_column, $source_destination_rel_id_column etc is escaped for backticks. $where clause and $order_by should already be escaped.
$query = $wpdb->prepare( $query = $wpdb->prepare(
" "
SELECT SELECT
@ -371,7 +444,7 @@ WHERE $where_clause;
* *
* @param array $entity_ids List of IDs to fetch metadata for. * @param array $entity_ids List of IDs to fetch metadata for.
* *
* @return string|void Query for fetching meta data. * @return string Query for fetching meta data.
*/ */
protected function build_meta_data_query( $entity_ids ) { protected function build_meta_data_query( $entity_ids ) {
global $wpdb; global $wpdb;
@ -384,7 +457,7 @@ WHERE $where_clause;
$meta_column_string = implode( ', ', array_fill( 0, count( $meta_keys ), '%s' ) ); $meta_column_string = implode( ', ', array_fill( 0, count( $meta_keys ), '%s' ) );
$entity_id_string = implode( ', ', array_fill( 0, count( $entity_ids ), '%d' ) ); $entity_id_string = implode( ', ', array_fill( 0, count( $entity_ids ), '%d' ) );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $meta_table_relational_key, $meta_key_column, $meta_value_column and $meta_table is escaped for backticks. $entity_id_string and $meta_column_string are placeholders. // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $meta_table_relational_key, $meta_key_column, $meta_value_column and $meta_table is escaped for backticks. $entity_id_string and $meta_column_string are placeholders.
$query = $wpdb->prepare( $query = $wpdb->prepare(
" "
SELECT `$meta_table_relational_key` as entity_id, `$meta_key_column` as meta_key, `$meta_value_column` as meta_value SELECT `$meta_table_relational_key` as entity_id, `$meta_key_column` as meta_key, `$meta_value_column` as meta_value
@ -475,7 +548,7 @@ WHERE
/** /**
* Validate and transform data so that we catch as many errors as possible before inserting. * Validate and transform data so that we catch as many errors as possible before inserting.
* *
* @param mixed $value Actual data value. * @param mixed $value Actual data value.
* @param string $type Type of data, could be decimal, int, date, string. * @param string $type Type of data, could be decimal, int, date, string.
* *
* @return float|int|mixed|string|\WP_Error * @return float|int|mixed|string|\WP_Error