web-dev-qa-db-fra.com

Échappement à WP_Query tax_query lorsque le terme a des caractères spéciaux

Voici mon problème. J'ai un type d'article personnalisé auquel j'attache une taxonomie personnalisée. Cette taxonomie permettra à l'administrateur de fournir des suggestions de recherche garantissant que la publication est trouvée lorsqu'un utilisateur effectue une recherche. Cette taxonomie est également utilisée sur le serveur frontal pour compléter automatiquement le champ de recherche avec ces suggestions.

Le système fonctionne correctement, sauf lorsque le terme contient une barre oblique, un espace ou d’autres caractères "spéciaux".

<?php
$keyword = 'Ski-in%2FSki-out';//Submitted via $_GET
$keyword = urldecode($keyword); // (string)'Ski-in/Ski-out'

$taxQuery = new WP_Query(array(
    'post_type' => 'ml_properties',
    'tax_query' => array(
        'relation' => 'OR',
        array(
            'taxonomy' => 'mc_tax_lifestyle',
            'field' => 'name',
            'terms' => array($keyword),
            'operator' => 'IN'
        )
    )
));
?>

J'ai étudié la possibilité d'utiliser les fonctionnalités d'échappement fournies par WordPress, mais je n'ai pas eu de chance là-bas. Je pensais que ce serait un problème simple, mais soit mon Google-fu n’est pas puissant, soit je n’ai aucune chance.

J'ai également vérifié que Ski-in/Ski-out est la chaîne exacte stockée dans MySQL. J'ai examiné l'utilisation de esc_attr(), $wpdb->esc_like() et esc_sql() mais aucune n'a d'effet.

Peut-être qu'il me manque quelque chose d'évident?

5
Philip Downer

Je ne suis pas sûr qu'il s'agisse d'un bug, mais une enquête plus approfondie est nécessaire. J'ai effectué quelques tests rapides sur le champ name dans un tax_query, et chaque fois qu'un nom de terme a un caractère spécial ou plusieurs mots, le tax_query est exclu de la requête SQL.

TEST 1

J'ai utilisé deux termes ici, votre terme Ski-in/Ski-out et l'un des termes de mon site de test Uit die koskas. Maintenant, si je lance ma requête personnalisée comme suit

$taxQuery = new \WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
        array(
            'taxonomy' => 'category',
            'field' => 'name',
            'terms' => 'Ski-in/Ski-out',
            'operator' => 'IN'
        )
    )
));
?><pre><?php var_dump($taxQuery->request); ?></pre><?php    

la var_dump() de la demande me donne ceci

string(254) "SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID 
FROM wp_posts  
WHERE 1=1  
AND ( 
      0 = 1
    ) 
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' 
OR wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 4"

Le tax_query n'est pas ajouté à la requête SQL

TEST 2

Si vous prenez un seul mot Word sans caractère spécial, la requête fonctionne. Ici, j'ai testé avec un terme appelé Ongekategoriseerd

$taxQuery = new \WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
        array(
            'taxonomy' => 'category',
            'field' => 'name',
            'terms' => 'Ongekategoriseerd',
            'operator' => 'IN'
        )
    )
));
?><pre><?php var_dump($taxQuery->request); ?></pre><?php

Cela me donne la requête SQL correcte

string(378) "SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  
INNER JOIN wp_term_relationships 
ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE 1=1  
AND ( 
      wp_term_relationships.term_taxonomy_id 
IN (1)
    ) 
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' 
OR wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 4" 

Je ne sais pas encore si c'est intentionnel ou s'il s'agit d'un bogue, mais en attendant, vous pouvez consulter le code source WP_Query et voir comment le tax_query est construit. Je vais me renseigner également dans un proche avenir

MISE À JOUR 1

J'ai rapidement parcouru la classe WP_Query avant de partir travailler. Vers la fin de la classe, WP_Query effectue des tests de compatibilité avec les versions antérieures et, à première vue, il peut sembler que c’est là que tout échoue, ce qui échoue à son tour en ajoutant le lien de jointure à la requête SQL.

Je ne trouve pas de ticket de correspondance sur ce problème. S'il y en a un et que tout le monde a un lien, n'hésitez pas à mettre à jour ma réponse ou à poster des commentaires.

WORKAROUND

Si vous devez et devez utiliser le nom de terme, vous devez créer vous-même une fonction d'assistance à l'aide de get_term_by() . Vous pouvez ensuite utiliser le terme nom pour obtenir le terme objet, puis utiliser le terme ID pour l'utiliser dans un tax_query

MISE À JOUR 2

TROUVÉ

Le problème n'était pas dans WP_Query lui-même ( quel désordre en parcourant ces classes ). WP_Query utilise WP_Tax_Query pour construire le tax_query. Vérifiez la dernière ligne juste avant l'appel do_action dans la méthode parse_tax_query dans WP_Query

$this->tax_query = new WP_Tax_Query( $tax_query );

Génial, je passe à la classe WP_Tax_Query. Cette classe a la méthode suivante, transform_query qui transforme une requête unique d'un champ à un autre. C’est là que tout se brise lorsque vous définissez votre paramètre field sur name

Si field est défini sur name, le nom est nettoyé à l’aide de sanitize_title_for_query

$terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $query['terms'] ) ) . "'";

Cela supprime les barres obliques et convertit les espaces vides en hypens. Cela signifie que Ski-in/Ski-out est converti en ski-inski-out et que Uit die koskas est converti en uit-die-koskas. Parce que votre nom de terme n'est pas valide, la requête suivante qui obtient le terme id et ses enfants,

$terms = $wpdb->get_col( "
    SELECT $wpdb->term_taxonomy.$resulting_field
    FROM $wpdb->term_taxonomy
    INNER JOIN $wpdb->terms USING (term_id)
    WHERE taxonomy = '{$query['taxonomy']}'
    AND $wpdb->terms.{$query['field']} IN ($terms)
" );

échoue et retourne un tableau vide

array(0) {
}

CONCLUSION

IMHO, l'assainissement ici est faux et devrait être remplacé par une méthode plus appropriée qui permet des espaces simples et des barres obliques. Quel est le but d'avoir un champ name si vous ne pouvez pas utiliser les noms de terme correctement. Cet assainissement supprime l'utilisation du champ name dans un tax_query dans les cas mentionnés ci-dessus.

Comme je l'ai déjà indiqué précédemment, la meilleure méthode si vous devez utiliser des noms de terme est de créer cette fonction d'assistance dans laquelle vous utilisez get_term_by() pour obtenir l'ID d'un terme, puis utilisez cet ID dans votre tax_query.

MISE À JOUR 3

Grâce à @manifestphil dans les commentaires, il existe un changeset # 31346 sur ce problème complètement fou de désinfection excessive. Espérons que cela soit corrigé dans les prochaines versions

6
Pieter Goosen