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
237 lines
6.5 KiB
PHP
237 lines
6.5 KiB
PHP
<?php
|
|
/**
|
|
* WP_Query interceptor for hp_listing meta queries.
|
|
*
|
|
* @package WP_Data_Optimizer
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* WP_Query interceptor for hp_listing meta queries.
|
|
*
|
|
* The hpct_listing_meta is a key-value table (like wp_postmeta), so each
|
|
* meta_query condition needs its own JOIN alias with a meta_key condition:
|
|
*
|
|
* LEFT JOIN wp_hpct_listing_meta AS wpdo_lm_0
|
|
* ON (wp_posts.ID = wpdo_lm_0.listing_id AND wpdo_lm_0.meta_key = 'hp_price')
|
|
* WHERE wpdo_lm_0.meta_value >= 100
|
|
*
|
|
* All hp_* and _hp_* meta keys are intercepted. Any other meta keys are
|
|
* left in the meta_query to be handled by wp_postmeta as usual.
|
|
*/
|
|
class TMDO_Listing_Meta_Query extends TMDO_Query_Interceptor_Base {
|
|
|
|
/**
|
|
* Returns the module identifier.
|
|
*
|
|
* @return string Module name.
|
|
*/
|
|
protected function get_module(): string {
|
|
return 'listing_meta';
|
|
}
|
|
|
|
/**
|
|
* Returns the post types this interceptor handles.
|
|
*
|
|
* @return string[] Post type slugs.
|
|
*/
|
|
protected function get_post_types(): array {
|
|
return array( 'hp_listing' );
|
|
}
|
|
|
|
/**
|
|
* Returns the custom table name.
|
|
*
|
|
* @return string Table name without prefix.
|
|
*/
|
|
protected function get_table(): string {
|
|
return 'hpct_listing_meta';
|
|
}
|
|
|
|
/**
|
|
* Returns the join column for this KV table.
|
|
*
|
|
* @return string Join column name.
|
|
*/
|
|
protected function get_join_column(): string {
|
|
return 'listing_id';
|
|
}
|
|
|
|
/**
|
|
* Not used — this class overrides pre_get_posts directly.
|
|
*
|
|
* @return array Empty array.
|
|
*/
|
|
protected function get_meta_key_map(): array {
|
|
return array();
|
|
}
|
|
|
|
// ── Override pre_get_posts for KV-table pattern ───────────────────────
|
|
|
|
/**
|
|
* Intercepts pre_get_posts to handle KV-table meta_query conditions.
|
|
*
|
|
* @param \WP_Query $query The WP_Query object.
|
|
* @return void
|
|
*/
|
|
public function pre_get_posts( \WP_Query $query ): void {
|
|
if ( ! $this->should_intercept( $query ) ) {
|
|
return;
|
|
}
|
|
|
|
$raw_meta_query = (array) $query->get( 'meta_query' );
|
|
if ( empty( $raw_meta_query ) ) {
|
|
return;
|
|
}
|
|
|
|
$our_clauses = array();
|
|
$remaining = array();
|
|
|
|
foreach ( $raw_meta_query as $k => $clause ) {
|
|
if ( 'relation' === $k || ! is_array( $clause ) || ! isset( $clause['key'] ) ) {
|
|
$remaining[ $k ] = $clause;
|
|
continue;
|
|
}
|
|
|
|
if ( $this->is_hp_key( $clause['key'] ) ) {
|
|
$our_clauses[] = array(
|
|
'meta_key' => $clause['key'],
|
|
'value' => $clause['value'] ?? '',
|
|
'compare' => strtoupper( trim( $clause['compare'] ?? '=' ) ),
|
|
'type' => strtoupper( trim( $clause['type'] ?? 'CHAR' ) ),
|
|
);
|
|
} else {
|
|
$remaining[ $k ] = $clause;
|
|
}
|
|
}
|
|
|
|
if ( empty( $our_clauses ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( isset( $raw_meta_query['relation'] ) && ! isset( $remaining['relation'] ) ) {
|
|
$remaining['relation'] = $raw_meta_query['relation'];
|
|
}
|
|
|
|
$query->set( 'meta_query', $remaining );
|
|
$query->set( $this->query_var(), $our_clauses );
|
|
}
|
|
|
|
// ── Override posts_join for KV-table: one JOIN alias per condition ─────
|
|
|
|
/**
|
|
* Appends LEFT JOINs for each KV-table meta_query condition.
|
|
*
|
|
* @param string $join Current JOIN SQL.
|
|
* @param \WP_Query $query The WP_Query object.
|
|
* @return string Modified JOIN SQL.
|
|
*/
|
|
public function posts_join( ?string $join, \WP_Query $query ): string {
|
|
$join = (string) ( $join ?? '' );
|
|
$clauses = $this->get_clauses( $query );
|
|
if ( empty( $clauses ) ) {
|
|
return $join;
|
|
}
|
|
|
|
global $wpdb;
|
|
$table = TMDO_DB::table( $this->get_table() );
|
|
$join_col = esc_sql( $this->get_join_column() );
|
|
|
|
foreach ( $clauses as $i => $clause ) {
|
|
$alias = $this->alias_for( $i );
|
|
$meta_key = $clause['meta_key'];
|
|
|
|
if ( false === strpos( $join, "`{$alias}`" ) ) {
|
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table from TMDO_DB::table(); $alias and $join_col are sanitize_key()-validated.
|
|
$join .= $wpdb->prepare(
|
|
" LEFT JOIN `{$table}` AS `{$alias}`"
|
|
. " ON (`{$wpdb->posts}`.`ID` = `{$alias}`.`{$join_col}`"
|
|
. " AND `{$alias}`.`meta_key` = %s)",
|
|
$meta_key
|
|
);
|
|
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
|
}
|
|
}
|
|
|
|
return $join;
|
|
}
|
|
|
|
// ── Override posts_where for KV-table ─────────────────────────────────
|
|
|
|
/**
|
|
* Appends WHERE conditions for KV-table meta_value comparisons.
|
|
*
|
|
* @param string $where Current WHERE SQL.
|
|
* @param \WP_Query $query The WP_Query object.
|
|
* @return string Modified WHERE SQL.
|
|
*/
|
|
public function posts_where( ?string $where, \WP_Query $query ): string {
|
|
$where = (string) ( $where ?? '' );
|
|
$clauses = $this->get_clauses( $query );
|
|
if ( empty( $clauses ) ) {
|
|
return $where;
|
|
}
|
|
|
|
foreach ( $clauses as $i => $clause ) {
|
|
$alias = $this->alias_for( $i );
|
|
$col = "`{$alias}`.`meta_value`";
|
|
$compare = $this->sanitize_compare( $clause['compare'] );
|
|
$type = $clause['type'];
|
|
$value = $clause['value'];
|
|
|
|
$where .= $this->build_condition( $col, $compare, $type, $value );
|
|
}
|
|
|
|
return $where;
|
|
}
|
|
|
|
// ── Override posts_groupby ─────────────────────────────────────────────
|
|
|
|
/**
|
|
* Ensures GROUP BY is set when KV-table clauses are active.
|
|
*
|
|
* @param string $groupby Current GROUP BY SQL.
|
|
* @param \WP_Query $query The WP_Query object.
|
|
* @return string Modified GROUP BY SQL.
|
|
*/
|
|
public function posts_groupby( ?string $groupby, \WP_Query $query ): string {
|
|
$groupby = (string) ( $groupby ?? '' );
|
|
$clauses = $this->get_clauses( $query );
|
|
if ( empty( $clauses ) ) {
|
|
return $groupby;
|
|
}
|
|
|
|
global $wpdb;
|
|
if ( '' === trim( $groupby ) ) {
|
|
$groupby = "`{$wpdb->posts}`.`ID`";
|
|
}
|
|
|
|
return $groupby;
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Generates a unique SQL alias for a KV-table JOIN at a given index.
|
|
*
|
|
* @param int $index Zero-based clause index.
|
|
* @return string SQL alias string.
|
|
*/
|
|
private function alias_for( int $index ): string {
|
|
return "wpdo_lm_{$index}";
|
|
}
|
|
|
|
/**
|
|
* Checks whether a meta key belongs to HivePress (hp_ or _hp_ prefix).
|
|
*
|
|
* @param string $meta_key Meta key to check.
|
|
* @return bool True if the key is an HP key.
|
|
*/
|
|
private function is_hp_key( string $meta_key ): bool {
|
|
return str_starts_with( $meta_key, 'hp_' ) || str_starts_with( $meta_key, '_hp_' );
|
|
}
|
|
}
|