| 1 |
<?php |
| 2 |
|
| 3 |
namespace WPDeveloper\BetterDocs\FrontEnd; |
| 4 |
|
| 5 |
if ( ! defined( 'ABSPATH' ) ) { |
| 6 |
exit; |
| 7 |
} |
| 8 |
|
| 9 |
/** |
| 10 |
* Extends WordPress search SQL on `docs` queries to also match docs whose |
| 11 |
* assigned `doc_tag` or `doc_category` term names contain the search term. |
| 12 |
* |
| 13 |
* The filter is registered globally so it applies to every search query against |
| 14 |
* the `docs` post type — including WP core's `GET /wp/v2/docs?search=...`, |
| 15 |
* BetterDocs' `/betterdocs/v1/search`, and the shortcode/widget AJAX paths. |
| 16 |
*/ |
| 17 |
class SearchExtender { |
| 18 |
public function __construct() { |
| 19 |
add_filter( 'posts_search', [ $this, 'extend_search' ], 20, 2 ); |
| 20 |
} |
| 21 |
|
| 22 |
/** |
| 23 |
* Inject an OR clause that matches docs whose related taxonomy term names |
| 24 |
* contain the search term. |
| 25 |
* |
| 26 |
* @param string $search The search SQL clause (already begins with " AND ("). |
| 27 |
* @param \WP_Query $query The WP_Query instance. |
| 28 |
* @return string Modified search SQL. |
| 29 |
*/ |
| 30 |
public function extend_search( $search, $query ) { |
| 31 |
global $wpdb; |
| 32 |
|
| 33 |
if ( empty( $search ) ) { |
| 34 |
return $search; |
| 35 |
} |
| 36 |
|
| 37 |
$post_type = isset( $query->query_vars['post_type'] ) ? $query->query_vars['post_type'] : ''; |
| 38 |
if ( is_array( $post_type ) ) { |
| 39 |
if ( ! in_array( 'docs', $post_type, true ) ) { |
| 40 |
return $search; |
| 41 |
} |
| 42 |
} elseif ( $post_type !== 'docs' ) { |
| 43 |
return $search; |
| 44 |
} |
| 45 |
|
| 46 |
$search_term = isset( $query->query_vars['s'] ) ? (string) $query->query_vars['s'] : ''; |
| 47 |
if ( $search_term === '' ) { |
| 48 |
return $search; |
| 49 |
} |
| 50 |
|
| 51 |
$taxonomies = [ 'doc_tag', 'doc_category' ]; |
| 52 |
$like = '%' . $wpdb->esc_like( $search_term ) . '%'; |
| 53 |
|
| 54 |
$placeholders = implode( ',', array_fill( 0, count( $taxonomies ), '%s' ) ); |
| 55 |
$args = $taxonomies; |
| 56 |
$args[] = $like; |
| 57 |
|
| 58 |
// $placeholders is a generated run of %s tokens bound via $args below; all |
| 59 |
// interpolated identifiers are $wpdb core table names, not user input. |
| 60 |
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared |
| 61 |
$subquery = $wpdb->prepare( |
| 62 |
"{$wpdb->posts}.ID IN ( |
| 63 |
SELECT DISTINCT tr.object_id |
| 64 |
FROM {$wpdb->term_relationships} tr |
| 65 |
INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id |
| 66 |
INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id |
| 67 |
WHERE tt.taxonomy IN ({$placeholders}) AND t.name LIKE %s |
| 68 |
)", |
| 69 |
$args |
| 70 |
); |
| 71 |
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared |
| 72 |
|
| 73 |
return preg_replace( '/^\s*AND\s*\(/', " AND ({$subquery} OR ", $search, 1 ); |
| 74 |
} |
| 75 |
} |
| 76 |
|