Applied coding standards.
This commit is contained in:
parent
02376e6ec5
commit
d5b164622f
|
@ -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 );
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Queries should already be prepared.
|
||||||
$result = $wpdb->query( $queries );
|
$result = $wpdb->query( $queries );
|
||||||
$wpdb->query( "COMMIT;" ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration?
|
$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 );
|
||||||
|
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Queries should already be prepared.
|
||||||
$result = $wpdb->query( $queries );
|
$result = $wpdb->query( $queries );
|
||||||
$wpdb->query( "COMMIT;" ); // For some reason, this seems necessary on some hosts? Maybe a MySQL configuration?
|
$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
|
||||||
|
|
Loading…
Reference in New Issue