File: //proc/self/cwd/wp-content/themes/woodmart/inc/modules/search/query/class-search-with-synonyms.php
<?php
/**
* Search Processor class.
*
* @package woodmart
*/
namespace XTS\Modules\Search\Query;
use XTS\Singleton;
/**
* Class SearchProcessor
*
* This class processes search queries, including handling AJAX searches,
* synonyms, exclusions, and building SQL search queries.
*/
class Search_With_Synonyms extends Singleton {
/**
* Array of synonyms to be used in the search.
*
* @var array $synonyms Array of synonyms to be used in the search.
*/
private $synonyms;
/**
* Prefix used to indicate terms to be excluded from the search.
*
* @var string $exclusion_prefix Prefix used to indicate terms to be excluded from the search.
*/
private $exclusion_prefix;
/**
* Array of columns to search within.
*
* @var array $search_columns Array of columns to search within.
*/
private $search_columns;
/**
* Init.
*/
public function init() {
$this->synonyms = $this->get_synonyms();
$this->exclusion_prefix = apply_filters('wp_query_search_exclusion_prefix', '-'); // phpcs:ignore
$this->search_columns = array( 'post_title', 'post_excerpt', 'post_content' );
}
/**
* Get the synonyms from the theme options.
*
* @return array Array of synonyms.
*/
public function get_synonyms() {
$synonyms_config = woodmart_get_opt( 'search_synonyms' );
if ( empty( $synonyms_config ) ) {
return array();
}
$synonyms_lines = array_map( 'strtolower', explode( PHP_EOL, $synonyms_config ) );
$synonyms_list = array();
foreach ( $synonyms_lines as $line ) {
$parts = explode( '=', $line );
if ( count( $parts ) < 2 ) {
continue;
}
$term = str_replace( ' ', '_', trim( $parts[0] ) );
$matches = array_map( 'trim', explode( ',', $parts[1] ) );
$synonyms_list[ $term ] = $matches;
}
return $synonyms_list;
}
/**
* Get the original terms from the search term.
*
* @param string $search_term The search term.
* @return array Array of unique original terms.
*/
public function get_original_terms( $search_term ) {
return array_unique( array_filter( array_map( 'trim', explode( ',', strtolower( $search_term ) ) ) ) );
}
/**
* Generate new terms including synonyms for the original terms.
*
* @param array $original_terms Array of original search terms.
* @return array Array of new search terms including synonyms.
*/
public function generate_new_terms( $original_terms ) {
$new_terms = array();
foreach ( $original_terms as $term ) {
$new_terms[] = $term;
$synonym_term_key = str_replace( ' ', '_', trim( $term ) );
if ( isset( $this->synonyms[ $synonym_term_key ] ) ) {
foreach ( $this->synonyms[ $synonym_term_key ] as $synonym ) {
$new_terms[] = $synonym;
}
}
}
return $new_terms;
}
/**
* Apply exclusion rules to the search terms.
*
* @param array $terms Array of search terms.
* @return array Array containing include terms and exclude terms.
*/
public function apply_exclusion( $terms ) {
$include_terms = array();
$exclude_terms = array();
foreach ( $terms as $term ) {
$exclude = $this->exclusion_prefix && str_starts_with( $term, $this->exclusion_prefix );
if ( ! $exclude ) {
if ( ! in_array( '-' . $term, $exclude_terms, true ) ) {
$include_terms[] = $term;
}
} else {
$exclude_terms[] = $term;
$key = array_search( substr( $term, 1 ), $include_terms, true );
if ( false !== $key ) {
unset( $include_terms[ $key ] );
}
}
}
return array( $include_terms, $exclude_terms );
}
/**
* Build the SQL search query from the terms.
*
* @param array $terms Array of search terms.
* @param bool $is_include_terms Whether to include or exclude the terms.
* @return string The SQL search query.
*/
public function build_search_query( $terms, $is_include_terms = true ) {
global $wpdb;
$search_query = '';
foreach ( $terms as $term ) {
if ( $is_include_terms ) {
$like_op = 'LIKE';
$andor_op = 'OR';
$search_op = ' OR ';
} else {
$like_op = 'NOT LIKE';
$andor_op = 'AND';
$term = substr( $term, 1 );
$search_op = ' AND ';
}
if ( empty( $search_query ) ) {
$search_op = '';
}
$like = '%' . $wpdb->esc_like( $term ) . '%';
$search_columns_parts = array();
foreach ( $this->search_columns as $search_column ) {
$search_columns_parts[] = $wpdb->prepare( "({$wpdb->posts}.$search_column $like_op %s)", $like );
}
$search_query .= "$search_op(" . implode( " $andor_op ", $search_columns_parts ) . ')';
}
return $search_query;
}
/**
* Combine the include and exclude search queries.
*
* @param string $include_search The include search query.
* @param string $exclude_search The exclude search query.
* @return string The combined search query.
*/
public function combine_search_queries( $include_search, $exclude_search ) {
if ( ! empty( $include_search ) && ! empty( $exclude_search ) ) {
$include_search = "({$include_search})";
}
return $include_search . ( ! empty( $exclude_search ) ? ' AND ' . $exclude_search : '' );
}
/**
* Finalize the SQL search query.
*
* @param string $search The new search query.
*
* @return string The finalized search query.
*/
public function finalize_search_query( $search ) {
global $wpdb;
if ( ! empty( $search ) ) {
$search = " AND ({$search}) ";
if ( ! is_user_logged_in() ) {
$search .= " AND ({$wpdb->posts}.post_password = '') ";
}
}
return $search;
}
/**
* Extends the WordPress search query by adding a condition for synonyms search.
*
* This function adds an additional condition to the SQL query to search within specified synonyms
* and searches by their names using the `LIKE` expression.
*
* @param string $search The current SQL search query.
* @param string $search_term The search term to be used for searching within synonyms.
*
* @return string The modified SQL search query.
*/
public function extend_query( $search, $search_term ) {
if ( empty( $this->synonyms ) ) {
return $search;
}
$original_terms = $this->get_original_terms( $search_term );
$new_terms = $this->generate_new_terms( $original_terms );
list($include_terms, $exclude_terms) = $this->apply_exclusion( $new_terms );
$include_search = $this->build_search_query( $include_terms, true );
$exclude_search = $this->build_search_query( $exclude_terms, false );
$search = $this->combine_search_queries( $include_search, $exclude_search );
return $this->finalize_search_query( $search );
}
}