Here’s my issue. I have a custom post type that I’m attaching a custom taxonomy to. This taxonomy will allow the admin to provide search suggestions that will ensure the post is found when a user searches. This taxonomy is also used on the front-end to autocomplete the search field with these suggestions.
The system works fine, except when the term contains a forward slash, a space or other “special” characters.
<?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'
)
)
));
?>
I’ve looked into using the escaping features provided by WordPress, but haven’t had any luck there. I thought this would be a simple problem, but either my Google-fu is not strong, or I’m just not having any luck.
I’ve also verified that that Ski-in/Ski-out is the exact string stored in MySQL. I’ve looked into using esc_attr(), $wpdb->esc_like() and esc_sql() but none have an effect.
Perhaps I’m missing something obvious?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
I’m not sure if this is a bug, but it need further investigation. I’ve run a few quick tests on the name field in a tax_query, and whenever a term name has got a special character or have more than one word, the tax_query is excluded from the SQL query
TEST 1
I have use two terms here, your term Ski-in/Ski-out and one of the terms on my test site Uit die koskas. Now, if I run my custom query as follows
$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
the var_dump() of the request gives me this
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"
The tax_query is not appended to the SQL query
TEST 2
If you take a single word term with no special character, the query works. Here I’ve tested with a term called 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
This gives me the correct SQL query
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"
I’m not yet sure if this is intentional or a bug, but in the meantime you can have a look at the WP_Query source code and see how the tax_query is build. I will look into it as well in the near future
UPDATE 1
I quickly had a look through the WP_Query class before leaving for work. Towards the end of the class, WP_Query does some backward compatibility tests, and from eye value it might seem that this is where everything fails which in turn fails in appending the join clausse to the SQL query.
I cannot find a trac ticket about this issue, if there are one and anyone has a link, feel free to update my answer or post in comments.
WORKAROUND
If you need to and have to use the term name, you should create yourself a helper function using get_term_by(). You can then use the term name to get the term object and use the term ID from there to use in a tax_query
UPDATE 2
FOUND IT
The problem was not in WP_Query itself (what a mess looking through these classes). WP_Query uses WP_Tax_Query to construct the tax_query. Check the last line just before the do_action call in the parse_tax_query method in WP_Query
$this->tax_query = new WP_Tax_Query( $tax_query );
Great, moving to the WP_Tax_Query class. This class have the following method, transform_query which transforms a single query from one field to another. This is where everything breaks when you set your field parameter to name
With the field set to name, the name is sanitized using sanitize_title_for_query
$terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $query['terms'] ) ) . "'";
This removes slashes and convert empty spaces into hypens. This means Ski-in/Ski-out is converted into ski-inski-out and Uit die koskas is converted to uit-die-koskas. Because your term name is invalid, the following query which gets the term id and its children,
$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)
" );
fails and returns an empty array
array(0) {
}
CONCLUSION
IMHO, the sanitation here is wrong and should be replaced with a more appropriate method which allows for single spaces and slashes. What is purpose of having a name field if you can’t use term names properly. This sanitation kills the usage of the name field in a tax_query in the above mentioned cases.
As I already stated before, maybe the best method if you need to use term names is to create that helper function where you use get_term_by() to get the ID from a term and then use that ID in your tax_query
UPDATE 3
Thanks to @manifestphil in comments, there is a changeset #31346 out on this exact crazy over-sanitizing issue. Lets hope this get fixed in future releases
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0