diff --git a/includes/class-tmdo-hivepress-entity-groups.php b/includes/class-tmdo-hivepress-entity-groups.php index 27d41ee..184c367 100644 --- a/includes/class-tmdo-hivepress-entity-groups.php +++ b/includes/class-tmdo-hivepress-entity-groups.php @@ -76,6 +76,7 @@ final class TMDO_HivePress_Entity_Groups { self::register_hp_request_core_group(); self::register_hp_vendor_core_group(); self::register_hp_user_group(); + self::register_hp_listing_attributes_group(); } // ── Group definitions ──────────────────────────────────────────────────── @@ -271,6 +272,39 @@ final class TMDO_HivePress_Entity_Groups { ) ); } + /** + * A site's own HivePress listing attributes (created under Listings → + * Attributes, e.g. a "Warranty" number → hp_warranty), opt-in through the + * `tmdo_hp_listing_attribute_fields` filter: `array( 'hp_warranty' => 'integer' )`. + * + * They get a group of their own — a new table, each column indexed at + * creation — so hp_listing_core and its pinned definition stay unchanged. + * Keys another group already manages, keys without the hp_ prefix and + * unknown types are ignored. Backfill with `wp tmdo post-migrate-group`. + * + * @return void + */ + private static function register_hp_listing_attributes_group(): void { + $fields = array(); + foreach ( (array) apply_filters( 'tmdo_hp_listing_attribute_fields', array() ) as $key => $type ) { + $key = (string) $key; + if ( 1 !== preg_match( '/^hp_[a-z0-9_]+$/', $key ) + || ! in_array( $type, TMDO_Entity_Registry::VALID_TYPES, true ) + || null !== TMDO_Entity_Registry::get_field( 'post', $key, 'hp_listing' ) ) { + continue; + } + $fields[] = array( + 'key' => $key, + 'type' => $type, + 'searchable' => true, + 'label' => 'Site listing attribute', + ); + } + if ( $fields ) { + TMDO_Entity_Registry::register_group( 'post', 'hp_listing_attrs', $fields, array( 'post_types' => array( 'hp_listing' ) ) ); + } + } + /** * HivePress request core meta keys (post_type=hp_request). * diff --git a/tests/unit/HivePressEntityGroupsTest.php b/tests/unit/HivePressEntityGroupsTest.php index 6eea5f1..44c170f 100644 --- a/tests/unit/HivePressEntityGroupsTest.php +++ b/tests/unit/HivePressEntityGroupsTest.php @@ -73,6 +73,39 @@ class HivePressEntityGroupsTest extends TestCase { ); } + public function test_site_listing_attributes_are_opt_in(): void { + $this->assertSame( array(), TMDO_Entity_Registry::get_group_fields( 'post', 'hp_listing_attrs' ) ); + } + + /** + * A site flattens its own HivePress listing attributes (e.g. a "Warranty" + * number attribute → hp_warranty) in a group of their own: a new table, + * indexed at creation; hp_listing_core and its pinned hash stay as they are. + */ + public function test_site_listing_attributes_get_their_own_indexed_group(): void { + $fields = static fn() => array( + 'hp_warranty' => 'integer', + 'hp_price' => 'decimal', // Already in hp_listing_core. + 'warranty' => 'integer', // Not a HivePress key. + 'hp_color' => 'colour', // Unknown type. + ); + add_filter( 'tmdo_hp_listing_attribute_fields', $fields ); + try { + TMDO_Entity_Registry::init(); + TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) ); + TMDO_Entity_Registry::register_adapter( 'user', $this->createMock( TMDO_Entity_Adapter_Interface::class ) ); + TMDO_HivePress_Entity_Groups::register_entity_fields(); + } finally { + $GLOBALS['_wp_filter_callbacks']['tmdo_hp_listing_attribute_fields'] = array(); + } + + $this->assertSame( array( 'hp_warranty' ), TMDO_Entity_Registry::get_group_keys( 'post', 'hp_listing_attrs' ) ); + $this->assertTrue( (bool) TMDO_Entity_Registry::get_group_fields( 'post', 'hp_listing_attrs' )[0]['searchable'] ); + $this->assertSame( array( 'hp_listing' ), TMDO_Entity_Registry::get_group_post_types( 'post', 'hp_listing_attrs' ) ); + $this->assertSame( 'hp_listing_core', TMDO_Entity_Registry::get_field( 'post', 'hp_price' )['group'] ); + $this->test_definitions_match_persisted_schema_hashes(); + } + public function test_shared_keys_keep_their_current_owner(): void { foreach ( self::SHARED_KEY_OWNERS as $key => $group ) { $this->assertSame( $group, TMDO_Entity_Registry::get_field( 'post', $key )['group'], $key );