fix(2meet-data-optimizer): route post entity keys by post_type
- Entity groups can declare post_types; get_field() resolves a post's key against its own type's groups first, then unscoped ones (legacy last-wins lookup kept for callers without a post type) - Hook Bus and the post query router pass the post type, so a listing's hp_rating / hp_status / hp_verified map to hp_listing_core instead of the vendor/request tables; core HP groups declare their post types (schema hashes unchanged) - Behind tmdo_post_scoped_routing: on for sites without post flat rows, off where rows may sit in the wrong table until PR-034 reconciles them (dry-run: dev20–22 off, dev23/24/30/31/40 on) - Verify: EntityRegistryScopeTest (8), PostScopedRoutingTest (4); core unit 630 / integration 443, HP 144/49, WC 10/22 OK - PR: PR-033
This commit is contained in:
@@ -31,6 +31,12 @@ final class TMDO_Entity_Registry {
|
||||
/** @var array<int, array{type:string, group:string, fields:array}> 待建表的 Schema 清單 */
|
||||
private static array $pending_schemas = array();
|
||||
|
||||
/** @var array<string, array<string, array>> 實體類型 → meta_key → 欄位定義(僅未限定 post_type 的群組)*/
|
||||
private static array $unscoped_index = array();
|
||||
|
||||
/** @var array<string, array<string, array<string, array>>> 實體類型 → post_type → meta_key → 欄位定義 */
|
||||
private static array $scoped_index = array();
|
||||
|
||||
/** @var array<string, array> "entity/group" → 同名群組以不同定義重複註冊的紀錄 */
|
||||
private static array $collisions = array();
|
||||
|
||||
@@ -55,10 +61,15 @@ final class TMDO_Entity_Registry {
|
||||
/** 合法的實體類型 */
|
||||
public const VALID_ENTITY_TYPES = array( 'post', 'user', 'term', 'comment' );
|
||||
|
||||
/** Option:'1' 時 get_field() 依傳入的 post_type 解析(見 get_field())。 */
|
||||
public const SCOPED_ROUTING_OPTION = 'tmdo_post_scoped_routing';
|
||||
|
||||
public static function init(): void {
|
||||
self::$adapters = array();
|
||||
self::$groups = array();
|
||||
self::$field_index = array();
|
||||
self::$unscoped_index = array();
|
||||
self::$scoped_index = array();
|
||||
self::$pending_schemas = array();
|
||||
self::$collisions = array();
|
||||
self::$restored = array();
|
||||
@@ -100,10 +111,14 @@ final class TMDO_Entity_Registry {
|
||||
* - unique (bool) 唯一索引
|
||||
* - options (array) enum 選項
|
||||
* - label (string) 顯示名稱
|
||||
* @param array $args 選填:post_types (string[]) — 群組只套用於這些 post_type。
|
||||
* 多個群組共用 meta_key 時,依 post 本身的類型決定歸屬;
|
||||
* 不屬於任何宣告 post_type 的 post 不受管理(走原生 meta)。
|
||||
* 不列入欄位定義,所以不影響 schema hash 與衝突比對。
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function register_group( string $entity_type, string $group_name, array $fields ): bool {
|
||||
public static function register_group( string $entity_type, string $group_name, array $fields, array $args = array() ): bool {
|
||||
|
||||
if ( ! in_array( $entity_type, self::VALID_ENTITY_TYPES, true ) ) {
|
||||
return false;
|
||||
@@ -128,8 +143,9 @@ final class TMDO_Entity_Registry {
|
||||
}
|
||||
|
||||
// 建立索引以供快速查詢
|
||||
$post_types = array_values( array_filter( array_map( 'strval', (array) ( $args['post_types'] ?? array() ) ) ) );
|
||||
foreach ( $normalized as $normalized_field ) {
|
||||
self::$field_index[ $entity_type ][ $normalized_field['key'] ] = $normalized_field;
|
||||
self::index_field( $entity_type, $normalized_field, $post_types );
|
||||
}
|
||||
|
||||
self::$groups[ $entity_type ][ $group_name ] = $normalized;
|
||||
@@ -229,7 +245,7 @@ final class TMDO_Entity_Registry {
|
||||
}
|
||||
|
||||
foreach ( $kept as $field ) {
|
||||
self::$field_index[ $entity_type ][ $field['key'] ] = $field;
|
||||
self::index_field( $entity_type, $field, array() );
|
||||
}
|
||||
self::$groups[ $entity_type ][ $group_name ] = $kept;
|
||||
|
||||
@@ -244,6 +260,24 @@ final class TMDO_Entity_Registry {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把欄位加入索引。field_index 保留舊語意(整個實體共用、後註冊者覆蓋),
|
||||
* 供不帶 post_type 的查詢使用;另外依是否限定 post_type 分別建索引。
|
||||
*
|
||||
* @param string[] $post_types 群組限定的 post_type;空陣列表示不限定。
|
||||
*/
|
||||
private static function index_field( string $entity_type, array $field, array $post_types ): void {
|
||||
self::$field_index[ $entity_type ][ $field['key'] ] = $field;
|
||||
|
||||
if ( empty( $post_types ) ) {
|
||||
self::$unscoped_index[ $entity_type ][ $field['key'] ] = $field;
|
||||
return;
|
||||
}
|
||||
foreach ( $post_types as $post_type ) {
|
||||
self::$scoped_index[ $entity_type ][ $post_type ][ $field['key'] ] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本請求從 wpdo_registry_meta 還原的孤兒群組。
|
||||
*
|
||||
@@ -305,10 +339,21 @@ final class TMDO_Entity_Registry {
|
||||
/**
|
||||
* 依 meta_key 查詢欄位定義
|
||||
*
|
||||
* 傳入 $post_type 且 SCOPED_ROUTING_OPTION 開啟時:先找限定該 post_type 的群組,
|
||||
* 再找不限定的群組,都沒有則為 null。否則沿用舊語意(整個實體共用索引,
|
||||
* 共用 key 由最後註冊的群組擁有)。舊資料可能已依舊語意散落在別的群組表,
|
||||
* 所以既有站要等資料對齊後才開啟 option。
|
||||
*
|
||||
* @param string $post_type 選填:post 的 post_type。
|
||||
* @return array|null 欄位定義,或 null(非 UAE 管理欄位)
|
||||
*/
|
||||
public static function get_field( string $entity_type, string $meta_key ): ?array {
|
||||
return self::$field_index[ $entity_type ][ $meta_key ] ?? null;
|
||||
public static function get_field( string $entity_type, string $meta_key, string $post_type = '' ): ?array {
|
||||
if ( '' === $post_type || '1' !== (string) get_option( self::SCOPED_ROUTING_OPTION, '0' ) ) {
|
||||
return self::$field_index[ $entity_type ][ $meta_key ] ?? null;
|
||||
}
|
||||
return self::$scoped_index[ $entity_type ][ $post_type ][ $meta_key ]
|
||||
?? self::$unscoped_index[ $entity_type ][ $meta_key ]
|
||||
?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,7 +80,7 @@ final class TMDO_Hook_Bus {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key );
|
||||
$field_def = self::field_for( $type, (int) $object_id, $meta_key );
|
||||
if ( ! $field_def ) {
|
||||
return $check; // 非管理欄位,放行
|
||||
}
|
||||
@@ -133,7 +133,7 @@ final class TMDO_Hook_Bus {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key );
|
||||
$field_def = self::field_for( $type, (int) $object_id, $meta_key );
|
||||
if ( ! $field_def ) {
|
||||
return $check;
|
||||
}
|
||||
@@ -374,7 +374,7 @@ final class TMDO_Hook_Bus {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key );
|
||||
$field_def = self::field_for( $type, (int) $object_id, $meta_key );
|
||||
if ( ! $field_def ) {
|
||||
return $check;
|
||||
}
|
||||
@@ -471,7 +471,7 @@ final class TMDO_Hook_Bus {
|
||||
return $check;
|
||||
}
|
||||
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key );
|
||||
$field_def = self::field_for( $type, (int) $object_id, $meta_key );
|
||||
if ( ! $field_def ) {
|
||||
return $check;
|
||||
}
|
||||
@@ -622,6 +622,19 @@ final class TMDO_Hook_Bus {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 欄位定義查詢;post meta 依該 post 本身的 post_type 解析(見 TMDO_Entity_Registry::get_field())。
|
||||
*
|
||||
* 先做不帶 post_type 的查詢:大多數 key 不受管理,可省下 get_post_type()。
|
||||
*/
|
||||
private static function field_for( string $type, int $object_id, string $meta_key ): ?array {
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $meta_key );
|
||||
if ( null === $field_def || 'post' !== $type ) {
|
||||
return $field_def;
|
||||
}
|
||||
return TMDO_Entity_Registry::get_field( $type, $meta_key, (string) get_post_type( $object_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接讀取(繞過 WP filter 系統)
|
||||
* 供 wpdo_get_meta() 便利函式使用,性能更好
|
||||
@@ -646,7 +659,7 @@ final class TMDO_Hook_Bus {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$field_def = TMDO_Entity_Registry::get_field( $type, $key );
|
||||
$field_def = self::field_for( $type, $entity_id, $key );
|
||||
if ( ! $field_def ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user