Last Updated:

Add a metafield search to a regular WordPress search

A basic WordPress search looks for specified text in the title, content, and passage (in the fields , , ). Here's how to add metafield search to these basic search fields. That is, below we will solve the problem: how to add a search for the specified metafield to the standard search by records.post_titlepost_contentpost_excerpt

 

Solving the problem on hook posts_clauses

dd_filter( 'posts_clauses', 'km_metadata_search' );

# Adding a meta field search to the WordPress base search
function km_metadata_search( $clauses ){
global $wpdb;

if( ! is_search() || ! is_main_query() )
return $clauses;

$clauses['join'] .= "LEFT JOIN $wpdb->postmeta kmpm ON (ID = kmpm.post_id)";

$clauses['where'] = preg_replace(
"/OR +\( *$wpdb->posts.post_content +LIKE +('[^']+')/",
"OR (kmpm.meta_value LIKE $1) $0",
$clauses['where']
);

// if you need to search in the specified meta field
//$clauses['where'] .= $wpdb->prepare(' AND kmpm.meta_key = %s', 'my_meta_key' );

$clauses['distinct'] = 'DISTINCT';

// debug the final request
0 && add_filter( 'posts_request', function( $sql ){ die( $sql ); } );

return $clauses;
}

What is done in the code:

Step 1: Add a metadata table to the query (JOIN)

Let's expand the base posts table and add a metadata table to it. This is necessary in order to be able to specify the field of the metafield table in which you want to search. Specify the field in the second step.

Step 2: Change the selection in the query (WHERE)

Let's add the WHERE part of the query so that the "standard search" in addition also searches for all metafields of posts.

Step 3: Change the selection in the query (DISTINCT)

Because of LEFT JOIN, duplicates may appear in Step 1. Let's get rid of them.

Solving the problem on hooks: posts_join, posts_where, posts_distinct

add_filter( 'posts_clauses', 'km_metadata_search' );

# Adding a meta field search to the WordPress base search
function km_metadata_search( $clauses ){
global $wpdb;

if( ! is_search() || ! is_main_query() )
return $clauses;

$clauses['join'] .= "LEFT JOIN $wpdb->postmeta kmpm ON (ID = kmpm.post_id)";

$clauses['where'] = preg_replace(
"/OR +\( *$wpdb->posts.post_content +LIKE +('[^']+')/",
"OR (kmpm.meta_value LIKE $1) $0",
$clauses['where']
);

// if you need to search in the specified meta field
//$clauses['where'] .= $wpdb->prepare(' AND kmpm.meta_key = %s', 'my_meta_key' );

$clauses['distinct'] = 'DISTINCT';

// debug the final request
0 && add_filter( 'posts_request', function( $sql ){ die( $sql ); } );

return $clauses;
}

Filters will not work if the suppress_filters parameter is enabled for the query.

For a request get_posts() the filter will not work - there this parameter is enabled by default!