Support

Account

Home Forums ACF PRO Repeater Fields and pre_get_posts

Solved

Repeater Fields and pre_get_posts

  • I have a static front page at http://veggiemagnifique.com – a client’s blog. This shows the 5 most recent posts via two loops, one displaying the entire first post and the next displaying excerpts from posts 2-4. At the bottom of the page is a link to http://veggiemagnifique.com/blog/ labelled “load more”.

    I have been using an offset via pre_get_posts in the functions.php file of the theme to preserve pagination on this posts page (/blog) and begin it at the 6th most recent post, but have noticed that for some reason this affects all the repeater fields I have created in the CMS (via the ACF plugin), including in single posts and on the homepage. I presume that my code (below) is not targeted enough to only affect the feed on the posts page but no combination of is_front_page, is_home, is_posts_page or is_page($ID) has narrowed it down.

    There seems therefore to be a clash between pre_get_posts and have_rows.

    I created this from these guidelines: https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination

    add_action('pre_get_posts', 'myprefix_query_offset', 1 );
    function myprefix_query_offset(&$query) {
    
        if ($query->is_home()) {
            return;
        }
        $offset = 5;
        $ppp = get_option('posts_per_page');
    
        if ( $query->is_paged ) {
            $page_offset = $offset + ( ($query->query_vars['paged']-1) * $ppp );
            $query->set('offset', $page_offset );
        }
        else {
            $query->set('offset',$offset);
        }
    }
    
    add_filter('found_posts', 'myprefix_adjust_offset_pagination', 1, 2 );
    function myprefix_adjust_offset_pagination($found_posts, $query) {
        $offset = 5;
         if ($query->is_home()) {
            return $found_posts - $offset;
        }
        return $found_posts;
    }

    I currently have this function commented out in my functions.php file so that the site works properly, so we are living with the lack of offset at the blog page. Has anyone else had this problem and found a workaround for it?

  • You need to add some more detailed checking to make sure you’re modifying the correct queries because your pre_get_posts filter will be called on every query that WP performs. To do this you need to look at the values in the current query and somehow narrow it down to only the query you want to modify. You may also need to look at the specific page that’s being loaded, for example, on the front page you might do

    
    if (is_front_page() && $query->is_paged ) {
    

    checking that you’re only modifying a posts query would look something like

    
    if (!empty($query->query_vars['post_type']) && 
        $query->query_vars['post_type'] == 'post') {
    

    and you probably always want to do this because you really never want to run a pre_get_posts filter in the admin, or almost never.

    
    if (is_admin()) {
      return;
    }
    

    I’m not exactly sure what you need to do in your case, you’re going to need to figure that out.

  • thanks john, i appreciate the reply.

    i think i understand what you’re suggesting in theory but i’m struggling to put it into practice – i don’t quite understand the syntax of the code well enough i guess.

    i’m stumbling over nailing the correct query because of the fact that the blog uses a static front page and as such the query i am looking to affect is on the posts page. as far as i can tell is_home should target that (and did perfectly apart from the effect it had on the repeater fields), but of course your additional code would need to be added into that and i’m not sure exactly how.

    it doest appear that i can simply stack those if checks one above the other. do they need to be combined into a single statement?

    as far as what i’m trying to do is concerned, it really is just a case of the posts page having an initial offset of 5 and still being paginated, but without affecting repeaters.

  • To be honest, I don’t know exactly how to correct the problem your having. Usually, when I’m dealing with pre_get_posts problems I do something like this.

    
    add_action('pre_get_posts', 'my_pre_get_posts');
    function my_pre_get_posts($query) {
      print_r($query);
    }
    

    This makes a mess of the site, but then I look at the source so I can see what’s being queried and figure out how to eliminate the ones I don’t want to change or target the one I do.

    As far as the if statements, some of them can just be stacked at the beginning of the function, for example, if you never want to modify a query in the admin you can either do this

    
    add_action('pre_get_posts', 'myprefix_query_offset', 1 );
    function myprefix_query_offset(&$query) {
        if (is_admin()) {
            return;
        }
        if ($query->is_home()) {
            return;
        }
    }
    

    or this

    
    add_action('pre_get_posts', 'myprefix_query_offset', 1 );
    function myprefix_query_offset(&$query) {
        if ($query->is_home() || is_admin()) {
            return;
        }
    }
    

    I would look for a way to white list the query you want to change, for example

    
    if (!is_admin() &&
        $query->is_main_query() && 
        !empty($query->query_vars['post_type']) && 
        $query->query_vars['post_type'] == 'post') {
      // only on the main query
      // make changes to the query here
    }
    

    That would narrow it down to posts and the main query, which I think is the query you are looking to change. checking for the main query should eliminate problems with any other query on the site…… which I should have thought of several sentences ago or maybe even when I was making my original comment.

  • thanks again – makes sense. i’ve been tinkering with similar things but nothing has rectified the issue with repeater fields, no matter how precisely i target the query. even

    if (!is_admin() &&
        $query->is_main_query() && 
        !empty($query->query_vars['post_type']) && 
        $query->query_vars['post_type'] == 'post') {
    }

    doesn’t leave the repeaters alone.

    i suspect it’s something that might need changing within the plugin. ACF support have told me it’s a “known issue” but i’ve been holding out to discover some kind of workaround.

    i really appreciate your input. thank you.

  • Known issue? I’m not the developer, but it’s news to me.

    So I want to understand your setup, I want to look at it if I can find some time.

    You have a static front (home) page and on this page you are showing the first 4 blog posts.

    Then on the blog page you don’t want to show these 4 posts. How many blog posts are you showing per page on /blog/?

    In the mean time, a simple solution could be to set the posts_per_page option to 5. Show the number of posts on the home page based on the posts_per_page setting and then link to page 2 of the blog and put a 301 redirect in place to redirect page 1 of the blog to the home page. Not the best solution, but it would have the same effect.

  • hey john, thanks for the tenacity here!

    so yes, i was surprised too, but this is from the last email i got on that subject from ACF support:

    I have verified the issue with pre_get_posts. Unfortunately, there is no workaround for the same at the moment.
    The only option would be to replace the implementation of the ‘pre_get_posts’ with an alternative.
    We have had quite a number of similar cases and the developer is already aware of this issue.

    so yes, as precisely as i can – i have a static homepage at http://veggiemagnifique.com which contains two loops, one to show the most recent post in its entirety, and another to show extracts from the next four, totalling the most recent five posts.

    i have then been trying to begin the blog archives at http://veggiemagnifique.com/blog with an offset of 5, designated within wordpress as the posts page. it’s important that this query is paginated. right now it goes back to the most recent post and starts from there. adding an offset to the query as i’ve done on the homepage would break pagination, and as we’ve discovered, adding one in the way WP suggest, via pre_get_posts has so far offset all repeater fields across the site, although it has had the effect i wanted with regard to offset and pagination on the blog.

    with the exception of the homepage we’re showing 16 posts per page. this is because the layout on the category pages requires it (although i could conceivably alter the queries in the category template to keep this number there if the WP settings were changed).

    the reason i haven’t done what you suggest, although i follow the logic clearly, is because the homepage displays the first post differently to the others. my instinct was to create a bespoke loop for that. maybe there’s a better way to target only the first post in a loop?

    if i could do that i wouldn’t even need the 301 redirect i think, i could just make the posts page the homepage and it would be page 1 of the blog.

  • I have set up a test and I put just this in the functions.php file of the site

    
    
    	
    	
    	add_action('pre_get_posts', 'myprefix_query_offset', 1);
    	function myprefix_query_offset(&$query) {
    	
    		if (is_admin() || !$query->is_home() || !$query->is_main_query()) {
    			return;
    		}
    		$offset = 5;
    		$ppp = get_option('posts_per_page');
    
    		if ($query->is_paged ) {
    			$page_offset = $offset + ( ($query->query_vars['paged']-1) * $ppp );
    			$query->set('offset', $page_offset );
    		}
    		else {
    			$query->set('offset',$offset);
    		}
    	}
    

    This seems to offset the posts shown on the blog page by 5 without effecting the repeater. You can see this test here http://www.ssirdc.com.php56-18.dfw3-2.websitetestlink.com/blog-page. The first post on this page is the post that’s marked as #6. I added a repeater to this post that has 10 rows and you can see that all 10 rows are being shown. I also set the number of posts to show on a page to 16 to match yours. If you go to the second page you’ll see that it starts with post 22 (5 offset + 16 on the first page). I also added rows to the repeater and it is not effected here either.

    I’m not exactly sure what your second filter does or why it is needed

    
    
    add_filter('found_posts', 'myprefix_adjust_offset_pagination', 1, 2 );
    function myprefix_adjust_offset_pagination($found_posts, $query) {
        $offset = 5;
         if ($query->is_home()) {
            return $found_posts - $offset;
        }
        return $found_posts;
    }
    
  • john, firstly let me apologise for not thanking you sooner – for some reason i didn’t receive a notification that you had replied and have only just come back here to re-read everything because i still hadn’t found a solution.

    now: i pasted your code in place of mine, having seen your example working, and it’s working fine for me. thank you so much. the difference is simply in the way in which you’ve targeted the query, correct? with this line?

    if (is_admin() || !$query->is_home() || !$query->is_main_query()) {
    return;

    i can’t thank you enough for being so persistent in your help tracking this down.

    the second filter is designed to prevent an empty page at the very end of the paginated feed, as per this article: https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination – however, everything seems to be fine without it, so i’m going to leave it out!

  • I think that the if statement was the only thing that I really changed. I like puzzles and I wanted to test this because you said that support told you it was a known issue. If there was a problem it could effect me when I was building a site and was something that I wanted to know about.

  • well, it will certainly be of use to me in the future, too! thanks john.

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.