4

I was using this code:

add_filter( 'pre_get_posts','search_only_blog_posts' );

function search_only_blog_posts( $query ) {

    if ( $query->is_search ) {

        $query->set( 'post_type', 'post' );
    }
    return $query;
}

..until I realized that it applies to pretty much any default search in WordPress (including search in post list page in admin area etc).

How could I make the search widget to only search blog posts (not custom posts, taxonomies, images etc) so that it doesn't apply to any default WP search (only widget search) ?

Or is it easier to just make my own search widget?

I would prefer to utilize everything WordPress has to offer and not to reinvent the wheel.

5 Answers 5

9

@PieterGoosen has a good description on why your pre_get_posts callback is giving you a problem.

Here's an alternative workaround to restrict the native search widget to the post post type:

/**
 * Restrict native search widgets to the 'post' post type
 */
add_filter( 'widget_title', function( $title, $instance, $id_base )
{
    // Target the search base
    if( 'search' === $id_base )
        add_filter( 'get_search_form', 'wpse_post_type_restriction' );
    return $title;
}, 10, 3 );

function wpse_post_type_restriction( $html )
{
    // Only run once
    remove_filter( current_filter(), __FUNCTION__ );

    // Inject hidden post_type value
    return str_replace( 
        '</form>', 
        '<input type="hidden" name="post_type" value="post" /></form>',
        $html 
    );
}  

where we use adjust the output of the get_search_form() function but only for the search widgets.

6
  • Quite an unorthodox method to use the widget_title filter ;-) Feb 22, 2016 at 8:59
  • yes, it comes in handy here, the widget_display_callback filter was another possibility ;-) @PieterGoosen
    – birgire
    Feb 22, 2016 at 9:12
  • 1
    Thanks for that info, never too old or too ugly to learn something ;-) Feb 22, 2016 at 9:15
  • Both are very good answers but I tend to go with shorter one that utilizes the WordPress search itself. @PieterGoosen, doesn't WP_Widget give deprecated error? I haven't found alternative online (did just few quick searches, not on top of my priority list right now).
    – N00b
    Feb 22, 2016 at 16:59
  • 1
    @PieterGoosen Ah, that explains a lot, it seems I didn't fully understand the deprecated error.. Thanks anyway. I can use your answer to fix my widgets. Two birds with one stone, bang!
    – N00b
    Feb 22, 2016 at 17:05
4

Your use of pre_get_posts is completely wrong.

  • pre_get_posts is an action, not a filter. Check the source

    do_action_ref_array( 'pre_get_posts', array( &$this ) );
    

    Yes, add_filter works because add_action calls add_filter, that is why your code will work. But as for proper usage, it is just plain wrong. If something is an action, use add_action(). It just makes sense

  • WP_Query::is_search (and WP_Query::is_search() for that matter) returns true on any query where s is passed to WP_Query. Remember, the conditional tags inside WP_Query are not set according to the URL, but according to the query vars passed to it. For the main query, the query vars passed to WP_Query will come from parsing the URL.

  • pre_get_posts alters all instances of WP_Query, query_posts and get_posts, front end and back end regardless, so you would want to target only the main query if you only would want to target the main query. Additionally, you would only want to target the front end, specially for archives and search queries.

Here is an example on how to use pre_get_posts correctly for the main query: (You can change the closure to normal spaghetti if you wish, just a note, only use closures if you're sure you would not want to remove the action later as anonymous functions can't be removed)

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin()         // Only target front end,
         && $q->is_main_query() // Only target the main query
         && $q->is_search()     // Only target the search page
    ) {
        $q->set( 'post_type', ['my_custom_post_type', 'post'] );
    }
});

To answer your question about the search widget, here is what I have found

  • The search widget simply call get_search_form()

  • There is no useful filter to specifically target the search widget. The filters available in get_search_form() will target all forms which uses get_search_form()

With the above, you would need to create your own search widget with your own custom form

You can try the following: (Modified from the core search widget, note, everything is untested)

class My_Custom_Search extends WP_Widget {
    /**
     * Sets up a new Search widget instance.
     *
     * @since 1.0.0
     * @access public
     */
    public function __construct() {
        $widget_ops = [
            'classname'   => 'widget_custom_search', 
            'description' => __( "A custom search form for your site.")
        ];
        parent::__construct( 'custom-search', _x( 'Custom search', 'My custom search widget' ), $widget_ops );
    }

    /**
     * Outputs the content for the current Search widget instance.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $args     Display arguments including 'before_title', 'after_title',
     *                        'before_widget', and 'after_widget'.
     * @param array $instance Settings for the current Search widget instance.
     */
    public function widget( $args, $instance ) {
        /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
        $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
        echo $args['before_widget'];

        if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        }

        $form = '<form role="search" method="get" class="search-form" action="' . esc_url( home_url( '/' ) ) . '">
            <label>
                <span class="screen-reader-text">' . _x( 'Search for:', 'label' ) . '</span>
                <input type="search" class="search-field" placeholder="' . esc_attr_x( 'Search &hellip;', 'placeholder' ) . '" value="' . get_search_query() . '" name="s" title="' . esc_attr_x( 'Search for:', 'label' ) . '" />
            </label>
            <input type="hidden" value="post" name="post_type" id="post_type" />
            <input type="submit" class="search-submit" value="'. esc_attr_x( 'Search', 'submit button' ) .'" />
        </form>';

        echo $form;

        echo $args['after_widget'];
    }

    /**
     * Outputs the settings form for the Search widget.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $instance Current settings.
     */
    public function form( $instance ) {
        $instance = wp_parse_args( (array) $instance, ['title' => '')];
        $title = $instance['title'];
        ?>
        <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></label></p>
        <?php
    }

    /**
     * Handles updating settings for the current Search widget instance.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $new_instance New settings for this instance as input by the user via
     *                            WP_Widget::form().
     * @param array $old_instance Old settings for this instance.
     * @return array Updated settings.
     */
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $new_instance = wp_parse_args((array) $new_instance, ['title' => '')];
        $instance['title'] = sanitize_text_field( $new_instance['title'] );
        return $instance;
    }
}
1
1

You can simply put this in your functions.php file.

function SearchFilter($query) 
{
    if (($query->is_search)&&(!is_admin())) {
        $query->set('post_type', 'post');
    }
    return $query;
}

add_filter('pre_get_posts','SearchFilter');
1
  • This worked perfectly - very nice!
    – manifestor
    Sep 19, 2018 at 14:43
0

Just Write this code on your functions.php WordPress theme.

function wpdocs_my_search_form( $form ) {
$form = '<form role="search" method="get" id="searchform" class="searchform" action="' . home_url( '/' ) . '" >
<div><label class="screen-reader-text" for="s">' . __( 'Search for:' ) . '</label>
<input type="text" value="' . get_search_query() . '" name="s" id="s" />
<input type="hidden" value="post" name="post_type" id="post_type" />
<input type="submit" id="searchsubmit" value="'. esc_attr__( 'Search' ) .'" />
</div>
</form>';

return $form;
} add_filter( 'get_search_form', 'wpdocs_my_search_form' );
1
  • Please explain why this helps … in an edit. Thanks.
    – kaiser
    Sep 16, 2017 at 9:22
-3

Please Refer this code and setting following link

2
  • Please post a real answer, not a link to some page where someone might find a solution Feb 22, 2016 at 6:54
  • Furthermore this is what the author already has, it won´t help him with anything...
    – flomei
    Feb 22, 2016 at 7:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.