When querying a combination of posts and other meta fields, is there a better solution than directly modifying the WHERE value?

This question is a follow up to a question I recently asked.

I’ve implemented what seemed to be the only solution, and it seems to work (YAY!). I just need to verify that:

  1. This is really the only way to modify the query to match with a match to the (post_title OR post_content OR any one of the other meta fields desired.
  2. That this won’t mess with other queries not related to searches. The regex used below, I think, would generally only be used for search, but what chance is there of it matching some other functionality in the core or a plugin. I realize with plugins anything could happen, but I’m especially concerned about conflicts in the WP Core.
  3. I considered doing all the work in the posts_where filter, but thought it was best to rely on modifying $query as much as possible and limit direct modification of WHERE. Is that a reasonable choice?

In any event, here is the function used to modify the WHERE:

function st_posts_where( $where ){
    if( !is_admin() ){
        global $wpdb;
        $ptn = "/(\s*AND \(\(\({$wpdb->prefix}posts.post_title LIKE '%.*%'\)\s+OR\s+\(".
               "{$wpdb->prefix}posts.post_content LIKE '%.*%'\)\)\)\s+)AND(.*)/";
        $where = preg_replace( $ptn, "$1OR$2", $where );
    }
    return $where;
}
add_filter( 'posts_where' , 'st_posts_where' );

Additionally, here is the function that the original question was addressing, which is still being used alongside the above one.

function st_search_all( $query ) {
    if( !is_admin() && $query->is_search ) {
        $query->set( 'post_type', array( 'page', 'attachment' ) );
        $query->set( 'post_status', 'inherit' );
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array( 
                'key'       => '_st_plaintext',
                'value'     => $query->get('s'),
                'compare'   => 'LIKE'
            ),
            array(
                'key'       => 'Training Classes',
                'value'     => $query->get('s'),
                'compare'   => 'LIKE'
            ),
            array(
                'key'       => 'External Content',
                'value'     => $query->get('s'),
                'compare'   => 'LIKE'
            )
        ) );
    }
    return $query;
}
add_filter( 'pre_get_posts', 'st_search_all' );

All of this in combination results in the following query result which achieves the desired result:

AND (((dev_posts.post_title LIKE '%sbir%') 
  OR (dev_posts.post_content LIKE '%sbir%')))  
OR dev_posts.post_type IN ('page', 'attachment') 
AND (dev_posts.post_status="inherit") 
AND ( 
(dev_postmeta.meta_key = '_st_plaintext' 
  AND CAST(dev_postmeta.meta_value AS CHAR) LIKE '%sbir%')
OR  (mt1.meta_key = 'Training Classes' 
  AND CAST(mt1.meta_value AS CHAR) LIKE '%sbir%')
OR  (mt2.meta_key = 'External Content' 
  AND CAST(mt2.meta_value AS CHAR) LIKE '%sbir%'))

1 Answer
1

You want to make sure you’re only messing with the main query and not subsequent queries called from a plugin or in a template on the search page. In other words…

 if( !is_admin() && $query->is_search ) {

should be

 if( !is_admin() && $query->is_search && $query->is_main_query() ) {

See http://codex.wordpress.org/Function_Reference/is_main_query

Leave a Comment