Next in my quest to extend WordPress is altering how WordPress uses the the $sentence
variable in posts_search
to include an additional string, when the $sentence
variable meets a certain criteria.
The specific usecase here is that when someone types in something like TL123
it should also search for TL-123
, with TL%
being the wildcard here. This is to cut-down on search mistakes (for those that do not include the hyphen).
I've seen how we can filter into posts_search
with additional SQL queries (I'm actually using this one with pretty good success so far), but am a little fuzzy on how this could work. Any help would be greatly appreciated. Thanks!
Edit - to include more information about needs thanks to @kaiser
- Searches are typically done by typing in TL123, when in reality the actual post title is TL-123 in most cases - so, the goal here is to intercept when the search query includes TL(NUMBER) and also search TL-(NUMBER).
Again, the trick here is that some posts do not include TL- but only TL, so that's why I'm looking for search queries that match that pattern to also search an additional "partial phrase".
Thanks!
Update
Alright, so based off of kaiser's starter function, I have come up with the following:
function wpse66815_search_query_string( $search, &$wp_query )
{
if (!is_admin() && is_search()) {
print_r($search);
global $wp_query;
// get search term
$search_term = array_shift($wp_query->query_vars['search_terms']);
// specify string we'll use to replace
$replace_var = 'TL';
// find matches for that string
preg_match_all("/{$replace_var}(?:[^0-9]*)(\d+)/i", $search_term, $out);
// if there's no matches, return the normal search
if ( empty($out[0]) )
return $search;
// find/generate the search term with the replacement
$modified_search_term = preg_replace("/{$replace_var}(?:[^0-9]*)(\d+)/i", "{$replace_var}-$1", $search_term);
// combine both the regular and modified search term
$new_search[] = $search_term;
$new_search[] = $modified_search_term;
//var_dump($new_search);
// generate the new search query
foreach ( $new_search as $keyword )
{
$new_string_parts[] = $GLOBALS['wpdb']->prepare(
"
AND ((%s.post_title LIKE '%%%s%%') OR (%s.post_content LIKE '%%%s%%'))
"
,"{$GLOBALS['wpdb']->prefix}posts"
,like_escape( $keyword )
,"{$GLOBALS['wpdb']->prefix}posts"
,like_escape( $keyword )
);
}
// set $search equal to results
$search = implode( " ", $new_string_parts );
//print_r($search);
}
return $search;
}
add_filter('posts_search', 'wpse66815_search_query_string',500,2);
The part that I can't get past is actually is the actual SQL query -> error below:
WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.post_title LIKE 'tl123') OR ('wp__posts'.post_content LIKE 'tl123')) ' at line 2]
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp__posts'.post_title LIKE 'tl123') OR ('wp__posts'.post_content LIKE 'tl123')) AND (('wp__posts'.post_title LIKE 'TL-123') OR ('wp__posts'.post_content LIKE 'TL-123')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80
Any pointers on where I went wrong? Thanks!
Update #2
I've started to see a few issues:
- It was doing
wp__posts
instead ofwp_posts
(I updated that above) - As
kaiser
mentioned, theLIKE %s
portion may need to beLIKE %%s%
but that is not parsing correctly.
Error is now:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp_posts'.post_title LIKE 'tl123') OR ('wp_posts'.post_content LIKE 'tl123')) AND (('wp_posts'.post_title LIKE 'TL-123') OR ('wp_posts'.post_content LIKE 'TL-123')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80
The error above (from what I can tell) is actually trying to look for a post that is both tl123
and TL-123
, but I think OR
is what I want (as I want to return posts in an either or situation).
Update #3
Updated function to properly escape %s
, so error is now:
WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.post_title LIKE '%tl123%') OR ('wp_posts'.post_content LIKE '%tl123%')) ' at line 2]
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp_posts'.post_title LIKE '%tl123%') OR ('wp_posts'.post_content LIKE '%tl123%')) AND (('wp_posts'.post_title LIKE '%TL-123%') OR ('wp_posts'.post_content LIKE '%TL-123%')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80
Update #4
Here's what I ended up doing -> the foreach wasn't wrapping correctly (with the AND/OR operator as @kaiser mentioned), so I separated them out into their own array insertions.
function wpse66815_search_query_string( $search, &$wp_query )
{
if (!is_admin() && is_search()) {
//print_r($search);
global $wp_query,$wpdb;
// get search term
$search_term = array_shift($wp_query->query_vars['search_terms']);
// specify string we'll use to replace
$replace_var = 'TL';
// find matches for that string
preg_match_all("/{$replace_var}(?:[^0-9]*)(\d+)/i", $search_term, $out);
// if there's no matches, return the normal search
if ( empty($out[0]) )
return $search;
// find/generate the search term with the replacement
$modified_search_term = preg_replace("/{$replace_var}(?:[^0-9]*)(\d+)/i", "{$replace_var}-$1", $search_term);
// combine both the regular and modified search term
$new_search[] = $search_term;
$new_search[] = $modified_search_term;
var_dump($new_search);
// generate the new search query
$new_string_parts[] = $wpdb->prepare( "AND ((({$wpdb->posts}.post_title LIKE '%%%s%%') OR ({$wpdb->posts}.post_content LIKE '%%%s%%'))",like_escape( $new_search[0] ),like_escape( $new_search[0] ));
$new_string_parts[] = $wpdb->prepare( "OR (({$wpdb->posts}.post_title LIKE '%%%s%%') OR ({$wpdb->posts}.post_content LIKE '%%%s%%')))",like_escape( $new_search[1] ),like_escape( $new_search[1] ));
// set $search equal to results
$search = implode( " ", $new_string_parts );
//print_r($search);
}
return $search;
}
add_filter('posts_search', 'wpse66815_search_query_string',500,2);
It does have some trouble if someone were to type in tl123 tl456
(two occurences of the tl
keyword), but I'm working on that portion. Thanks!
LIKE
escaped bylike_escape()
.$sentence
variable). I definitely want to learn, but we sometimes all need a nudge (more like a shove here). Thanks!AB123CD-45
, then theLIKE´ parst would be
array( 'AB', '123', 'CD', '45' );`? In other words: Split the search query string in numbers and letters?$sentence
hasTL
(then a number), likeTL123
, no matter what else is attached in the$sentence
, it should also search forTL-123
. For example,TL123 Supercallafrajalistic Expialadocious
would also searchTL-123 Supercallafrajalistic Expialadocious
orTL123withwords
will also searchTL-123withwords
.TLmoreletters
would only searchTLmoreletters
(as that could be a regular word)'wp_posts'
and the brackets around AND/OR.