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:
@@ -268,6 +268,48 @@ class TMDO_Installer {
|
||||
self::install();
|
||||
}
|
||||
self::maybe_preserve_misc_bucket_default();
|
||||
self::maybe_init_post_scoped_routing();
|
||||
}
|
||||
|
||||
/**
|
||||
* One-time default for post_type-scoped entity routing.
|
||||
*
|
||||
* Autoloaded because TMDO_Entity_Registry::get_field() reads it per lookup.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_init_post_scoped_routing(): void {
|
||||
global $wpdb;
|
||||
if ( false !== get_option( TMDO_Entity_Registry::SCOPED_ROUTING_OPTION, false ) || ! class_exists( 'TMDO_Mode_Manager' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_rows = false;
|
||||
$tables = (array) $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'wpdo_post_' ) . '%' ) );
|
||||
foreach ( $tables as $table ) {
|
||||
if ( $wpdb->get_var( "SELECT 1 FROM `{$table}` LIMIT 1" ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from SHOW TABLES.
|
||||
$has_rows = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
add_option( TMDO_Entity_Registry::SCOPED_ROUTING_OPTION, self::post_scoped_routing_default( TMDO_Mode_Manager::get( 'post' ), $has_rows ), '', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether post_type-scoped routing can start on right away.
|
||||
*
|
||||
* Existing post flat rows were written with legacy routing (a key shared by
|
||||
* several groups went to the last one registered), so they may sit in
|
||||
* another post type's table. Scoped lookups stay off there until the rows
|
||||
* are reconciled; with the post bridge disabled nothing reads them.
|
||||
*
|
||||
* @param string $post_mode Current post entity bridge mode.
|
||||
* @param bool $has_post_flat_rows Whether any wpdo_post_* table has rows.
|
||||
* @return string '1' to enable, '0' to wait for reconcile.
|
||||
*/
|
||||
public static function post_scoped_routing_default( string $post_mode, bool $has_post_flat_rows ): string {
|
||||
return ( 'disabled' === $post_mode || ! $has_post_flat_rows ) ? '1' : '0';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -340,7 +340,8 @@ final class TMDO_Post_Fields {
|
||||
'type' => 'decimal',
|
||||
'label' => 'Hourly rate',
|
||||
),
|
||||
)
|
||||
),
|
||||
array( 'post_types' => array( 'hp_listing' ) )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -384,7 +385,8 @@ final class TMDO_Post_Fields {
|
||||
'type' => 'integer',
|
||||
'label' => 'View counter',
|
||||
),
|
||||
)
|
||||
),
|
||||
array( 'post_types' => array( 'hp_request' ) )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -427,7 +429,8 @@ final class TMDO_Post_Fields {
|
||||
'searchable' => true,
|
||||
'label' => 'Average rating',
|
||||
),
|
||||
)
|
||||
),
|
||||
array( 'post_types' => array( 'hp_vendor' ) )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ class TMDO_Post_Query_Router {
|
||||
|
||||
$matched = false;
|
||||
foreach ( $post_types as $pt ) {
|
||||
$field = TMDO_Entity_Registry::get_field( 'post', $clause['key'] );
|
||||
$field = TMDO_Entity_Registry::get_field( 'post', $clause['key'], (string) $pt );
|
||||
if ( ! $field ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Hook Bus routes post meta to the group of the post's own type.
|
||||
*
|
||||
* Two groups share `shared_key`; the listing group is scoped to hp_listing,
|
||||
* the vendor group to hp_vendor. Before scoping, both posts' values landed
|
||||
* in the vendor table (last registered wins).
|
||||
*/
|
||||
class PostScopedRoutingTest extends TestCase {
|
||||
|
||||
private const LISTING_TABLE = 'wp_itest_wpdo_post_scope_listing';
|
||||
private const VENDOR_TABLE = 'wp_itest_wpdo_post_scope_vendor';
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
global $wpdb;
|
||||
$base = WPDO_PLUGIN_DIR;
|
||||
foreach ( array(
|
||||
'includes/adapters/interface-entity-adapter.php',
|
||||
'includes/engine/class-tmdo-type-caster.php',
|
||||
'includes/engine/class-tmdo-schema-manager.php',
|
||||
'includes/engine/class-tmdo-entity-registry.php',
|
||||
'includes/engine/class-tmdo-mode-manager.php',
|
||||
'includes/engine/class-tmdo-cache-orchestrator.php',
|
||||
'includes/adapters/class-tmdo-adapter-post.php',
|
||||
'includes/engine/class-tmdo-hook-bus.php',
|
||||
) as $file ) {
|
||||
require_once $base . $file;
|
||||
}
|
||||
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
|
||||
$wpdb->query(
|
||||
"CREATE TABLE `{$table}` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`post_id` BIGINT(20) NOT NULL,
|
||||
`shared_key` VARCHAR(255) DEFAULT NULL,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_entity` (`post_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void {
|
||||
global $wpdb;
|
||||
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" );
|
||||
}
|
||||
WPDO_Entity_Registry::init();
|
||||
$ref = new ReflectionProperty( WPDO_Mode_Manager::class, 'cache' );
|
||||
$ref->setAccessible( true );
|
||||
$ref->setValue( null, null );
|
||||
}
|
||||
|
||||
protected function setUp(): void {
|
||||
global $wpdb;
|
||||
foreach ( array( self::LISTING_TABLE, self::VENDOR_TABLE ) as $table ) {
|
||||
$wpdb->query( "TRUNCATE TABLE `{$table}`" );
|
||||
}
|
||||
$GLOBALS['_wp_options'] = array( WPDO_Entity_Registry::SCOPED_ROUTING_OPTION => '1' );
|
||||
$GLOBALS['_wp_cache'] = array();
|
||||
$GLOBALS['_wp_post_types'] = array( 101 => 'hp_listing', 202 => 'hp_vendor', 303 => 'post' );
|
||||
$GLOBALS['_wp_current_filter'] = 'update_post_metadata';
|
||||
|
||||
$tec = new ReflectionProperty( WPDO_Schema_Manager::class, 'table_exists_cache' );
|
||||
$tec->setAccessible( true );
|
||||
$tec->setValue( null, array( self::LISTING_TABLE => true, self::VENDOR_TABLE => true ) );
|
||||
$l1 = new ReflectionProperty( WPDO_Cache_Orchestrator::class, 'l1_cache' );
|
||||
$l1->setAccessible( true );
|
||||
$l1->setValue( null, array() );
|
||||
$mode = new ReflectionProperty( WPDO_Mode_Manager::class, 'cache' );
|
||||
$mode->setAccessible( true );
|
||||
$mode->setValue( null, array( 'post' => 'aeav_only', 'user' => 'disabled', 'term' => 'disabled', 'comment' => 'disabled' ) );
|
||||
|
||||
WPDO_Entity_Registry::init();
|
||||
WPDO_Entity_Registry::register_adapter( 'post', new WPDO_Adapter_Post() );
|
||||
WPDO_Entity_Registry::register_group( 'post', 'scope_listing', array( array( 'key' => 'shared_key', 'type' => 'text' ) ), array( 'post_types' => array( 'hp_listing' ) ) );
|
||||
WPDO_Entity_Registry::register_group( 'post', 'scope_vendor', array( array( 'key' => 'shared_key', 'type' => 'text' ) ), array( 'post_types' => array( 'hp_vendor' ) ) );
|
||||
}
|
||||
|
||||
private function value_in( string $table, int $post_id ): ?string {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var( $wpdb->prepare( "SELECT shared_key FROM `{$table}` WHERE post_id = %d", $post_id ) );
|
||||
}
|
||||
|
||||
public function test_each_post_type_writes_to_its_own_group_table(): void {
|
||||
WPDO_Hook_Bus::intercept_update( null, 101, 'shared_key', 'listing value', '' );
|
||||
WPDO_Hook_Bus::intercept_update( null, 202, 'shared_key', 'vendor value', '' );
|
||||
|
||||
$this->assertSame( 'listing value', $this->value_in( self::LISTING_TABLE, 101 ) );
|
||||
$this->assertNull( $this->value_in( self::VENDOR_TABLE, 101 ) );
|
||||
$this->assertSame( 'vendor value', $this->value_in( self::VENDOR_TABLE, 202 ) );
|
||||
}
|
||||
|
||||
public function test_reads_come_from_the_post_types_group_table(): void {
|
||||
global $wpdb;
|
||||
$wpdb->insert( self::LISTING_TABLE, array( 'post_id' => 101, 'shared_key' => 'from listing table' ) );
|
||||
$wpdb->insert( self::VENDOR_TABLE, array( 'post_id' => 101, 'shared_key' => 'stray copy' ) );
|
||||
|
||||
$this->assertSame( array( 'from listing table' ), WPDO_Hook_Bus::intercept_get( null, 101, 'shared_key', true, 'post' ) );
|
||||
}
|
||||
|
||||
public function test_other_post_types_fall_through_to_native_meta(): void {
|
||||
$this->assertNull( WPDO_Hook_Bus::intercept_update( null, 303, 'shared_key', 'x', '' ) );
|
||||
$this->assertNull( WPDO_Hook_Bus::intercept_get( null, 303, 'shared_key', true, 'post' ) );
|
||||
}
|
||||
|
||||
public function test_without_the_option_routing_stays_legacy(): void {
|
||||
$GLOBALS['_wp_options'][ WPDO_Entity_Registry::SCOPED_ROUTING_OPTION ] = '0';
|
||||
|
||||
WPDO_Hook_Bus::intercept_update( null, 101, 'shared_key', 'legacy', '' );
|
||||
|
||||
$this->assertSame( 'legacy', $this->value_in( self::VENDOR_TABLE, 101 ) );
|
||||
$this->assertNull( $this->value_in( self::LISTING_TABLE, 101 ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Post entity groups scoped to post types.
|
||||
*
|
||||
* The post entity used to keep one meta_key index for every post type, so a
|
||||
* key shared by several groups (hp_rating in hp_listing_core and
|
||||
* hp_vendor_core) was routed to whichever group registered last — a
|
||||
* listing's hp_rating landed in the vendor table. Groups can now declare the
|
||||
* post types they belong to, and lookups that pass a post type use it.
|
||||
*/
|
||||
class EntityRegistryScopeTest extends TestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
TMDO_Entity_Registry::init();
|
||||
TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
|
||||
$GLOBALS['_wp_options'] = array( TMDO_Entity_Registry::SCOPED_ROUTING_OPTION => '1' );
|
||||
|
||||
TMDO_Entity_Registry::register_group( 'post', 'site_core', array( array( 'key' => '_edit_lock', 'type' => 'text' ) ) );
|
||||
TMDO_Entity_Registry::register_group(
|
||||
'post',
|
||||
'listing_core',
|
||||
array(
|
||||
array( 'key' => 'rating', 'type' => 'decimal' ),
|
||||
array( 'key' => 'price', 'type' => 'decimal' ),
|
||||
),
|
||||
array( 'post_types' => array( 'hp_listing' ) )
|
||||
);
|
||||
TMDO_Entity_Registry::register_group(
|
||||
'post',
|
||||
'vendor_core',
|
||||
array( array( 'key' => 'rating', 'type' => 'decimal' ) ),
|
||||
array( 'post_types' => array( 'hp_vendor' ) )
|
||||
);
|
||||
}
|
||||
|
||||
public function test_shared_key_resolves_to_the_group_of_the_post_type(): void {
|
||||
$this->assertSame( 'listing_core', TMDO_Entity_Registry::get_field( 'post', 'rating', 'hp_listing' )['group'] );
|
||||
$this->assertSame( 'vendor_core', TMDO_Entity_Registry::get_field( 'post', 'rating', 'hp_vendor' )['group'] );
|
||||
}
|
||||
|
||||
public function test_scoped_key_is_unmanaged_for_other_post_types(): void {
|
||||
$this->assertNull( TMDO_Entity_Registry::get_field( 'post', 'rating', 'post' ) );
|
||||
$this->assertNull( TMDO_Entity_Registry::get_field( 'post', 'price', 'hp_vendor' ) );
|
||||
}
|
||||
|
||||
public function test_unscoped_group_applies_to_every_post_type(): void {
|
||||
$this->assertSame( 'site_core', TMDO_Entity_Registry::get_field( 'post', '_edit_lock', 'hp_listing' )['group'] );
|
||||
$this->assertSame( 'site_core', TMDO_Entity_Registry::get_field( 'post', '_edit_lock', 'page' )['group'] );
|
||||
}
|
||||
|
||||
public function test_lookup_without_post_type_keeps_legacy_last_wins(): void {
|
||||
$this->assertSame( 'vendor_core', TMDO_Entity_Registry::get_field( 'post', 'rating' )['group'] );
|
||||
}
|
||||
|
||||
public function test_scoping_is_off_until_the_option_enables_it(): void {
|
||||
$GLOBALS['_wp_options'][ TMDO_Entity_Registry::SCOPED_ROUTING_OPTION ] = '0';
|
||||
|
||||
$this->assertSame( 'vendor_core', TMDO_Entity_Registry::get_field( 'post', 'rating', 'hp_listing' )['group'] );
|
||||
$this->assertSame( 'listing_core', TMDO_Entity_Registry::get_field( 'post', 'price', 'hp_vendor' )['group'] );
|
||||
}
|
||||
|
||||
public function test_scope_is_not_part_of_the_field_definition(): void {
|
||||
// Keeps schema hashes and PR-017 collision checks unchanged.
|
||||
$fields = TMDO_Entity_Registry::get_group_fields( 'post', 'vendor_core' );
|
||||
$this->assertArrayNotHasKey( 'post_types', $fields[0] );
|
||||
}
|
||||
|
||||
public function test_scoped_routing_default_waits_for_reconcile_on_sites_with_post_flat_data(): void {
|
||||
// Existing flat rows may sit in the wrong group table (legacy routing);
|
||||
// switching lookups before they are reconciled would hide them.
|
||||
$this->assertSame( '0', TMDO_Installer::post_scoped_routing_default( 'aeav_only', true ) );
|
||||
$this->assertSame( '0', TMDO_Installer::post_scoped_routing_default( 'dual_write', true ) );
|
||||
$this->assertSame( '1', TMDO_Installer::post_scoped_routing_default( 'aeav_only', false ) );
|
||||
$this->assertSame( '1', TMDO_Installer::post_scoped_routing_default( 'disabled', true ) );
|
||||
}
|
||||
|
||||
public function test_core_hivepress_groups_are_scoped_without_changing_their_hash(): void {
|
||||
TMDO_Entity_Registry::init();
|
||||
TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
|
||||
TMDO_Post_Fields::register_entity_fields();
|
||||
|
||||
$persisted = array(
|
||||
'hp_listing_core' => 'fb5ab760',
|
||||
'hp_request_core' => '27d6f4f5',
|
||||
'hp_vendor_core' => '5366e5c8',
|
||||
);
|
||||
foreach ( $persisted as $group => $hash ) {
|
||||
$fields = TMDO_Entity_Registry::get_group_fields( 'post', $group );
|
||||
$this->assertSame( $hash, substr( TMDO_Schema_Manager::calculate_schema_hash( $fields ), 0, 8 ), $group );
|
||||
}
|
||||
|
||||
$this->assertSame( 'hp_listing_core', TMDO_Entity_Registry::get_field( 'post', 'hp_rating', 'hp_listing' )['group'] );
|
||||
$this->assertSame( 'hp_listing_core', TMDO_Entity_Registry::get_field( 'post', 'hp_status', 'hp_listing' )['group'] );
|
||||
$this->assertSame( 'hp_request_core', TMDO_Entity_Registry::get_field( 'post', 'hp_status', 'hp_request' )['group'] );
|
||||
$this->assertSame( 'hp_vendor_core', TMDO_Entity_Registry::get_field( 'post', 'hp_user', 'hp_vendor' )['group'] );
|
||||
$this->assertSame( 'hp_request_core', TMDO_Entity_Registry::get_field( 'post', 'hp_user', 'hp_request' )['group'] );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user