Je souhaite que l’entrée de recherche WordPress native recherche les métadonnées en plus de son titre standard et de la recherche de contenu. Cependant, définir un meta_query
ajoute une clause AND
à la demande lorsque j’ai besoin d’une OR
.
Ma fonction:
function search_faqs_metadata($query) {
if(!is_admin() && $query->is_main_query()) {
if($query->is_search) {
$query->set('meta_query', array(array(
'key' => '_faqs',
'value' => $query->query_vars['s'],
'compare' => 'LIKE'
)));
}
}
}
add_action('pre_get_posts', 'search_faqs_metadata');
Le problème est la requête qu'il génère:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE 1=1
AND (
(
(wp_posts.post_title LIKE '%foobar%') OR (wp_posts.post_content LIKE '%foobar%')
)
)
AND (wp_posts.post_password = '')
AND wp_posts.post_type IN ('post', 'page', 'attachment')
AND (wp_posts.post_status = 'publish')
AND ( //I need this to be an OR!
(wp_postmeta.meta_key = '_faqs' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%foobar%')
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_title LIKE '%foobar%' DESC, wp_posts.post_date DESC LIMIT 0, 10
Donc, je ne trouve pas de correspondance sauf si le titre/le contenu de la publication ET, les métadonnées correspondent au terme recherché.
Je comprends qu’il existe un paramètre relation
pour le meta_query
, mais il ne fonctionne que lorsqu’on compare plusieurs valeurs de métadonnées.
Actuellement, je suis en train d'exécuter str_replace()
sur la dernière requête pour changer la dernière AND
en une OR
. J'ai choisi cette méthode parce que, bien que hacky, il dépend de la quantité de code que je dois ajouter pour que cet ajustement réussisse:
function search_where_or_metadata($where) {
if(is_search()) {
global $wp_query;
$new_where = $where;
$new_where = str_replace('AND ( (wp_postmeta.meta_key', 'OR ( (wp_postmeta.meta_key', $where);
return $new_where;
}
return $where;
}
add_filter('posts_where', 'search_where_or_metadata');
EDIT Voici une meilleure fonction utilisant un filtre plus approprié qui change la AND
en OR
:
function change_meta_key_where_from_and_to_or($sql) {
if(is_search()) {
$sql['where'] = substr($sql['where'], 1, 3) == 'AND' ? substr_replace($sql['where'], 'OR', 1, 3) : $sql['where'];
}
return $sql;
}
add_filter('get_meta_sql', 'change_meta_key_where_from_and_to_or');
Existe-t-il un meilleur moyen de transformer cette AND
finale en OR
?
Je crois que la méthode la plus courte et la meilleure consiste à (1) modifier la requête:
function search_faqs_metadata($query) {
if(!is_admin() && $query->is_main_query()) {
if($query->is_search) {
$query->set('meta_query', array(array(
'key' => '_faqs',
'value' => $query->query_vars['s'],
'compare' => 'LIKE'
)));
}
}
}
add_action('pre_get_posts', 'search_faqs_metadata');
puis (2) remplacez la AND
par la OR
via le filtre get_meta_sql
:
function change_meta_key_where_from_and_to_or($sql) {
if(is_search()) {
$sql['where'] = substr($sql['where'], 1, 3) == 'AND' ? substr_replace($sql['where'], 'OR', 1, 3) : $sql['where'];
}
return $sql;
}
add_filter('get_meta_sql', 'change_meta_key_where_from_and_to_or');
J'ai trouvé un tutoriel qui pourrait faire l'affaire: http://www.deluxeblogtips.com/2012/04/search-all-custom-fields.html
Le Gist de base consiste à utiliser des requêtes mySQL personnalisées pour trouver les identifiants de publication des publications avec la méta appropriée, puis alimenter ces identifiants dans la requête:
function wpse143477_parse_clauses($clauses, $query) {
if(!is_admin() && $query->is_main_query()) {
if($query->is_search) {
$keyword = $query->get('s');
$keyword = '%' . like_escape( $keyword ) . '%';
$post_ids = $wpdb->get_col( $wpdb->prepare( "
SELECT DISTINCT post_id FROM {$wpdb->postmeta}
WHERE meta_key = '_faqs' AND meta_value LIKE '%s'
", $keyword ) );
if( !empty( $post_ids )) {
$clauses['where'] .= " OR {$wpdb->posts}.ID IN (" . implode( ', ', $post_ids ) . ')';
}
}
}
return $clauses;
}
add_filter( 'posts_clauses', 'wpse143477_parse_clauses', 10, 2 );