b4400a68e5
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
268 lines
7.8 KiB
PHP
268 lines
7.8 KiB
PHP
<?php
|
|
/**
|
|
* Requests adapter — `hivepress-requests`.
|
|
*
|
|
* Two related models:
|
|
*
|
|
* - Request (post_type=hp_request)
|
|
* hot fields: hp_drafted (bool), hp_expired_time (unix ts)
|
|
* — same access pattern as Listing (user dashboard + expiry cron)
|
|
*
|
|
* - Offer (comment_type=hp_offer, parent post_type=hp_request)
|
|
* comment-column based; benefit comes from comment-router using
|
|
* (comment_post_ID, comment_type) covering index + dedicated shadow
|
|
* table for "approved offers per request" aggregation.
|
|
*
|
|
* Shadow table for offers (analogous to favorites pattern):
|
|
*
|
|
* CREATE TABLE wp_wpdo_comment_hp_offer (
|
|
* comment_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
|
|
* request_id BIGINT UNSIGNED NOT NULL,
|
|
* bidder_id BIGINT UNSIGNED NOT NULL,
|
|
* approved TINYINT(1) NOT NULL DEFAULT 0,
|
|
* created_at DATETIME NOT NULL,
|
|
* KEY idx_request_approved (request_id, approved),
|
|
* KEY idx_bidder (bidder_id)
|
|
* );
|
|
*
|
|
* @package WP_Data_Optimizer
|
|
* @since 3.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( 'TMDO_HivePress_Requests_Adapter' ) ) {
|
|
|
|
/**
|
|
* Requests addon adapter.
|
|
*/
|
|
final class TMDO_HivePress_Requests_Adapter implements TMDO_HivePress_Adapter {
|
|
|
|
use TMDO_HivePress_Adapter_Trait;
|
|
|
|
/** Comment_type for the Offer side of Requests. */
|
|
public const OFFER_COMMENT_TYPE = 'hp_offer';
|
|
|
|
/** Shadow table for offers (without wpdb prefix). */
|
|
public const OFFER_TABLE = 'wpdo_comment_hp_offer';
|
|
|
|
/**
|
|
* Constructor — bind register hooks via inherited trait.
|
|
*/
|
|
public function __construct() {
|
|
$this->bind_anti_eav_hooks();
|
|
}
|
|
|
|
/** Adapter slug (matches wp.org plugin slug). */
|
|
public function plugin_slug(): string {
|
|
return 'hivepress-requests';
|
|
}
|
|
|
|
/** Class probed for addon presence. */
|
|
public function detection_class(): string {
|
|
return 'HivePress\\Requests\\Plugin';
|
|
}
|
|
|
|
/** Version constant probed for addon presence. */
|
|
public function detection_const(): string {
|
|
return 'HIVEPRESS_REQUESTS_VERSION';
|
|
}
|
|
|
|
/** Minimum addon version supported. */
|
|
public function minimum_addon_version(): string {
|
|
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,
|
|
),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Declare offer shadow table.
|
|
*
|
|
* @param TMDO_Custom_Table_Registry $registry Registry singleton.
|
|
*/
|
|
public function on_register_custom_tables( TMDO_Custom_Table_Registry $registry ): void {
|
|
$this->register_custom_table(
|
|
$registry,
|
|
array(
|
|
'table_name' => self::OFFER_TABLE,
|
|
'primary_key' => 'comment_id',
|
|
'post_type_link' => 'hp_request',
|
|
'expected_columns' => array(
|
|
'comment_id' => 'BIGINT UNSIGNED NOT NULL PRIMARY KEY',
|
|
'request_id' => 'BIGINT UNSIGNED NOT NULL',
|
|
'bidder_id' => 'BIGINT UNSIGNED NOT NULL',
|
|
'approved' => 'TINYINT(1) NOT NULL DEFAULT 0',
|
|
'created_at' => 'DATETIME NOT NULL',
|
|
),
|
|
'indexes' => array(
|
|
'idx_request_approved' => '(request_id, approved)',
|
|
'idx_bidder' => '(bidder_id)',
|
|
),
|
|
'doctor_callback' => array( $this, 'doctor_check' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Mirror new hp_offer comments into the shadow table.
|
|
*/
|
|
public function on_register_event_hooks(): void {
|
|
add_action( 'wp_insert_comment', array( $this, 'mirror_offer_insert' ), 10, 2 );
|
|
add_action( 'edit_comment', array( $this, 'mirror_offer_update' ), 10, 2 );
|
|
add_action( 'deleted_comment', array( $this, 'mirror_offer_delete' ), 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Insert a hp_offer mirror row.
|
|
*
|
|
* @param int $comment_id Comment ID.
|
|
* @param object|\WP_Comment $comment Comment object.
|
|
*/
|
|
public function mirror_offer_insert( int $comment_id, $comment ): void {
|
|
if ( ! is_object( $comment ) || ( $comment->comment_type ?? '' ) !== self::OFFER_COMMENT_TYPE ) {
|
|
return;
|
|
}
|
|
global $wpdb;
|
|
$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
|
$wpdb->prepare(
|
|
'INSERT INTO `' . $wpdb->prefix . self::OFFER_TABLE . '` (comment_id, request_id, bidder_id, approved, created_at) VALUES (%d, %d, %d, %d, %s) ON DUPLICATE KEY UPDATE approved = VALUES(approved)',
|
|
$comment_id,
|
|
(int) ( $comment->comment_post_ID ?? 0 ),
|
|
(int) ( $comment->user_id ?? 0 ),
|
|
(int) ( $comment->comment_approved ?? 0 ),
|
|
$comment->comment_date ?? current_time( 'mysql' )
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Update mirror row when comment is edited (approval flip is the hot path).
|
|
*
|
|
* @param int $comment_id Comment ID.
|
|
* @param array $data Comment data array passed by edit_comment.
|
|
*/
|
|
public function mirror_offer_update( int $comment_id, $data ): void {
|
|
unset( $data );
|
|
global $wpdb;
|
|
// Re-fetch comment object for accurate state then re-mirror via insert path.
|
|
if ( function_exists( 'get_comment' ) ) {
|
|
$comment = get_comment( $comment_id );
|
|
if ( $comment ) {
|
|
$this->mirror_offer_insert( $comment_id, $comment );
|
|
}
|
|
}
|
|
unset( $wpdb );
|
|
}
|
|
|
|
/**
|
|
* Remove mirror row on hp_offer deletion.
|
|
*
|
|
* @param int $comment_id Comment ID.
|
|
* @param object|\WP_Comment $comment Comment object.
|
|
*/
|
|
public function mirror_offer_delete( int $comment_id, $comment ): void {
|
|
if ( ! is_object( $comment ) || ( $comment->comment_type ?? '' ) !== self::OFFER_COMMENT_TYPE ) {
|
|
return;
|
|
}
|
|
global $wpdb;
|
|
$wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
|
$wpdb->prefix . self::OFFER_TABLE,
|
|
array( 'comment_id' => $comment_id ),
|
|
array( '%d' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Doctor probe — request hot table + offer shadow 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',
|
|
);
|
|
}
|
|
$details = array();
|
|
$ok = true;
|
|
foreach ( array(
|
|
'hp_request_hot' => $wpdb->prefix . 'wpdo_hot_hp_request',
|
|
'hp_offer_shadow' => $wpdb->prefix . self::OFFER_TABLE,
|
|
) as $key => $table ) {
|
|
try {
|
|
$exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
|
|
$details[ $key ] = array(
|
|
'table' => $table,
|
|
'exists' => $exists,
|
|
);
|
|
if ( ! $exists ) {
|
|
$ok = false;
|
|
}
|
|
} catch ( \Throwable $e ) {
|
|
$details[ $key ] = array(
|
|
'table' => $table,
|
|
'error' => $e->getMessage(),
|
|
);
|
|
$ok = false;
|
|
}
|
|
}
|
|
return array(
|
|
'ok' => $ok,
|
|
'message' => $ok ? 'hivepress-requests: tables OK' : 'hivepress-requests: one or more tables missing',
|
|
'details' => $details,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 8-D self-score.
|
|
*/
|
|
public function suitability_score(): array {
|
|
return $this->compose_score( array() );
|
|
}
|
|
|
|
/**
|
|
* FSM modules this adapter contributes.
|
|
*
|
|
* @return array<int,string>
|
|
*/
|
|
public function migrations(): array {
|
|
return array(
|
|
'hot_hp_request',
|
|
'comment_hp_offer',
|
|
);
|
|
}
|
|
}
|
|
|
|
} // end if ( ! class_exists )
|