Экранирование налогового запроса WP-запроса, когда термин имеет специальный символ(ы)


Вот в чем моя проблема. У меня есть пользовательский тип записи, к которому я прикрепляю пользовательскую таксономию. Эта таксономия позволит администратору предоставлять предложения по поиску, которые гарантируют, что сообщение будет найдено при поиске пользователя. Эта таксономия также используется в интерфейсе для автоматического заполнения поля поиска этими предложениями.

Система работает нормально, за исключением случаев, когда термин содержит косую черту, пробел или другие "специальные" символы.

<?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'
        )
    )
));
?>

Я изучил возможность использования ускользающие функции, предоставляемые WordPress, но там не повезло. Я думал, что это будет простая проблема, но либо мой Google-fu не силен, либо мне просто не везет.

Я также проверил, что эта Ski-in/Ski-out является точной строкой, хранящейся в MySQL. Я изучил возможность использования esc_attr(), $wpdb->esc_like() и esc_sql(), но ни один из них не имеет эффекта.

Возможно, я упускаю что-то очевидное?

Author: Pieter Goosen, 2015-03-09

1 answers

Я не уверен, что это ошибка, но она нуждается в дальнейшем расследовании. Я провел несколько быстрых тестов в поле name в tax_query, и всякий раз, когда имя термина содержит специальный символ или содержит более одного слова, tax_query исключается из SQL-запроса

ТЕСТ 1

Здесь я использую два термина: ваш термин Ski-in/Ski-out и один из терминов на моем тестовом сайте Uit die koskas. Теперь, если я выполню свой пользовательский запрос следующим образом

$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    

var_dump() запроса дает мне это

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"

tax_query не добавляется к SQL-запросу

ТЕСТ 2

Если вы берете термин из одного слова без специального символа, запрос работает. Здесь я протестировал термин под названием 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

Это дает мне правильный SQL-запрос

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" 

Я еще не уверен, намеренно ли это или ошибка, но тем временем вы можете взглянуть на исходный код WP_Query и посмотреть, как tax_query строится. Я также рассмотрю это в ближайшее время будущее

ОБНОВЛЕНИЕ 1

Я быстро просмотрел класс WP_Query перед уходом на работу. Ближе к концу класса WP_Query выполняет некоторые тесты на обратную совместимость, и, судя по значению eye, может показаться, что именно здесь все терпит неудачу, что, в свою очередь, приводит к сбою при добавлении клаусса соединения к SQL-запросу.

Я не могу найти билет trac по этой проблеме, если он есть, и у кого-нибудь есть ссылка, не стесняйтесь обновлять мой ответ или публиковать в комментарии.

ОБХОДНОЙ ПУТЬ

Если вам нужно и нужно использовать название термина, вы должны создать себе вспомогательную функцию, используя get_term_by(). Затем вы можете использовать имя термина, чтобы получить объект термина, и использовать идентификатор термина оттуда для использования в tax_query

ОБНОВЛЕНИЕ 2

НАШЕЛ ЕГО

Проблема заключалась не в WP_Query самой по себе ( какой беспорядок, просматривая эти классы ). WP_Query использует WP_Tax_Query чтобы построить tax_query. Проверьте последнюю строку просто перед вызовом do_action метода parse_tax_query в WP_Query

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

Отлично, переходим в класс WP_Tax_Query. Этот класс имеет следующий метод, transform_query который преобразует один запрос из одного поля в другое. Здесь все ломается, когда вы устанавливаете свой параметр field в name

С field, установленным в name, имя очищается с помощью sanitize_title_for_query

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

Это удаляет косые черты и преобразует пустые пробелы в дефисы. Это означает, что Ski-in/Ski-out является преобразуется в ski-inski-out и Uit die koskas преобразуется в uit-die-koskas. Поскольку ваше имя термина недопустимо, следующий запрос, который получает идентификатор термина и его дочерние элементы,

$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)
" );

Завершается ошибкой и возвращает пустой массив

array(0) {
}

ЗАКЛЮЧЕНИЕ

ИМХО, санитария здесь неверна и должна быть заменена более подходящим методом, который допускает одиночные пробелы и косые черты. Какова цель наличия поля name, если вы не можете правильно использовать имена терминов. Эта санитария убивает использование поле name в tax_query в вышеупомянутых случаях.

Как я уже говорил ранее, возможно, лучший метод, если вам нужно использовать имена терминов, - это создать вспомогательную функцию, в которой вы используете get_term_by(), чтобы получить идентификатор из термина, а затем использовать этот идентификатор в своем tax_query

ОБНОВЛЕНИЕ 3

Благодаря @manifestphil в комментариях, есть набор изменений #31346, посвященный именно этой сумасшедшей проблеме чрезмерной очистки. Будем надеяться, что это будет исправлено в будущих выпусках

 6
Author: Pieter Goosen, 2015-03-11 14:59:20