web-dev-qa-db-fra.com

Une seule requête meta_query utilisant OR au lieu de AND dans la requête WHERE de la requête

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?

1
cfx

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');
2
cfx

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 );
0
Manny Fleurmond