pre_get_posts and is_front_page

Activity Forums Bespoke WordPress Theme & Plugin Development pre_get_posts and is_front_page

This topic contains 20 replies, has 6 voices, and was last updated by Avatar of Anthony Hortin Anthony Hortin 1 year, 6 months ago.

  • Author
    Posts
  • #984
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    So, whilst coding up a new theme, I wanted to be a good little developer and use pre_get_posts, rather than unnecessarily using new WP_Query(). After hitting my head on the desk quite a few times when my stoopid code decided not to work, I decided I needed to simplify things to try and work out where I was going wrong.

    What I found is that is_front_page() doesn’t seem to return the correct response under certain conditions. Would love to hear what you guys think and by all means, please let me know if you can see anything wrong with what I’ve done here.

    THE TEST:

    To make things nice and simple, I installed a fresh copy of WordPress 3.5.1 and added the following code to the functions.php file in TwentyTwelve.


    function twentytwelve_pre_get_posts( $query ) {
    echo 'is_main_query: ' . $query->is_main_query() . ' ';
    echo 'is_front_page: ' . is_front_page();
    }
    add_action( 'pre_get_posts', 'twentytwelve_pre_get_posts' );

    As I’m sure you’re all aware, pre_get_posts will get called for every query. So as you can see, all this does is echo out the return values for is_main_query() and is_front_page(). At some point, is_main_query() and is_front_page() should both return True.

    There were two different instances that I wanted to check. Browsing the homepage…
    (A) When the “Front Page Displays” setting (SETTINGS > READING) is set to ‘Your latest posts’
    (B) When the “Front Page Displays” setting is set to ‘A static page’

    THE RESULTS:

    This is what the above statements in pre_get_posts returned

    Front Page Displays: Your latest posts
    ———————————————
    is_main_query: True
    is_front_page: True

    ^ The above results are correct for that instance. As per the Codex, is_front_page() ‘returns TRUE when the main blog page is being displayed and the Settings->Reading->Front page displays is set to “Your latest posts”‘

    Front Page Displays: A static page
    ———————————————
    is_main_query: True
    is_front_page: False

    ^ The above results are incorrect for that instance. As per the Codex, is_front_page() also returns TRUE ‘when is set to “A static page” and the “Front Page” value is the current Page being displayed.’

    In that last example, both values should’ve returned True, like the first instance as I was viweing the ‘static page’ that I had set (ie. the homepage). This basically means that if you have a static page as your homepage, you can’t ever check if it’s the current page within your pre_get_posts function.

    Would love to hear your thoughts on this guys (& girls)! I’m thinking that this is something that really should get fixed.

    Cheers!

  • #988
    Avatar of Bronson Quick
    Bronson Quick
    Moderator

    Hey @maddisondesigns,

    I was curious about this after our chat yesterday. From what I’ve been reading is_front_page() uses is_page() and that uses wp() which hasn’t been called when pre_get_posts runs. I just looked at http://codex.wordpress.org/Plugin_API/Action_Reference#Actions_Run_During_a_Typical_Request and pre_get_posts runs twice so I’m wondering if we added an action for pre_get_posts on the wp_head action instead if that might help?

    I’m gonna have a play now and will report back my findings.

  • #989
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    Hi Anthony,

    I get the same thing. Looks like a bug to me. We should probably report it.

    I did find a workaround that works for me – basically use the following instead of is_front_page():

    if( $query->get( 'page_id' ) == get_option( 'page_on_front' ) )

    Cheers,
    Stephen

  • #990
    Avatar of Bronson Quick
    Bronson Quick
    Moderator

    I’m reporting back that that’s a #fail from me. I’m still learning how the internals of WP load when it comes to stuff like this. Nice workaround @stephencronin :)

  • #991
    Avatar of Bronson Quick
    Bronson Quick
    Moderator

    I put it on loop_start and all is well :)

    You can tell I hadn’t had my first coffee…wp_head…more like wp_head_desk ;)

  • #992
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    Nice find @stephencronin. That seems to work ok. Well done.

    Interesting idea of adding the action to loop_start, @bronsonquick. That seems to work as well. I wonder if there’s any other unseen consequences to that.

    I think Stephen’s right though. It might be an idea to report this to see if they can fix it so we don’t have to do a workaround like that. Most people are going to expect that is_front_page() works, no matter whether you’ve got a static page as your homepage or a blog.

    *On the plus side, it’s good to know that it wasn’t just my dodgy coding that was causing problems ;-)

  • #993
    Avatar of Dion Hulse
    Dion Hulse
    Participant

    To cover the above:

    Template tags / Conditional functions (such a is_front_page() and is_category()) rely upon WP_Query, pre_get_posts runs before WP_Query has been setup.

    $wp_query->is_main_query() is slightly different, It’s not based on the actual request, All as it is doing is saying “This is the WP_Query object which was instantated during the WordPress bootstrapping” – ie. It’s the main WP_Query which the page uses, and not a custom loop.

    When using pre_get_posts, you basically have to work on the Query Vars (which are loaded in the WP_Query passed to the function), $_GET, $_POST, and any other functionality which doesn’t depend upon WP_Query.
    Another way to look at it is, you’re setting up the environment for WordPress, rather than WordPress setting up the environment for you.

  • #995
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    Great, thanks @dd32.

    I’ve updated the codex page to add the following note:

    pre_get_posts runs before WP_Query has been setup. Template tags and conditional functions that rely on WP_Query will not work. This includes functions such as is_home(), is_front_page() and is_category(). Instead, you need to work directly with the query vars, which are passed to pre_get_posts as an argument ($query in examples on this page).

    The example on the page uses is_home() which I’ve tested doesn’t work, but I don’t have the appropriate substitute code to hand and I have a train to catch. I may update later, but happy for anyone else to do that.

     

  • #996
    Avatar of Dion Hulse
    Dion Hulse
    Participant

    Awesome @stephencronin ! Unfortunately a lot of people update the codex with code that isn’t 100% working, although, it might “work” in their tests.

  • #997
    Avatar of webaware
    webaware
    Participant

    More fun to add to the above: rather than echo stuff to the front end (and lose it amongst a fog of CSS-styled HTML), have a read of Debugging in WordPress and make some use of WP_DEBUG and WP_DEBUG_LOG. Much easier. (Running the site in a PHP debugger through XDebug can help too, but WP_DEBUG_LOG is usually a lot easier!)

  • #1000
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    @dd32 Regarding the codex eg using $query->is_home: It must have worked at some point, because I came across a code snippet from Otto which uses it and people saying “thanks, that works”. So the codex is probably just using an eg that used to work, which no longer works.

    I’m not sure when it changed and don’t have time to dig into it, but I’ve replicated is_home not working in 3.4.2 and 3.5.1.

    Anyway, a workaround seems to be to do the following:

    global $wp;
    if ( add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) == get_home_url() && $query->is_main_query() )
    do stuff

    That uses some code from Kovshenin to get the current URL. The query args need to be added to cater for the default permalink structure as every page looks like the home page!).

    So I’m thinking of replacing the current broken Codex example with that, but I’ll wait and see if you think that makes sense. It’s pretty complex, but it seems to work (for me at least).

    Cheers,
    Stephen

     

  • #1001
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    Ah, what a doofus I am.

    I just realised that I had the static page set, so of course $query->is_home wouldn’t work. I’ve tested it now and can confirm that $query->is_home does work and the Codex is not incorrect.

    I’ve also checked is_category and it also works. I’ve therefore ammended my edit on the Codex to:

    pre_get_posts runs before WP_Query has been setup. Some template tags and conditional functions that rely on WP_Query will not work. For example, is_front_page() will not work, although is_home() does work. In such cases, you will need to work directly with the query vars, which are passed to pre_get_posts as an argument ($query in examples on this page).

    Cheers,
    Stephen

  • #1009
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    Nice work @stephencronin!

    You’re right, is_home() does work correctly, and this was something that I originally tried as well. Unfortunately though, it doesn’t really help much with getting the front page (when it’s a static page) as doing something like !is_home() will return true for any page that isn’t a blog page.

    Also, after doing some more tests, it looks like your original workaround, the one that was checking the page_id, doesn’t work when the homepage is showing your latest posts, unfortunately.

    Yours (and Kovshenin’s) add_query_arg hack does seem to work though in both instances, when it’s your latest posts or a static page. It is pretty “hacky” though.

    I’m also just wondering, since all it’s really doing is returning the current url with the query string appended to the end and then comparing that to the “home” url, why not simply just check if the query string is empty by doing


    global $wp;
    if ( empty( $wp->query_string ) && $query->is_main_query() )
    do_something

    Seems to me that this achieves the same thing, unless I’m missing something (ie. the ‘goal’ being to find out whether the current page is in fact the homepage or not).

  • #1011
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    Hi @maddisondesigns,

    First up, my static pages method works fine when a static page is set, but triggers for every single damn page on the site when a non static home page is being used. Here it is again, but this time checking if a static page is in use:

    if( $query->get( 'page_id' ) == get_option( 'page_on_front' ) && get_option( 'page_on_front' ) )

    Next, are you looking for something that works for both static and non static home pages? If so, your code above (checking for an empty query string) seems brilliant at first glance!

    I tried to find a hole with it, but couldn’t see one. It seems to work perfectly with both static and non static home pages. I checked various admin pages, the feed, various front end pages, with both /%category%/%postname%/ and the default permalink, and I didn’t find any false postives. And yet..

    If it were okay to use, then why would the Core have code like this:

    if ( !( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_comments_popup || $this->is_robots ) )
    $this->is_home = true;

    and then more code tweaking the value after that? I guess it’s possible a plugin may add something to the query string?

    One difference I found was that is_home() returns true for both http://sitename/ and http://sitename/page/2/. That’s also the case for my static pages method. However, query_string will actually contain “paged=2″ for the second URL, so checking for an empty query_string will return false. That may be fine depending on what you’re doing, but it diverges from the is_home functionality.

    So, if that was important to me, and if I was trying to target both static and non static home pages, ie similar to if( is_home() || is_front_page() ), only through pre_get_posts, then I’d probably use the following:

    if( $query->is_main_query() && ( ( $query->get( 'page_id' ) == get_option( 'page_on_front' ) && get_option( 'page_on_front' ) ) || is_home() ) )

    although that’s ghastly long, so I might break it down into separate if statements to make it more readable.

    It’s not nearly as simple as checking for an empty query string, but may be slightly more robust. Anyway, that’s the best I can come up with tonight! Happy for that to be torn down if I’ve missed something!

    Cheers,
    Stephen

  • #1017
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    I appreciate your work on this @stephencronin. I s’pose, as you said, it all comes down to what you want to check for. Especially when it comes to examples like you mentioned, http://sitename/ and http://sitename/page/2/ and whether or not you want that second page to return true as being the font page or whether you simply want the “real/main” homepage.

    Either way you do it, it’s not nearly as neat as being able to call one simple function like is_front_page(), which is a real pity. All this is god to know though, for when the need arises next time.

  • #1018
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    Ok… After a bit more playing around, I think this is probably something that is fairly useful when checking for your homepages. It’ll allow you to determine if you’re viewing a static page or your blog posts (on the homepage).


    global $wp;
    if ( !is_admin() && $query->is_main_query() ) {
    if ( is_home() && empty( $wp->query_string ) ) {
    echo 'This displays when set to Your Latest Posts and the homepage is showing';
    }
    elseif ( ( $query->get( 'page_id' ) == get_option( 'page_on_front' ) && get_option( 'page_on_front' ) ) || empty( $wp->query_string ) ) {
    echo 'This displays when set to A Static Page and the homepage is showing.';
    echo 'It also displays for homepages with multiple pages (eg. http://sitename/page/2/)';
    }
    }

    Basically, the first IF returns true if the homepage is showing AND your settings are configured to show the latest posts.

    The second IF returns true if the homepage is showing AND your settings are configured to show a static page. It’ll also work when the homepage has multiple pages (eg. http://sitename/page/2/).

  • #1025
    Avatar of Ryan McCue
    Ryan McCue
    Keymaster

    I’d just like to point out that in pre_get_posts, you don’t want to use is_front_page() but rather $query->is_front_page() so that you’re using the query you’re operating on, rather than the global query.

    Likewise for is_home() and basically every is_*() (except is_admin()).

  • #1026
    Avatar of Stephen Cronin
    Stephen Cronin
    Participant

    Thanks Ryan,

    That’s a distinction I hadn’t picked up on! Filed away for future. Although $query->is_front_page() doesn’t work with a static home page in pre_get_posts, any more than is_front_page() does.. :(

  • #1027
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    Thanks Ryan. I think you’ll find though that you dont actually need to use $query-> for any of the is_*() functions. As Stephen pointed out as well, it doesn’t help at all in this particular situation either.

    Here’s a short post from Justin Tadlock with an example of him using pre_get_posts, is_home() and no $query->

    http://justintadlock.com/archives/2010/02/02/showing-custom-post-types-on-your-home-blog-page

  • #1035
    Avatar of Ryan McCue
    Ryan McCue
    Keymaster

    I think you’ll find though that you dont actually need to use $query-> for any of the is_*() functions.

    The only time you don’t need it is when you’re only operating on the main query, since is_*() operates on the main query. Your check still needs to be $query->is_main_query() though.

    That said: it is really bad practice not to use the method versions. So please, please, please use the method versions.

  • #1037
    Avatar of Anthony Hortin
    Anthony Hortin
    Keymaster

    I will from now on. I promise! Thanks for clarifying that :-)

You must be logged in to reply to this topic.