13

I am filtering the content with the the_content filter. Everything works perfect, excerpt that my changes are applied to custom queries as well. My changes appear in the sidebar as well if the widget uses a custom query

To counter that, I'm using is_main_query() to target the main query only, but it is not working. Changes are simply still applied to all queries through out. What is funny though, all other conditional checks like is_single() and is_category() is working if I target specific pages, except that all changes affect any other custom query on that page, whether I use is_main_query() or not

Am I missing something here. How do I apply my changes to the main query only using the the_content filter

add_filter('the_content', 'custom_content');

function custom_content($content){

    if(is_main_query()){ // << THIS IS NOT WORKING
        // My custom content that I add to the_content()    
    }
    return $content;
}
11

To be honest, the function in_the_loop() is what you are looking for:

add_filter( 'the_content', 'custom_content' );

function custom_content( $content ) {
    if ( in_the_loop() ) {
        // My custom content that I add to the_content()    
    }
    return $content;
}

What in_the_loop does is to check if global for $wp_query (that is the main query object) of the current post is -1 < $current_post < $post_count.

That happens when the main query is looping, because before loop starts, current post is -1, and after loop ends, current post is reset to -1 again.

So, if in_the_loop() is true, it means that the main query object is looping, which is what you need in this case (and is the same result of adding the action on loop_start and removing on loop_end, like the answer @ialocin wrote; in fact it works for the same reason, and got my +1).

Benefit of @ialocin's approach is when you want to target a different query object than main one, because in_the_loop() only works for main query.

  • In none of my site searches or online searched did I come across this. A hidden gem that works. Every solution uses is_main_query, really think no one tested this thoroughly. Thank you for your input, really appreciated – Pieter Goosen Sep 28 '14 at 5:37
  • 1
    @PieterGoosen Glad it works. That is a very old function, coming straight from times when is_main_query was not a thing. – gmazzap Sep 28 '14 at 5:42
  • You see, this is where I missed it, I'm not an old timer :-), joined Wordpress in 3.3. – Pieter Goosen Sep 28 '14 at 5:44
  • 2
    @G.M. would you mind adding this to your answer. This is useful info to others that might stumble upon this answer. Most people, like me, don't read comments :-) – Pieter Goosen Sep 28 '14 at 7:46
  • 1
    @PieterGoosen done :) – gmazzap Sep 28 '14 at 7:53
7

This is merely an addition to @Otto's answer. Just to make it a little bit better understandable. Basically what @Otto is saying, you have to reverse the logic, that means: if you can reliably determine the main query, then you can add - and remove - your hooking into the the_content filter.

For example the main query can reliably be recognized at the pre_get_posts action, so this would work:

function wpse162747_the_content_filter_callback( $content ) {
    return $content . 'with something appended';
}

add_action( 'pre_get_posts', 'wpse162747_pre_get_posts_callback' );
function wpse162747_pre_get_posts_callback( $query ) {
    if ( $query->is_main_query() ) {
        add_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}

As you are supposed to remove the filter when it is not longer needed, I'm thinking the loop_end action should be a good place for that and as its counterpart we can use loop_start. Which would look like this:

add_action( 'loop_start', 'wpse162747_loop_start_callback' );
function wpse162747_loop_start_callback( $query ) {
    if ( $query->is_main_query() ) {
        add_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}

add_action( 'loop_end', 'wpse162747_loop_end_callback' );
function wpse162747_loop_end_callback( $query ) {
    if ( $query->is_main_query() ) {
        remove_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}
  • Will test this tommorow. Thanks for your detailed explanation. – Pieter Goosen Sep 27 '14 at 17:00
  • My pleasure as always @PieterGoosen No hurry, but do it, because I haven't - at least not sufficient enough. – Nicolai Sep 27 '14 at 17:13
  • 1
    What if a shortcode is in use within the_content() and the shortcode starts another query that calling the_content(), reset the current post object and the loop continues? All filter will apply. Quite tricky here, not saved by the bell in_the_loop() ... Thats why I suggest, to always remove unique filters as soon as they done, as approached by @Nicolai – Jonas Lundman Dec 3 '17 at 13:33
5

You're using is_main_query() incorrectly. The global is_main_query() function returns true unless the global $wp_query variable has been redefined.

There is probably no 100% reliable way to tell, from inside a the_content filter, whether or not the current Loop you're in is the main query or not. The content filter just filters content. It doesn't have any form of ability to know what loop it is being used for.

Instead, you should add your filter when you need it, then remove it when you don't.

  • It is actually a let-down that there is no straighforward means to target the main query with the_content filter – Pieter Goosen Sep 27 '14 at 17:01
  • Well, that really is not surprising though. Like any other filter, it just filters things. It doesn't know the context surrounding when it is being called. It might not even be called from inside a proper Loop. No way for it to tell. – Otto Sep 27 '14 at 18:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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