fix(2meet-data-optimizer): close the gaps in deferred table upgrades found in the 2.3 review

- a deferred group whose stored definition matches again (reverted, or
  upgraded by another process) goes through end_deferral() instead of staying
  native for good (review F2)
- the tmdo_ensure_group_indexes handler upgrades deferred tables itself: with
  ALTERNATE_WP_CRON the event runs after init in a visitor's request (F3)
- a new group table created while the entity writes to flat stays native
  until the drift fix has filled it from native meta; an opted-in HivePress
  attribute no longer routes searches to an empty table (F6)
- direct_read() returns native values for a deferred group (F10); the Hook
  Bus ignores deferral in aeav_only, as the router does
- dbDelta failures: the definition is recorded only when every column exists
  (schema.upgrade_failed otherwise), so a failed ALTER is retried and the
  group is not released onto missing columns
- test bootstrap: get_metadata() reads the real meta table
- Verify: SchemaUpgradeDeferralTest (6), Hook Bus direct read, integration
  587, unit 703, phpcs, stan
- PR: PR-114 (2.3 acceptance correctness review)
This commit is contained in:
2026-09-27 17:30:36 +08:00
parent e26aadc4ec
commit 1d70d13f45
5 changed files with 125 additions and 11 deletions
+21 -5
View File
@@ -951,8 +951,20 @@ final class TMDO_Hook_Bus {
if ( null !== $field_def && 'post' === $type ) {
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key, (string) get_post_type( $object_id ) );
}
// A group whose table upgrade waits for WP-CLI / WP-Cron stays native.
return null !== $field_def && TMDO_Schema_Manager::is_deferred( $type, (string) $field_def['group'] ) ? null : $field_def;
// A group whose table upgrade waits for WP-CLI / WP-Cron stays native
// (not in aeav_only, where native is stale; the router agrees).
return null !== $field_def && self::deferred( $type, (string) $field_def['group'] ) ? null : $field_def;
}
/**
* Whether a group is deferred and so read and written natively.
*
* @param string $type Entity type.
* @param string $group Group name.
* @return bool
*/
private static function deferred( string $type, string $group ): bool {
return TMDO_Schema_Manager::is_deferred( $type, $group ) && TMDO_Mode_Manager::MODE_AEAV_ONLY !== TMDO_Mode_Manager::get( $type );
}
/**
@@ -970,10 +982,13 @@ final class TMDO_Hook_Bus {
// 回傳所有群組的所有欄位
$result = array();
foreach ( TMDO_Entity_Registry::get_groups_for_type( $type ) as $group ) {
$row = self::get_or_load_row( $type, $entity_id, $group );
$deferred = self::deferred( $type, $group );
$row = $deferred ? array() : self::get_or_load_row( $type, $entity_id, $group );
foreach ( TMDO_Entity_Registry::get_group_fields( $type, $group ) as $field ) {
$col = TMDO_Schema_Manager::sanitize_column_name( $field['key'] );
$result[ $field['key'] ] = TMDO_Type_Caster::from_db( $row[ $col ] ?? null, $field );
$result[ $field['key'] ] = $deferred
? get_metadata( $type, $entity_id, $field['key'], true )
: TMDO_Type_Caster::from_db( $row[ $col ] ?? null, $field );
}
}
return $result;
@@ -981,7 +996,8 @@ final class TMDO_Hook_Bus {
$field_def = self::field_for( $type, $entity_id, $key );
if ( ! $field_def ) {
return null;
// A deferred group's value is the native one.
return null !== TMDO_Entity_Registry::get_field( $type, $key ) ? get_metadata( $type, $entity_id, $key, true ) : null;
}
$row = self::get_or_load_row( $type, $entity_id, $field_def['group'] );