refactor(2meet-data-optimizer-hivepress-addon): stop registering Hot zone fields
- ADR-003 step 4 (AddOn side): core, requests, bookings, memberships and marketplace adapters no longer register zone=hot fields or contribute hot_* FSM modules; their keys live in TMDO_HivePress_Entity_Groups. Cold/warm registrations are unchanged. - Hot-table doctor probes go with them (trait defaults apply). The hot-clause query rewrite and cron optimizer are now inert and are left for the ADR step-5 removal together with core's hot path. - New NoHotZoneRegistrationTest pins the invariant across all 13 adapters; per-adapter hot assertions removed. - Verify: HP unit 142 / integration 49 green, phpcs clean (WordPress) - PR: PR-037
This commit is contained in:
@@ -8,18 +8,13 @@
|
|||||||
* Booking model (post_type=hp_booking) — fields are best-effort from the
|
* Booking model (post_type=hp_booking) — fields are best-effort from the
|
||||||
* addon's documented field aliases:
|
* addon's documented field aliases:
|
||||||
*
|
*
|
||||||
* start_time / end_time → unix timestamp range queries (hot)
|
* start_time / end_time → unix timestamp range queries
|
||||||
* status → enum filter (hot)
|
* quantity → places booked, hp_quantity (only written when
|
||||||
* quantity → places booked, hp_quantity (hot for slot-availability;
|
* "Allow multiple places" is on)
|
||||||
* only written when "Allow multiple places" is on)
|
|
||||||
* listing → post_parent (already a posts column)
|
* listing → post_parent (already a posts column)
|
||||||
*
|
*
|
||||||
* Hot-zone target: `wp_wpdo_hot_hp_booking` with covering indexes on
|
* These keys are flattened by the `hp_booking_core` entity group
|
||||||
* `(listing_id, start_time)` for slot-availability queries and
|
* (TMDO_HivePress_Entity_Groups); the adapter registers no zone fields.
|
||||||
* `(status, start_time)` for vendor dashboard listings.
|
|
||||||
*
|
|
||||||
* The schema MAY drift from real Booking model when the addon is finally
|
|
||||||
* installed; reconcile with `wp wpdo doctor` then.
|
|
||||||
*
|
*
|
||||||
* @package WP_Data_Optimizer
|
* @package WP_Data_Optimizer
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
@@ -65,73 +60,6 @@ if ( ! class_exists( 'TMDO_HivePress_Bookings_Adapter' ) ) {
|
|||||||
return '1.0.0';
|
return '1.0.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register hot fields for hp_booking post type.
|
|
||||||
*
|
|
||||||
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
|
||||||
*/
|
|
||||||
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
|
||||||
$registry->register_many(
|
|
||||||
$this->plugin_slug(),
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_booking',
|
|
||||||
'meta_key' => 'hp_start_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_start_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_booking',
|
|
||||||
'meta_key' => 'hp_end_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_end_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_booking',
|
|
||||||
'meta_key' => 'hp_quantity',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'int(11) NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_quantity',
|
|
||||||
'indexed' => false,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doctor probe — verify hot table when addon is active.
|
|
||||||
*
|
|
||||||
* @return array{ok:bool, message:string}
|
|
||||||
*/
|
|
||||||
public function doctor_check(): array {
|
|
||||||
global $wpdb;
|
|
||||||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'wpdb global not available',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$table = $wpdb->prefix . 'wpdo_hot_hp_booking';
|
|
||||||
try {
|
|
||||||
$exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
|
|
||||||
return array(
|
|
||||||
'ok' => $exists,
|
|
||||||
'message' => $exists
|
|
||||||
? sprintf( 'hivepress-bookings: %s present', $table )
|
|
||||||
: sprintf( 'hivepress-bookings: %s missing (addon installed but not migrated)', $table ),
|
|
||||||
);
|
|
||||||
} catch ( \Throwable $e ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => sprintf( 'hivepress-bookings doctor failed: %s', $e->getMessage() ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 8-D self-score.
|
* 8-D self-score.
|
||||||
*
|
*
|
||||||
@@ -146,15 +74,6 @@ if ( ! class_exists( 'TMDO_HivePress_Bookings_Adapter' ) ) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* FSM modules this adapter contributes.
|
|
||||||
*
|
|
||||||
* @return array<int,string>
|
|
||||||
*/
|
|
||||||
public function migrations(): array {
|
|
||||||
return array( 'hot_hp_booking' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end if ( ! class_exists )
|
} // end if ( ! class_exists )
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
*
|
*
|
||||||
* Covers the primary HivePress models:
|
* Covers the primary HivePress models:
|
||||||
*
|
*
|
||||||
* - `Listing` (post_type=hp_listing) — Hot zone columns for search/filter
|
* - `Listing` (post_type=hp_listing) — cold blob for the gallery.
|
||||||
* (drafted, featured, verified, expired_time, featured_time).
|
* - `Vendor` (post_type=hp_vendor) — cold blob for the image.
|
||||||
* - `Vendor` (post_type=hp_vendor) — Hot column `verified`; cold blob
|
|
||||||
* for image / description (display-only).
|
|
||||||
* - `User` (entity=user) — no zone fields; its `hp_user`
|
* - `User` (entity=user) — no zone fields; its `hp_user`
|
||||||
* entity group is registered by TMDO_HivePress_Entity_Groups.
|
* entity group is registered by TMDO_HivePress_Entity_Groups.
|
||||||
*
|
*
|
||||||
|
* Search/filter keys are flattened by TMDO_HivePress_Entity_Groups
|
||||||
|
* (ADR-003: no Hot zone fields).
|
||||||
|
*
|
||||||
* This adapter SUPERSEDES the legacy `class-tmdo-hivepress.php` (242 lines).
|
* This adapter SUPERSEDES the legacy `class-tmdo-hivepress.php` (242 lines).
|
||||||
* Behavioural parity:
|
* Behavioural parity:
|
||||||
*
|
*
|
||||||
@@ -23,11 +24,6 @@
|
|||||||
* adapter only registers `hivepress` core fields.
|
* adapter only registers `hivepress` core fields.
|
||||||
* - Vendor cold fields (`hp_image`, `hp_images`) — preserved.
|
* - Vendor cold fields (`hp_image`, `hp_images`) — preserved.
|
||||||
*
|
*
|
||||||
* NEW additions vs legacy:
|
|
||||||
*
|
|
||||||
* - `hp_drafted` (Listing) — Hot tinyint(1) for user-dashboard filtering.
|
|
||||||
* - `hp_expired_time` / `hp_featured_time` (Listing) — Hot bigint indexed
|
|
||||||
* for cron expiry scan acceleration (replaces postmeta full scan).
|
|
||||||
* (This adapter used to register a second `hp_user` definition —
|
* (This adapter used to register a second `hp_user` definition —
|
||||||
* hp_verified / hp_avatar_id, keys HivePress never writes — which the
|
* hp_verified / hp_avatar_id, keys HivePress never writes — which the
|
||||||
* registry silently dropped; TMDO_HivePress_Entity_Groups owns it now.)
|
* registry silently dropped; TMDO_HivePress_Entity_Groups owns it now.)
|
||||||
@@ -114,37 +110,10 @@ if ( ! class_exists( 'TMDO_HivePress_Core_Adapter' ) ) {
|
|||||||
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
||||||
*/
|
*/
|
||||||
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
||||||
// ── Listing hot fields (search/filter accelerators) ──
|
// ── Cold blobs (display-only) ──
|
||||||
$registry->register_many(
|
$registry->register_many(
|
||||||
$this->plugin_slug(),
|
$this->plugin_slug(),
|
||||||
array(
|
array(
|
||||||
// Drafted — used in user dashboard "my drafts" filter.
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_listing',
|
|
||||||
'meta_key' => 'hp_drafted',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'tinyint(1) NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_drafted',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
// Expired time — replaces `meta_query` clause in hourly cron.
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_listing',
|
|
||||||
'meta_key' => 'hp_expired_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_expired_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
// Featured time — same expiry pattern as above.
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_listing',
|
|
||||||
'meta_key' => 'hp_featured_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_featured_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
// Vendor cold blob — image attachment id (display-only).
|
// Vendor cold blob — image attachment id (display-only).
|
||||||
array(
|
array(
|
||||||
'post_type' => 'hp_vendor',
|
'post_type' => 'hp_vendor',
|
||||||
@@ -276,57 +245,6 @@ if ( ! class_exists( 'TMDO_HivePress_Core_Adapter' ) ) {
|
|||||||
|
|
||||||
// ── Doctor / score / migrations ────────────────────────────────────
|
// ── Doctor / score / migrations ────────────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Health probe: verify hot tables exist + sample row count.
|
|
||||||
*
|
|
||||||
* @return array{ok:bool, message:string, details?:array<string,mixed>}
|
|
||||||
*/
|
|
||||||
public function doctor_check(): array {
|
|
||||||
global $wpdb;
|
|
||||||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'wpdb global not available',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = array();
|
|
||||||
$ok = true;
|
|
||||||
foreach ( self::HOT_MODELS as $post_type ) {
|
|
||||||
$table = $wpdb->prefix . 'wpdo_hot_' . sanitize_key( $post_type );
|
|
||||||
try {
|
|
||||||
$exists = (bool) $wpdb->get_var(
|
|
||||||
$wpdb->prepare( 'SHOW TABLES LIKE %s', $table )
|
|
||||||
);
|
|
||||||
$details[ $post_type ] = array(
|
|
||||||
'table' => $table,
|
|
||||||
'exists' => $exists,
|
|
||||||
);
|
|
||||||
if ( $exists ) {
|
|
||||||
$details[ $post_type ]['rows'] = (int) $wpdb->get_var(
|
|
||||||
"SELECT COUNT(*) FROM `{$table}`" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Sanitized via sanitize_key + wpdb prefix.
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
} catch ( \Throwable $e ) {
|
|
||||||
$details[ $post_type ] = array(
|
|
||||||
'table' => $table,
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
);
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'ok' => $ok,
|
|
||||||
'message' => $ok
|
|
||||||
? 'hivepress core: hot tables OK'
|
|
||||||
: 'hivepress core: one or more hot tables missing or unreadable',
|
|
||||||
'details' => $details,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 8-dimension self-score for the core adapter.
|
* 8-dimension self-score for the core adapter.
|
||||||
*
|
*
|
||||||
@@ -348,18 +266,6 @@ if ( ! class_exists( 'TMDO_HivePress_Core_Adapter' ) ) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* FSM modules this adapter contributes.
|
|
||||||
*
|
|
||||||
* @return array<int,string>
|
|
||||||
*/
|
|
||||||
public function migrations(): array {
|
|
||||||
return array(
|
|
||||||
'hot_hp_listing',
|
|
||||||
'hot_hp_vendor',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end if ( ! class_exists )
|
} // end if ( ! class_exists )
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
*
|
*
|
||||||
* The marketplace addon turns listings into purchasable items, integrating
|
* The marketplace addon turns listings into purchasable items, integrating
|
||||||
* with WooCommerce. Its filterable, sortable listing attribute `price` is
|
* with WooCommerce. Its filterable, sortable listing attribute `price` is
|
||||||
* stored as `hp_price` → decimal(10,2), used in price-range filters (HOT).
|
* stored as `hp_price` → decimal(10,2), used in price-range filters. It is
|
||||||
|
* flattened by the `hp_listing_core` entity group
|
||||||
|
* (TMDO_HivePress_Entity_Groups); the adapter registers no zone fields.
|
||||||
*
|
*
|
||||||
* Its other listing attributes (price tiers / extras / discounts, quantity,
|
* Its other listing attributes (price tiers / extras / discounts, quantity,
|
||||||
* subscription period, …) are not search filters, and it keeps no per-listing
|
* subscription period, …) are not search filters, and it keeps no per-listing
|
||||||
@@ -57,57 +59,6 @@ if ( ! class_exists( 'TMDO_HivePress_Marketplace_Adapter' ) ) {
|
|||||||
return '1.0.0';
|
return '1.0.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register marketplace hot fields on the listing post type.
|
|
||||||
*
|
|
||||||
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
|
||||||
*/
|
|
||||||
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
|
||||||
$registry->register_many(
|
|
||||||
$this->plugin_slug(),
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_listing',
|
|
||||||
'meta_key' => 'hp_price',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'decimal(10,2) DEFAULT NULL',
|
|
||||||
'column' => 'hp_price',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doctor probe — verify hot column exists on listing hot table.
|
|
||||||
*
|
|
||||||
* @return array{ok:bool, message:string}
|
|
||||||
*/
|
|
||||||
public function doctor_check(): array {
|
|
||||||
global $wpdb;
|
|
||||||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'wpdb global not available',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$table = $wpdb->prefix . 'wpdo_hot_hp_listing';
|
|
||||||
try {
|
|
||||||
$exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
|
|
||||||
return array(
|
|
||||||
'ok' => $exists,
|
|
||||||
'message' => $exists
|
|
||||||
? 'hivepress-marketplace: shares wp_wpdo_hot_hp_listing'
|
|
||||||
: 'hivepress-marketplace: hot listing table missing',
|
|
||||||
);
|
|
||||||
} catch ( \Throwable $e ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => sprintf( 'hivepress-marketplace doctor failed: %s', $e->getMessage() ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 8-D self-score.
|
* 8-D self-score.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,18 +5,13 @@
|
|||||||
* Two post-type models:
|
* Two post-type models:
|
||||||
*
|
*
|
||||||
* - Membership_Plan (post_type=hp_membership_plan)
|
* - Membership_Plan (post_type=hp_membership_plan)
|
||||||
* hot fields: hp_expire_period (int), hp_primary (bool)
|
* hp_expire_period (int), hp_primary (bool)
|
||||||
*
|
*
|
||||||
* - Membership (post_type=hp_membership)
|
* - Membership (post_type=hp_membership)
|
||||||
* hot fields: hp_expired_time (unix ts) — checked on every page load
|
* hp_expired_time (unix ts) — checked on every page load
|
||||||
*
|
*
|
||||||
* Both fields registered to Hot zone so:
|
* Flattened by the `hp_membership_plan_core` / `hp_membership_core` entity
|
||||||
* - `hp_expired_time < NOW()` lookups become indexed range scans
|
* groups (TMDO_HivePress_Entity_Groups); the adapter registers no zone fields.
|
||||||
* (replaces O(N) postmeta scan on each request).
|
|
||||||
* - `hp_primary = 1` lookups for "default plan" become O(1).
|
|
||||||
*
|
|
||||||
* Tables `wp_wpdo_hot_hp_membership_plan` and `wp_wpdo_hot_hp_membership`
|
|
||||||
* are created by `TMDO_Schema_Manager` from the registered hot columns.
|
|
||||||
*
|
*
|
||||||
* @package WP_Data_Optimizer
|
* @package WP_Data_Optimizer
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
@@ -62,81 +57,6 @@ if ( ! class_exists( 'TMDO_HivePress_Memberships_Adapter' ) ) {
|
|||||||
return '2.0.0';
|
return '2.0.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register hot fields for both post types.
|
|
||||||
*
|
|
||||||
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
|
||||||
*/
|
|
||||||
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
|
||||||
$registry->register_many(
|
|
||||||
$this->plugin_slug(),
|
|
||||||
array(
|
|
||||||
// Plan fields.
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_membership_plan',
|
|
||||||
'meta_key' => 'hp_expire_period',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'int(11) NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_expire_period',
|
|
||||||
'indexed' => false,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_membership_plan',
|
|
||||||
'meta_key' => 'hp_primary',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'tinyint(1) NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_primary',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
// Membership (subscription) field — hottest path: every page load.
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_membership',
|
|
||||||
'meta_key' => 'hp_expired_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_expired_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doctor probe — verify both hot tables.
|
|
||||||
*
|
|
||||||
* @return array{ok:bool, message:string, details?:array<string,mixed>}
|
|
||||||
*/
|
|
||||||
public function doctor_check(): array {
|
|
||||||
global $wpdb;
|
|
||||||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'wpdb global not available',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$details = array();
|
|
||||||
$ok = true;
|
|
||||||
foreach ( array( 'hp_membership_plan', 'hp_membership' ) as $pt ) {
|
|
||||||
$table = $wpdb->prefix . 'wpdo_hot_' . sanitize_key( $pt );
|
|
||||||
try {
|
|
||||||
$exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
|
|
||||||
$details[ $pt ]['table'] = $table;
|
|
||||||
$details[ $pt ]['exists'] = $exists;
|
|
||||||
if ( ! $exists ) {
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
} catch ( \Throwable $e ) {
|
|
||||||
$details[ $pt ]['error'] = $e->getMessage();
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array(
|
|
||||||
'ok' => $ok,
|
|
||||||
'message' => $ok ? 'hivepress-memberships: hot tables OK' : 'hivepress-memberships: hot tables missing',
|
|
||||||
'details' => $details,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 8-D self-score.
|
* 8-D self-score.
|
||||||
*
|
*
|
||||||
@@ -152,18 +72,6 @@ if ( ! class_exists( 'TMDO_HivePress_Memberships_Adapter' ) ) {
|
|||||||
public function suitability_score(): array {
|
public function suitability_score(): array {
|
||||||
return $this->compose_score( array() );
|
return $this->compose_score( array() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* FSM modules this adapter contributes.
|
|
||||||
*
|
|
||||||
* @return array<int,string>
|
|
||||||
*/
|
|
||||||
public function migrations(): array {
|
|
||||||
return array(
|
|
||||||
'hot_hp_membership_plan',
|
|
||||||
'hot_hp_membership',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end if ( ! class_exists )
|
} // end if ( ! class_exists )
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
* Two related models:
|
* Two related models:
|
||||||
*
|
*
|
||||||
* - Request (post_type=hp_request)
|
* - Request (post_type=hp_request)
|
||||||
* hot fields: hp_drafted (bool), hp_expired_time (unix ts)
|
* hp_drafted / hp_expired_time are flattened by the `hp_draft_flags`
|
||||||
* — same access pattern as Listing (user dashboard + expiry cron)
|
* and `hp_request_core` entity groups (TMDO_HivePress_Entity_Groups).
|
||||||
*
|
*
|
||||||
* - Offer (comment_type=hp_offer, parent post_type=hp_request)
|
* - Offer (comment_type=hp_offer, parent post_type=hp_request)
|
||||||
* stored in comment columns only — no comment meta to flatten.
|
* stored in comment columns only — no comment meta to flatten.
|
||||||
@@ -59,74 +59,6 @@ if ( ! class_exists( 'TMDO_HivePress_Requests_Adapter' ) ) {
|
|||||||
public function minimum_addon_version(): string {
|
public function minimum_addon_version(): string {
|
||||||
return '1.2.0';
|
return '1.2.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register hot fields for hp_request post type.
|
|
||||||
*
|
|
||||||
* @param TMDO_Schema_Registry $registry Schema registry singleton.
|
|
||||||
*/
|
|
||||||
public function on_register_fields( TMDO_Schema_Registry $registry ): void {
|
|
||||||
$registry->register_many(
|
|
||||||
$this->plugin_slug(),
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_request',
|
|
||||||
'meta_key' => 'hp_drafted',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'tinyint(1) NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_drafted',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'post_type' => 'hp_request',
|
|
||||||
'meta_key' => 'hp_expired_time',
|
|
||||||
'zone' => 'hot',
|
|
||||||
'data_type' => 'bigint(20) UNSIGNED NOT NULL DEFAULT 0',
|
|
||||||
'column' => 'hp_expired_time',
|
|
||||||
'indexed' => true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doctor probe — request hot table.
|
|
||||||
*
|
|
||||||
* @return array{ok:bool, message:string, details?:array<string,mixed>}
|
|
||||||
*/
|
|
||||||
public function doctor_check(): array {
|
|
||||||
global $wpdb;
|
|
||||||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'wpdb global not available',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$table = $wpdb->prefix . 'wpdo_hot_hp_request';
|
|
||||||
try {
|
|
||||||
$exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
|
|
||||||
return array(
|
|
||||||
'ok' => $exists,
|
|
||||||
'message' => $exists
|
|
||||||
? sprintf( 'hivepress-requests: %s present', $table )
|
|
||||||
: sprintf( 'hivepress-requests: %s missing', $table ),
|
|
||||||
);
|
|
||||||
} catch ( \Throwable $e ) {
|
|
||||||
return array(
|
|
||||||
'ok' => false,
|
|
||||||
'message' => sprintf( 'hivepress-requests doctor failed: %s', $e->getMessage() ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FSM modules this adapter contributes.
|
|
||||||
*
|
|
||||||
* @return array<int,string>
|
|
||||||
*/
|
|
||||||
public function migrations(): array {
|
|
||||||
return array( 'hot_hp_request' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end if ( ! class_exists )
|
} // end if ( ! class_exists )
|
||||||
|
|||||||
@@ -33,31 +33,6 @@ class HivePressBookingsAdapterTest extends TestCase {
|
|||||||
$this->assertSame( 'HivePress\\Bookings\\Plugin', $this->adapter->detection_class() );
|
$this->assertSame( 'HivePress\\Bookings\\Plugin', $this->adapter->detection_class() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_on_register_fields_reserves_booking_hot_columns(): void {
|
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
|
||||||
$this->adapter->on_register_fields( $registry );
|
|
||||||
|
|
||||||
$start = $registry->get_field( 'hp_booking', 'hp_start_time' );
|
|
||||||
$this->assertSame( 'hot', $start['zone'] );
|
|
||||||
$this->assertTrue( (bool) $start['indexed'] );
|
|
||||||
|
|
||||||
$end = $registry->get_field( 'hp_booking', 'hp_end_time' );
|
|
||||||
$this->assertSame( 'hot', $end['zone'] );
|
|
||||||
$this->assertTrue( (bool) $end['indexed'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_on_register_fields_uses_real_booking_meta_keys(): void {
|
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
|
||||||
$this->adapter->on_register_fields( $registry );
|
|
||||||
|
|
||||||
// hivepress-bookings has no guests field; places are stored as hp_quantity.
|
|
||||||
$this->assertNull( $registry->get_field( 'hp_booking', 'hp_guests' ) );
|
|
||||||
|
|
||||||
$quantity = $registry->get_field( 'hp_booking', 'hp_quantity' );
|
|
||||||
$this->assertNotNull( $quantity );
|
|
||||||
$this->assertSame( 'hot', $quantity['zone'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_score_reflects_unverified_perf_dimension(): void {
|
public function test_score_reflects_unverified_perf_dimension(): void {
|
||||||
$score = $this->adapter->suitability_score();
|
$score = $this->adapter->suitability_score();
|
||||||
// D8 reduced to 0.9 because schema is best-effort until addon installs.
|
// D8 reduced to 0.9 because schema is best-effort until addon installs.
|
||||||
@@ -65,8 +40,4 @@ class HivePressBookingsAdapterTest extends TestCase {
|
|||||||
// Aggregate = (7 × 1.0 + 0.9) / 8 × 10 = 9.875 → 9.88.
|
// Aggregate = (7 × 1.0 + 0.9) / 8 × 10 = 9.875 → 9.88.
|
||||||
$this->assertSame( 9.88, $score['aggregate'] );
|
$this->assertSame( 9.88, $score['aggregate'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_migrations_lists_booking_module(): void {
|
|
||||||
$this->assertSame( array( 'hot_hp_booking' ), $this->adapter->migrations() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,29 +46,10 @@ class HivePressCoreAdapterTest extends TestCase {
|
|||||||
$this->assertSame( '1.7.0', $this->adapter->minimum_addon_version() );
|
$this->assertSame( '1.7.0', $this->adapter->minimum_addon_version() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_migrations_returns_listing_and_vendor_modules(): void {
|
|
||||||
$mods = $this->adapter->migrations();
|
|
||||||
$this->assertContains( 'hot_hp_listing', $mods );
|
|
||||||
$this->assertContains( 'hot_hp_vendor', $mods );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_on_register_fields_populates_schema_registry(): void {
|
public function test_on_register_fields_populates_schema_registry(): void {
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
$registry = WPDO_Schema_Registry::instance();
|
||||||
$this->adapter->on_register_fields( $registry );
|
$this->adapter->on_register_fields( $registry );
|
||||||
|
|
||||||
// Listing hot fields.
|
|
||||||
$drafted = $registry->get_field( 'hp_listing', 'hp_drafted' );
|
|
||||||
$this->assertIsArray( $drafted );
|
|
||||||
$this->assertSame( 'hot', $drafted['zone'] );
|
|
||||||
$this->assertTrue( (bool) $drafted['indexed'] );
|
|
||||||
|
|
||||||
$expired = $registry->get_field( 'hp_listing', 'hp_expired_time' );
|
|
||||||
$this->assertSame( 'hot', $expired['zone'] );
|
|
||||||
$this->assertStringContainsString( 'bigint', $expired['data_type'] );
|
|
||||||
|
|
||||||
$featured_time = $registry->get_field( 'hp_listing', 'hp_featured_time' );
|
|
||||||
$this->assertSame( 'hot', $featured_time['zone'] );
|
|
||||||
|
|
||||||
// Vendor cold blob.
|
// Vendor cold blob.
|
||||||
$vendor_image = $registry->get_field( 'hp_vendor', 'hp_image' );
|
$vendor_image = $registry->get_field( 'hp_vendor', 'hp_image' );
|
||||||
$this->assertIsArray( $vendor_image );
|
$this->assertIsArray( $vendor_image );
|
||||||
@@ -87,16 +68,6 @@ class HivePressCoreAdapterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_doctor_check_reports_table_status(): void {
|
|
||||||
$result = $this->adapter->doctor_check();
|
|
||||||
$this->assertIsArray( $result );
|
|
||||||
$this->assertArrayHasKey( 'ok', $result );
|
|
||||||
$this->assertArrayHasKey( 'message', $result );
|
|
||||||
$this->assertArrayHasKey( 'details', $result );
|
|
||||||
// The stub $wpdb returns null for SHOW TABLES, so tables appear missing → ok=false.
|
|
||||||
$this->assertFalse( $result['ok'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_optimize_query_extracts_hot_clauses(): void {
|
public function test_optimize_query_extracts_hot_clauses(): void {
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
$registry = WPDO_Schema_Registry::instance();
|
||||||
$this->adapter->on_register_fields( $registry );
|
$this->adapter->on_register_fields( $registry );
|
||||||
|
|||||||
@@ -33,21 +33,6 @@ class HivePressMarketplaceAdapterTest extends TestCase {
|
|||||||
$this->assertSame( 'HivePress\\Marketplace\\Plugin', $this->adapter->detection_class() );
|
$this->assertSame( 'HivePress\\Marketplace\\Plugin', $this->adapter->detection_class() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_on_register_fields_registers_hot_hp_price(): void {
|
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
|
||||||
$this->adapter->on_register_fields( $registry );
|
|
||||||
|
|
||||||
// Marketplace's filterable, sortable listing "price" attribute is stored as hp_price.
|
|
||||||
$price = $registry->get_field( 'hp_listing', 'hp_price' );
|
|
||||||
$this->assertSame( 'hot', $price['zone'] );
|
|
||||||
$this->assertTrue( (bool) $price['indexed'] );
|
|
||||||
$this->assertStringContainsString( 'decimal', $price['data_type'] );
|
|
||||||
|
|
||||||
// Neither key exists in hivepress-marketplace.
|
|
||||||
$this->assertNull( $registry->get_field( 'hp_listing', 'hp_purchase_price' ) );
|
|
||||||
$this->assertNull( $registry->get_field( 'hp_listing', 'hp_purchase_count' ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_score_aggregates_to_nine_eight_eight(): void {
|
public function test_score_aggregates_to_nine_eight_eight(): void {
|
||||||
// D8 = 0.9 → aggregate = (7×1.0 + 0.9)/8 × 10 = 9.875 → 9.88.
|
// D8 = 0.9 → aggregate = (7×1.0 + 0.9)/8 × 10 = 9.875 → 9.88.
|
||||||
$this->assertSame( 9.88, $this->adapter->suitability_score()['aggregate'] );
|
$this->assertSame( 9.88, $this->adapter->suitability_score()['aggregate'] );
|
||||||
|
|||||||
@@ -33,41 +33,8 @@ class HivePressMembershipsAdapterTest extends TestCase {
|
|||||||
$this->assertSame( '2.0.0', $this->adapter->minimum_addon_version() );
|
$this->assertSame( '2.0.0', $this->adapter->minimum_addon_version() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_on_register_fields_registers_three_hot_fields(): void {
|
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
|
||||||
$this->adapter->on_register_fields( $registry );
|
|
||||||
|
|
||||||
// Plan fields.
|
|
||||||
$expire_period = $registry->get_field( 'hp_membership_plan', 'hp_expire_period' );
|
|
||||||
$this->assertSame( 'hot', $expire_period['zone'] );
|
|
||||||
$this->assertStringContainsString( 'int', $expire_period['data_type'] );
|
|
||||||
|
|
||||||
$primary = $registry->get_field( 'hp_membership_plan', 'hp_primary' );
|
|
||||||
$this->assertSame( 'hot', $primary['zone'] );
|
|
||||||
$this->assertTrue( (bool) $primary['indexed'] );
|
|
||||||
|
|
||||||
// Membership field — the hottest path.
|
|
||||||
$expired = $registry->get_field( 'hp_membership', 'hp_expired_time' );
|
|
||||||
$this->assertSame( 'hot', $expired['zone'] );
|
|
||||||
$this->assertTrue( (bool) $expired['indexed'] );
|
|
||||||
$this->assertStringContainsString( 'bigint', $expired['data_type'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_doctor_check_reports_missing_tables_in_stub_env(): void {
|
|
||||||
$result = $this->adapter->doctor_check();
|
|
||||||
$this->assertFalse( $result['ok'] );
|
|
||||||
$this->assertArrayHasKey( 'hp_membership_plan', $result['details'] );
|
|
||||||
$this->assertArrayHasKey( 'hp_membership', $result['details'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_score_aggregates_to_perfect_ten(): void {
|
public function test_score_aggregates_to_perfect_ten(): void {
|
||||||
$score = $this->adapter->suitability_score();
|
$score = $this->adapter->suitability_score();
|
||||||
$this->assertSame( 10.0, $score['aggregate'] );
|
$this->assertSame( 10.0, $score['aggregate'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_migrations_lists_both_hot_modules(): void {
|
|
||||||
$mods = $this->adapter->migrations();
|
|
||||||
$this->assertContains( 'hot_hp_membership_plan', $mods );
|
|
||||||
$this->assertContains( 'hot_hp_membership', $mods );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ADR-003: post fields converge on entity groups; the Hot zone is frozen.
|
||||||
|
*
|
||||||
|
* No HivePress adapter registers zone=hot fields or contributes hot_* FSM
|
||||||
|
* modules. The fields they used to cover live in TMDO_HivePress_Entity_Groups.
|
||||||
|
*/
|
||||||
|
class NoHotZoneRegistrationTest extends TestCase {
|
||||||
|
|
||||||
|
private const ADAPTERS = array(
|
||||||
|
'TMDO_HivePress_Core_Adapter',
|
||||||
|
'TMDO_HivePress_Requests_Adapter',
|
||||||
|
'TMDO_HivePress_Bookings_Adapter',
|
||||||
|
'TMDO_HivePress_Memberships_Adapter',
|
||||||
|
'TMDO_HivePress_Marketplace_Adapter',
|
||||||
|
'TMDO_HivePress_Favorites_Adapter',
|
||||||
|
'TMDO_HivePress_Reviews_Adapter',
|
||||||
|
'TMDO_HivePress_Social_Links_Adapter',
|
||||||
|
'TMDO_HivePress_Blocks_Adapter',
|
||||||
|
'TMDO_HivePress_Seo_Adapter',
|
||||||
|
'TMDO_HivePress_Messages_Adapter',
|
||||||
|
'TMDO_HivePress_Statistics_Adapter',
|
||||||
|
'TMDO_HivePress_Tags_Adapter',
|
||||||
|
);
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
$prop = ( new ReflectionClass( TMDO_Schema_Registry::class ) )->getProperty( 'instance' );
|
||||||
|
$prop->setAccessible( true );
|
||||||
|
$prop->setValue( null, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_no_adapter_registers_hot_fields(): void {
|
||||||
|
$registry = TMDO_Schema_Registry::instance();
|
||||||
|
foreach ( self::ADAPTERS as $class ) {
|
||||||
|
( new $class() )->on_register_fields( $registry );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame( array(), $registry->get_zone_fields( 'hot' ) );
|
||||||
|
$this->assertSame( 'cold', $registry->get_field_zone( 'hp_listing', 'hp_images' ), 'cold fields stay' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_no_adapter_contributes_hot_modules(): void {
|
||||||
|
foreach ( self::ADAPTERS as $class ) {
|
||||||
|
$this->assertSame( array(), ( new $class() )->migrations(), $class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,19 +35,6 @@ class HivePressRequestsAdapterTest extends TestCase {
|
|||||||
$this->assertSame( 'hp_offer', WPDO_HivePress_Requests_Adapter::OFFER_COMMENT_TYPE );
|
$this->assertSame( 'hp_offer', WPDO_HivePress_Requests_Adapter::OFFER_COMMENT_TYPE );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_on_register_fields_registers_request_hot_columns(): void {
|
|
||||||
$registry = WPDO_Schema_Registry::instance();
|
|
||||||
$this->adapter->on_register_fields( $registry );
|
|
||||||
|
|
||||||
$drafted = $registry->get_field( 'hp_request', 'hp_drafted' );
|
|
||||||
$this->assertSame( 'hot', $drafted['zone'] );
|
|
||||||
$this->assertTrue( (bool) $drafted['indexed'] );
|
|
||||||
|
|
||||||
$expired = $registry->get_field( 'hp_request', 'hp_expired_time' );
|
|
||||||
$this->assertSame( 'hot', $expired['zone'] );
|
|
||||||
$this->assertTrue( (bool) $expired['indexed'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_registers_no_offer_shadow_table(): void {
|
public function test_registers_no_offer_shadow_table(): void {
|
||||||
// The offer shadow table was never created, so every mirror write failed.
|
// The offer shadow table was never created, so every mirror write failed.
|
||||||
$registry = WPDO_Custom_Table_Registry::instance();
|
$registry = WPDO_Custom_Table_Registry::instance();
|
||||||
@@ -69,8 +56,4 @@ class HivePressRequestsAdapterTest extends TestCase {
|
|||||||
public function test_score_aggregates_to_perfect_ten(): void {
|
public function test_score_aggregates_to_perfect_ten(): void {
|
||||||
$this->assertSame( 10.0, $this->adapter->suitability_score()['aggregate'] );
|
$this->assertSame( 10.0, $this->adapter->suitability_score()['aggregate'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_migrations_lists_only_request_hot_module(): void {
|
|
||||||
$this->assertSame( array( 'hot_hp_request' ), $this->adapter->migrations() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user