What is the correct way to use pre_get_post?


From your comments, it seems that you are finding it difficult to understand about the main query of wordpress and is_main_query() method In WP_Query class, so hopefully the following will help clear your doubts:

  1. Please see codex.wordpress.org/Query_Overview and learn how WordPress determines which posts or pages to display on a given page. The main query is setup in step 4 and there you will see “$wp_query class object WP_Query,

    However, that object is a copy of the actual object containing the main question, and the object is named wp_the_querymeaning

    $GLOBALS['wp_the_query'] = new WP_Query();
  2. This much is_main_query method is used to check if the query object – which is hooked to callbacks/functions – as the first parameter pre_get_posts – is the main query object or custom/secondary query object.

    // WordPress runs the pre_get_posts hook like so, where the $this is an instance
    // of the WP_Query class:
    do_action_ref_array( 'pre_get_posts', array( &$this ) );
    // So a callback/function hooked on the pre_get_posts hook like so, will receive
    // the above $this as the first (and the only) parameter:
    function my_pre_get_posts_callback( $query ) {
        // do something here
    // And inside of the above function, you can call the is_main_query METHOD if you
    // need to check whether the $query is for the main query or not:
    if ( $query->is_main_query() ) {
        // it's the main query object
    // The above `if` block is equivalent to this, but do not do this and instead, use
    // the is_main_query METHOD when inside a filter or action hook callback which is
    // passed the WP_Query object:
    global $wp_the_query; // access the main query object which should NEVER be modified
    if ( $query === $wp_the_query ) {
        // it's the main query object
  3. See also Global name function is_main_queryWhich one? call the above class method and checks whether the global variable $wp_query (mentioned in the above linked article in the codex) refers to or (still) the main query object.

    // The global is_main_query FUNCTION will always compare the global $wp_query object
    // with the global $wp_the_query object, i.e.
    // This function call:
    if ( is_main_query() ) {
        // it's the main query object
    // is equivalent to, but this is just a demo, so do not do this:
    global $wp_query, $wp_the_query;
    if ( $wp_query === $wp_the_query ) {
        // it's the main query object

Remember, a global function is a function defined in the global scope and can be called anywhere in PHP, whereas a class method is a function defined inside a class.

Plugin Name: Example Plugin
Version: 0.1

class Example_Plugin {
    public function a_class_method() {
        // do something

function a_global_function() {
    // do something

and you need Reading Here, if you haven’t:

original answer

Is $query->is_main_query() the wanted?

From pre_get_posts Documentation:

target the right query

Be aware of the queries you are turning to as you use
pre_get_posts action. Use conditional tags to target the correct query. For example, it is recommended to use the is_admin() conditional No Change the questions in the admin screen. with
$query->is_main_query() You can target the main query of a page request, conditional from the query object. The main query is used by the primary post loop that displays the main content for a post, page, or collection. Without these conditions, you may inadvertently change the query for custom loops in the sidebar, footer, or elsewhere.

So this should answer your question, but I’d like to add some additional details:

  • pre_get_posts runs for all WP_Query requests and runs too much everywhere In WordPress, for example the front-end/non-admin side of the site, the admin area (wp-admin), REST API and feed requests, and many more (at.) /favicon.ico,

    So if you are modifying the query arguments such as posts_per_page and/or post_typeSo they will be modified by default for all queries including the main WordPress query which automatically runs on page load before the template is determined.

    So, if for example post_type it’s on postSo admin pages to manage posts in other post types will always appear in posts post type!

    And thus, to prevent issues like the above from happening, make sure to target the right query.

If the purpose is to target the search page only, I’m not clear why it can’t be written differently.

For him if ( $query->is_search() )You can of course do this or use such conditionals, but remember that $query The main question might be (global $wp_query object) or a custom instance of WP_Query class, for example $query = new WP_Query( 's=foo' );So you may or may not need to test the main query depending on your specific use case or what/what your code modifies.

For your first example, however, what is in Example 2 below can certainly be done, and either of these will work, i.e. posts_per_page it’s on 5 only for the main query on the search result pages, for example at https://example.com/?s=keyword,

  • Example 1: if Here block is used exit function if current query is No the main question is No a search query.

    add_action( 'pre_get_posts', function ( $query ) {
        if ( is_admin() || !$query->is_search() || !$query->is_main_query() ) {
            return; // do nothing and exit the function
        $query->set( 'posts_per_page', 5 );
    } );
  • Example 2: if Here block is used do something Only current query Is main question and Is a search query.

    add_action( 'pre_get_posts', function ( $query ) {
        if ( $query->is_search() && $query->is_main_query() && ! is_admin() ) {
            $query->set( 'posts_per_page', 5 );
    } );

So the only difference is how you write the algorithm.

Leave a Comment