feat(2meet-data-optimizer): guard orphaned flat-table groups
- On init:1, re-register aeav_only groups whose flat table exists and whose definition is in wpdo_registry_meta but no active plugin registers them, so their data stays readable (routing only, never DDL) - Keys owned by a live group, or with rows in native meta (newer data written while orphaned), are left alone; the native-meta check is cached for a day - Surface restored groups via Site Health (wpdo_orphaned_entity_groups), the admin Doctor tab, and wp wpdo doctor - Verify: EntityRegistryRestoreTest (9), OrphanGroupRestoreTest (7); core unit 622 / integration 439, HP 141/49, WC 10/22 OK; dry-run on dev20–22 restores 16/17/4 groups, keeps hp_taxonomy hp_sort_order/hp_icon on native - PR: PR-018
This commit is contained in:
@@ -34,6 +34,9 @@ final class TMDO_Entity_Registry {
|
||||
/** @var array<string, array> "entity/group" → 同名群組以不同定義重複註冊的紀錄 */
|
||||
private static array $collisions = array();
|
||||
|
||||
/** @var array<string, array> "entity/group" → 從 wpdo_registry_meta 還原的孤兒群組 */
|
||||
private static array $restored = array();
|
||||
|
||||
/** 合法的欄位型別 */
|
||||
public const VALID_TYPES = array(
|
||||
'text',
|
||||
@@ -58,6 +61,7 @@ final class TMDO_Entity_Registry {
|
||||
self::$field_index = array();
|
||||
self::$pending_schemas = array();
|
||||
self::$collisions = array();
|
||||
self::$restored = array();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────
|
||||
@@ -109,7 +113,41 @@ final class TMDO_Entity_Registry {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 驗證並正規化欄位定義
|
||||
$normalized = self::normalize_fields( $entity_type, $group_name, $fields );
|
||||
|
||||
if ( empty( $normalized ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 防重複登錄:相同定義(init:1 重新觸發)靜默略過;定義不同則記錄衝突,保留先註冊者。
|
||||
if ( isset( self::$groups[ $entity_type ][ $group_name ] ) ) {
|
||||
if ( self::$groups[ $entity_type ][ $group_name ] !== $normalized ) {
|
||||
self::record_collision( $entity_type, $group_name, self::$groups[ $entity_type ][ $group_name ], $normalized );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 建立索引以供快速查詢
|
||||
foreach ( $normalized as $normalized_field ) {
|
||||
self::$field_index[ $entity_type ][ $normalized_field['key'] ] = $normalized_field;
|
||||
}
|
||||
|
||||
self::$groups[ $entity_type ][ $group_name ] = $normalized;
|
||||
|
||||
// 加入待建表佇列
|
||||
self::$pending_schemas[] = array(
|
||||
'type' => $entity_type,
|
||||
'group' => $group_name,
|
||||
'fields' => $normalized,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證並正規化欄位定義(略過缺 key/type 或型別不合法者)。
|
||||
*/
|
||||
private static function normalize_fields( string $entity_type, string $group_name, array $fields ): array {
|
||||
$normalized = array();
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
@@ -142,35 +180,79 @@ final class TMDO_Entity_Registry {
|
||||
$normalized[] = $normalized_field;
|
||||
}
|
||||
|
||||
if ( empty( $normalized ) ) {
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 還原孤兒群組:依 wpdo_registry_meta 保存的定義重新登錄,只供讀寫路由用。
|
||||
*
|
||||
* 與 register_group() 的差異:不進 pending_schemas(絕不觸發 DDL),且已被
|
||||
* 其他存活群組管理的 key 一律略過,不覆蓋。$native_keys 列出原生 meta 仍有
|
||||
* 資料的 key:那些值比扁平表新(孤兒期間寫入),必須留在原生 meta。
|
||||
*
|
||||
* @param string[] $native_keys 原生 meta 仍有資料、不可還原的 key。
|
||||
* @return bool 有登錄任何欄位時為 true
|
||||
*/
|
||||
public static function restore_group( string $entity_type, string $group_name, array $fields, array $native_keys = array() ): bool {
|
||||
if ( ! isset( self::$adapters[ $entity_type ] ) || isset( self::$groups[ $entity_type ][ $group_name ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 防重複登錄:相同定義(init:1 重新觸發)靜默略過;定義不同則記錄衝突,保留先註冊者。
|
||||
if ( isset( self::$groups[ $entity_type ][ $group_name ] ) ) {
|
||||
if ( self::$groups[ $entity_type ][ $group_name ] !== $normalized ) {
|
||||
self::record_collision( $entity_type, $group_name, self::$groups[ $entity_type ][ $group_name ], $normalized );
|
||||
$kept = array();
|
||||
$skipped = array();
|
||||
$native = array();
|
||||
foreach ( self::normalize_fields( $entity_type, $group_name, $fields ) as $field ) {
|
||||
if ( isset( self::$field_index[ $entity_type ][ $field['key'] ] ) ) {
|
||||
$skipped[] = (string) $field['key'];
|
||||
continue;
|
||||
}
|
||||
if ( in_array( $field['key'], $native_keys, true ) ) {
|
||||
$native[] = (string) $field['key'];
|
||||
continue;
|
||||
}
|
||||
$kept[] = $field;
|
||||
}
|
||||
|
||||
$id = $entity_type . '/' . $group_name;
|
||||
if ( empty( $kept ) ) {
|
||||
// 整組都留在原生 meta 時仍要回報:扁平表的舊資料需要人工處理。
|
||||
if ( $native ) {
|
||||
self::$restored[ $id ] = array(
|
||||
'entity_type' => $entity_type,
|
||||
'group' => $group_name,
|
||||
'fields' => 0,
|
||||
'skipped' => $skipped,
|
||||
'native' => $native,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 建立索引以供快速查詢
|
||||
foreach ( $normalized as $normalized_field ) {
|
||||
self::$field_index[ $entity_type ][ $normalized_field['key'] ] = $normalized_field;
|
||||
foreach ( $kept as $field ) {
|
||||
self::$field_index[ $entity_type ][ $field['key'] ] = $field;
|
||||
}
|
||||
self::$groups[ $entity_type ][ $group_name ] = $kept;
|
||||
|
||||
self::$groups[ $entity_type ][ $group_name ] = $normalized;
|
||||
|
||||
// 加入待建表佇列
|
||||
self::$pending_schemas[] = array(
|
||||
'type' => $entity_type,
|
||||
'group' => $group_name,
|
||||
'fields' => $normalized,
|
||||
self::$restored[ $id ] = array(
|
||||
'entity_type' => $entity_type,
|
||||
'group' => $group_name,
|
||||
'fields' => count( $kept ),
|
||||
'skipped' => $skipped,
|
||||
'native' => $native,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本請求從 wpdo_registry_meta 還原的孤兒群組。
|
||||
*
|
||||
* @return array<int, array{entity_type:string, group:string, fields:int, skipped:string[], native:string[]}>
|
||||
*/
|
||||
public static function get_restored_groups(): array {
|
||||
return array_values( self::$restored );
|
||||
}
|
||||
|
||||
/**
|
||||
* 記錄同名群組衝突(每個 entity/group 每請求一筆),並寫入 warning。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user