refactor(2meet-data-optimizer): list post groups for backfill from the registry
- Admin "backfill all" and `wp tmdo post-migrate-group` take any registered post group via TMDO_Post_Migration::backfillable_groups() instead of a hard-coded list of 7 (dev36 registers 11; the 4 HP add-on groups were never backfilled). Restored orphan groups are excluded and rejected — their values live only in the flat table. - Groups with json fields (attachment, nav_menu_item) go row by row through the migration engine instead of leaving the json columns NULL in the bulk pivot. - Verify: PostBackfillJsonTest (3 RED on hardening/2.2); dev36 post-migrate-group attachment / nav_menu_item / hp_draft_flags: json columns semantically equal native (24/24, 21/21), checker unchanged. - PR: PR-067
This commit is contained in:
@@ -556,11 +556,11 @@ class TMDO_Admin {
|
||||
exit;
|
||||
}
|
||||
|
||||
// v2.10.0: Post Migration Wizard — backfill all 7 groups.
|
||||
// v2.10.0: Post Migration Wizard — backfill every registered post group.
|
||||
if ( isset( $_POST['wpdo_post_backfill_all'] ) && check_admin_referer( 'wpdo_post_backfill_all' ) && class_exists( 'TMDO_Post_Migration' ) ) {
|
||||
$total_migrated = 0;
|
||||
$errors = array();
|
||||
foreach ( array( 'wp_core', 'attachment', 'wc_product', 'hp_listing_core', 'hp_request_core', 'hp_vendor_core', 'nav_menu_item' ) as $group ) {
|
||||
foreach ( TMDO_Post_Migration::backfillable_groups() as $group ) {
|
||||
try {
|
||||
$result = TMDO_Post_Migration::backfill_group( $group );
|
||||
$total_migrated += (int) $result['migrated'];
|
||||
|
||||
@@ -198,25 +198,16 @@ class TMDO_CLI_Post {
|
||||
|
||||
/**
|
||||
* Backfill one post entity group from wp_postmeta into its flat table
|
||||
* via a single bulk SQL pivot (ON DUPLICATE KEY UPDATE).
|
||||
* via a single bulk SQL pivot (ON DUPLICATE KEY UPDATE). Groups with
|
||||
* JSON-typed fields are backfilled row by row instead.
|
||||
*
|
||||
* Idempotent — safe to re-run. Skips JSON-typed fields (handled by the
|
||||
* row-by-row backfill phase in v2.9.4+).
|
||||
* Idempotent — safe to re-run.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* --group=<group>
|
||||
* : Entity group name. Required.
|
||||
* ---
|
||||
* options:
|
||||
* - wp_core
|
||||
* - attachment
|
||||
* - wc_product
|
||||
* - hp_listing_core
|
||||
* - hp_request_core
|
||||
* - hp_vendor_core
|
||||
* - nav_menu_item
|
||||
* ---
|
||||
* : Registered post entity group name (e.g. wp_core, attachment,
|
||||
* hp_listing_core). Required.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
|
||||
@@ -99,11 +99,12 @@ final class TMDO_Post_Migration {
|
||||
/**
|
||||
* Bulk SQL pivot for one group: select managed keys from wp_postmeta,
|
||||
* filter by post_type, pivot via MAX(CASE) GROUP BY post_id, UPSERT
|
||||
* into the group's flat table.
|
||||
* into the group's flat table. Groups with json fields go row by row
|
||||
* through backfill_group_json() instead.
|
||||
*
|
||||
* @param string $group_name Entity group name (e.g. 'wc_product').
|
||||
* @return array{migrated:int,group:string,post_type:string|null}
|
||||
* @throws InvalidArgumentException When group is not registered.
|
||||
* @throws InvalidArgumentException When group is not registered, or is a restored orphan group.
|
||||
* @throws RuntimeException When the pivot SQL fails.
|
||||
*/
|
||||
public static function backfill_group( string $group_name ): array {
|
||||
@@ -115,8 +116,30 @@ final class TMDO_Post_Migration {
|
||||
'Unknown post entity group: ' . esc_html( $group_name )
|
||||
);
|
||||
}
|
||||
if ( ! in_array( $group_name, self::backfillable_groups(), true ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
'Orphan post group, its values live only in the flat table: ' . esc_html( $group_name )
|
||||
);
|
||||
}
|
||||
|
||||
$post_type = self::group_post_type( $group_name );
|
||||
|
||||
// json values need PHP-level unserialize → json encode, which the bulk
|
||||
// pivot below cannot do: backfill such groups row by row instead.
|
||||
if ( in_array( 'json', array_column( $fields, 'type' ), true ) ) {
|
||||
$result = self::backfill_group_json( $group_name, array( 'resume' => false ) );
|
||||
if ( ! empty( $result['error'] ) || ! empty( $result['errors'] ) ) {
|
||||
throw new RuntimeException(
|
||||
'backfill_group failed for ' . esc_html( $group_name ) . ': ' . esc_html( (string) ( $result['error'] ?? $result['errors'] . ' row error(s)' ) )
|
||||
);
|
||||
}
|
||||
return array(
|
||||
'migrated' => (int) $result['migrated'],
|
||||
'group' => $group_name,
|
||||
'post_type' => $post_type,
|
||||
);
|
||||
}
|
||||
|
||||
$post_type = self::group_post_type( $group_name );
|
||||
$flat_table = TMDO_Schema_Manager::get_table_name( self::ENTITY_TYPE, $group_name );
|
||||
|
||||
// Introspect target table columns so we only pivot fields that actually
|
||||
@@ -131,16 +154,10 @@ final class TMDO_Post_Migration {
|
||||
}
|
||||
|
||||
// Build column list and CASE expressions.
|
||||
// Skip json/textarea types from the bulk pivot — those need row-by-row
|
||||
// unserialize handling (deferred to v2.9.4 backfill_unserialize phase).
|
||||
$columns = array();
|
||||
$cases = array();
|
||||
$update_parts = array();
|
||||
foreach ( $fields as $field ) {
|
||||
$type = $field['type'] ?? 'text';
|
||||
if ( 'json' === $type ) {
|
||||
continue;
|
||||
}
|
||||
$col = TMDO_Schema_Manager::sanitize_column_name( $field['key'] );
|
||||
if ( ! isset( $existing_cols[ $col ] ) ) {
|
||||
continue; // Column not present in this table — skip silently.
|
||||
@@ -536,6 +553,23 @@ final class TMDO_Post_Migration {
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Registered post groups that can be backfilled from wp_postmeta. Orphan
|
||||
* groups restored from wpdo_registry_meta are left out: their values live
|
||||
* only in the flat table.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function backfillable_groups(): array {
|
||||
$orphans = array();
|
||||
foreach ( TMDO_Entity_Registry::get_restored_groups() as $restored ) {
|
||||
if ( self::ENTITY_TYPE === $restored['entity_type'] ) {
|
||||
$orphans[] = $restored['group'];
|
||||
}
|
||||
}
|
||||
return array_values( array_diff( TMDO_Entity_Registry::get_groups_for_type( self::ENTITY_TYPE ), $orphans ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* All meta_keys registered for any post entity group.
|
||||
*
|
||||
|
||||
@@ -215,6 +215,38 @@ class PostBackfillJsonTest extends TestCase {
|
||||
$this->assertSame( 0, $result['errors'] ?? -1 );
|
||||
}
|
||||
|
||||
// ── backfill_group() / backfillable_groups() (2.2 PR-067) ───────────────
|
||||
|
||||
public function test_backfill_group_fills_json_columns_instead_of_skipping_them(): void {
|
||||
$this->seed_attachment( 1, array( 'width' => 640 ), 'Alt text' );
|
||||
|
||||
$result = WPDO_Post_Migration::backfill_group( 'attachment' );
|
||||
|
||||
$this->assertSame( 'attachment', $result['group'] );
|
||||
global $wpdb;
|
||||
$row = $wpdb->get_row( 'SELECT _wp_attachment_metadata, _wp_attachment_image_alt FROM `' . self::FLAT . '` WHERE post_id = 1', ARRAY_A );
|
||||
$this->assertSame( array( 'width' => 640 ), json_decode( (string) $row['_wp_attachment_metadata'], true ), 'json field must not be left NULL by the bulk pivot' );
|
||||
$this->assertSame( 'Alt text', $row['_wp_attachment_image_alt'] );
|
||||
}
|
||||
|
||||
public function test_backfillable_groups_are_the_registered_post_groups(): void {
|
||||
WPDO_Entity_Registry::register_group( 'post', 'itest_extra', array( array( 'key' => 'itest_extra_key', 'type' => 'text' ) ) );
|
||||
|
||||
$groups = WPDO_Post_Migration::backfillable_groups();
|
||||
|
||||
$this->assertSame( WPDO_Entity_Registry::get_groups_for_type( 'post' ), $groups );
|
||||
$this->assertContains( 'itest_extra', $groups, 'a group registered by an add-on must be offered for backfill' );
|
||||
}
|
||||
|
||||
public function test_restored_orphan_groups_are_never_backfilled(): void {
|
||||
WPDO_Entity_Registry::restore_group( 'post', 'itest_orphan', array( array( 'key' => 'itest_orphan_key', 'type' => 'text' ) ) );
|
||||
$this->assertContains( 'itest_orphan', WPDO_Entity_Registry::get_groups_for_type( 'post' ) );
|
||||
|
||||
$this->assertNotContains( 'itest_orphan', WPDO_Post_Migration::backfillable_groups(), 'orphan data lives only in the flat table' );
|
||||
$this->expectException( InvalidArgumentException::class );
|
||||
WPDO_Post_Migration::backfill_group( 'itest_orphan' );
|
||||
}
|
||||
|
||||
public function test_returns_error_for_unknown_group(): void {
|
||||
$result = WPDO_Post_Migration::backfill_group_json( 'bogus_group' );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user