/************************************************************************\
|** **|
|** Allow WP_Query() search function to look for multiple keywords **|
|** in metas in addition to post_title and post_content **|
|** **|
|** By rAthus @ Arkanite **|
|** Created: 2020-08-18 **|
|** Updated: 2020-08-19 **|
|** **|
|** Just use the usual 's' argument and add a 's_meta_keys' argument **|
|** containing an array of the meta(s) key you want to search in :) **|
|** **|
|** Example : **|
|** **|
|** $args = array( **|
|** 'numberposts' => -1, **|
|** 'post_type' => 'post', **|
|** 's' => $MY_SEARCH_STRING, **|
|** 's_meta_keys' => array('META_KEY_1','META_KEY_2'); **|
|** 'orderby' => 'date', **|
|** 'order' => 'DESC', **|
|** ); **|
|** $posts = new WP_Query($args); **|
|** **|
\************************************************************************/
add_action('pre_get_posts', 'akn_meta_search_query'); // add the special search fonction on each get_posts query (this includes WP_Query())
function akn_meta_search_query($query) {
if ($query->is_search() and $query->query_vars and $query->query_vars['s'] and $query->query_vars['s_meta_keys']) { // if we are searching using the 's' argument and added a 's_meta_keys' argument
global $wpdb;
$search = $query->query_vars['s']; // get the search string
$ids = array(); // initiate array of matching post ids per searched keyword
foreach (explode(' ',$search) as $term) { // explode keywords and look for matching results for each
$term = trim($term); // remove unnecessary spaces
if (!empty($term)) { // check the the keyword is not empty
$query_posts = $wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE post_status='publish' AND ((post_title LIKE '%%%s%%') OR (post_content LIKE '%%%s%%'))", $term, $term); // search in title and content like the normal function does
$ids_posts = [];
$results = $wpdb->get_results($query_posts);
if ($wpdb->last_error)
die($wpdb->last_error);
foreach ($results as $result)
$ids_posts[] = $result->ID; // gather matching post ids
$query_meta = [];
foreach($query->query_vars['s_meta_keys'] as $meta_key) // now construct a search query the search in each desired meta key
$query_meta[] = $wpdb->prepare("meta_key='%s' AND meta_value LIKE '%%%s%%'", $meta_key, $term);
$query_metas = $wpdb->prepare("SELECT * FROM {$wpdb->postmeta} WHERE ((".implode(') OR (',$query_meta)."))");
$ids_metas = [];
$results = $wpdb->get_results($query_metas);
if ($wpdb->last_error)
die($wpdb->last_error);
foreach ($results as $result)
$ids_metas[] = $result->post_id; // gather matching post ids
$merged = array_merge($ids_posts,$ids_metas); // merge the title, content and meta ids resulting from both queries
$unique = array_unique($merged); // remove duplicates
if (!$unique)
$unique = array(0); // if no result, add a "0" id otherwise all posts will be returned
$ids[] = $unique; // add array of matching ids into the main array
}
}
if (count($ids)>1)
$intersected = call_user_func_array('array_intersect',$ids); // if several keywords keep only ids that are found in all keywords' matching arrays
else
$intersected = $ids[0]; // otherwise keep the only matching ids array
$unique = array_unique($intersected); // remove duplicates
if (!$unique)
$unique = array(0); // if no result, add a "0" id otherwise all posts will be returned
unset($query->query_vars['s']); // unset normal search query
$query->set('post__in',$unique); // add a filter by post id instead
}
}
Comments