How can I paginate WP_Query results when there's an offset?
Asked Answered
R

2

8

EDIT: This is the current code that I'm trying to paginate. It creates a custom query that excludes the latest post, as well as all posts from one category. Pagination works fine for the most part, but the problem is that the final link in the pagination list takes me to a blank page.

<section class="card-grid card-grid--push">
    <main class="container container--wide">

    <?php

        $current_page = get_query_var('paged');
        $current_page = max( 1, $current_page );
        $per_page = 3;
        $offset = ( $current_page - 1 ) * $per_page + 1;

        $post_list = new WP_Query(array(
            'cat'            => -15,
            'posts_per_page' => $per_page,
            'paged'          => $current_page,
            'offset'         => $offset, 
            'orderby'        => 'date', 
            'order'          => 'DESC',  
        ));

        if ( $post_list->have_posts() ):
            while ( $post_list->have_posts() ):
                $post_list->the_post();

    ?>

    <a href="<?php the_permalink(); ?>" <?php post_class('card'); ?>>
        <article class="card__content">
            <?php the_post_thumbnail('th-card'); ?>
            <div class="card__head">
                <span class="category">
                    <?php $category = get_the_category(); echo $category[0]->cat_name; ?>
                </span>
                <h2 class="card__title"><?php the_title(); ?></h2>
            </div>
        </article>
    </a>

    <?php endwhile; ?>

    <div class="pagination">

        <?php 
            echo paginate_links( array(
                'total'   => $post_list->max_num_pages,
                'current' => $current_page,
                'type'          => 'list',
                'prev_text' => '«',
                'next_text' => '»'
            ) );
        ?>

    </div>

  <?php  
        endif;
        wp_reset_postdata();
    ?>

</main>

Reich answered 12/3, 2018 at 2:52 Comment(0)
B
21

[EDIT] Here it is, fully tested and working:

$current_page = get_query_var('paged');
$current_page = max( 1, $current_page );

$per_page = 12;
$offset_start = 1;
$offset = ( $current_page - 1 ) * $per_page + $offset_start;

$post_list = new WP_Query(array(
    'cat'            => -15,
    'posts_per_page' => $per_page,
    'paged'          => $current_page,
    'offset'         => $offset, // Starts with the second most recent post.
    'orderby'        => 'date',  // Makes sure the posts are sorted by date.
    'order'          => 'DESC',  // And that the most recent ones come first.
));

// Manually count the number of pages, because we used a custom OFFSET (i.e.
// other than 0), so we can't simply use $post_list->max_num_pages or even
// $post_list->found_posts without extra work/calculation.
$total_rows = max( 0, $post_list->found_posts - $offset_start );
$total_pages = ceil( $total_rows / $per_page );

if ( $post_list->have_posts() ):
    while ( $post_list->have_posts() ):
        $post_list->the_post();


        // loop output here
    endwhile;

    echo paginate_links( array(
        'total'   => $total_pages,
        'current' => $current_page,
    ) );
endif;
wp_reset_postdata();

PS: The code was re-indented using tabs.

Bogor answered 12/3, 2018 at 4:16 Comment(12)
Thanks so much! I tried this and it sorta worked...the only problem is that last page of pagination is blank. Any idea why that would be?Bittersweet
Did you use the exact code I provided, or did you add/edit/remove any parts of it? Did you still use the offset arg?Bogor
Sorry, please ignore my previous comments (that I've deleted..). I think I know what needs to be changed. =) I'll update the code soon.Bogor
I've updated the code. Try it out. Hopefully that one works!Bogor
Hi, Sally. Thanks again for your help! Unfortunately, this is still creating a blank page at the end of the paginated results. I'll keep digging for a solution. Thanks :-)Bittersweet
In that case, it would probably be helpful if you share the actual code in the loop (i.e. the // loop output here part). Because the code I previously provided and the current one, they both worked for me. =) You can also try troubleshooting like disable all plugins (because there might be plugins that filter the query or the results), use the code with another theme, etc.Bogor
You can also add 'suppress_filters' => true to the array() passed to the WP_Query class, and see if that (i.e. suppressing filters) help solve the blank page issue. (I wonder though, is the entire page blank, or just the part with the $post_list loop?)Bogor
Let us continue this discussion in chat.Bittersweet
Apparently, I didn't actually fully test the code before this.. sorry about that and for the hassles! The updated code should work well. =)Bogor
Oh my goodness, thank you SOOO much! It's working now. You're the best! I really appreciate your taking so much time to help me and seeing it through. :-) Thank you, thank you, thank you!Bittersweet
It's my pleasure helping you, and I'm glad it finally worked! =)Bogor
Thank you for this very helpful piece of code but I had to set $offset_start = 0 to get the very first post to show up.Yachtsman
P
0

I know this might not be the solution for this but instead of using offset for highlighting a different container for a post I decided to break down the query with $count and add different wrappers instead due to the difficulty of offset pagination. Here's what I ended up using:

   <?php $args = array(
      'posts_per_page' => 13,
      'post_type'      => 'post',
      'paged'          => get_query_var( 'paged' ), 
    );
  $wp_query = new WP_Query( $args );
  $count = 0; while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
  <?php if($count == 0): ?><!-- first highlighted post we show with categories on side -->
    <div class="mb-4 flex flex-col flex-col-reverse gap-4 md:mb-10 md:flex-row md:gap-10 ">
      <aside class="flex flex-col items-baseline gap-2 no-gap:mt-4 md:max-w-[200px] md:gap-4 no-gap:md:mt-0 no-gap:md:mr-10">
        <h2 class="flex-none font-denim-semibold text-sm xs:text-lg no-gap:mb-2 no-gap:md:mb-4">Categories</h2>
          <?php 
            $terms = get_terms( 'category' );
            echo '<ul class="flex flex-wrap gap-2 no-gap:-m-1">';
            echo '<li class="no-gap:m-1"><button class="flex justify-center items-center gap-2 rounded-xl select-none min-w-fit px-3 py-1 xs:px-3.5 xs:py-2 bg-brand-black ring-1 ring-inset ring-brand-black focus-visible:outline focus-visible:outline-4 focus-visible:outline-offset-0 focus-visible:outline-brand-black/20 text-white text-xs xs:text-sm rounded-xl" tabindex="0" type="button"><span class="no-gap:ml-2 no-gap:first:ml-0">All</span></button></li>';
            foreach ( $terms as $term ) {
              $class = ( is_category( $term->name ) ) ? 'active' : ''; // assign this class if we're on the same category page as $term->name
              if($class == 'active'): 
                echo '<li class="no-gap:m-1"><a href="' . get_term_link( $term ) . '" class="' . $class . '"><button class="flex justify-center items-center gap-2 rounded-xl select-none min-w-fit px-3 py-1 xs:px-3.5 xs:py-2 bg-brand-black ring-1 ring-inset ring-brand-black focus-visible:outline focus-visible:outline-4 focus-visible:outline-offset-0 focus-visible:outline-brand-black/20 text-white text-xs xs:text-sm rounded-xl" tabindex="0" type="button"><span class="no-gap:ml-2 no-gap:first:ml-0">' . $term->name . '</span></button></a></li>';
              else:
                echo '<li class="no-gap:m-1"><a href="' . get_term_link( $term ) . '" class="' . $class . '"><button class="flex justify-center items-center gap-2 rounded-xl select-none min-w-fit px-3 py-1 xs:px-3.5 xs:py-2 bg-transparent hover:bg-brand-black/10 active:bg-brand-black ring-1 ring-inset ring-brand-black focus-visible:outline focus-visible:outline-4 focus-visible:outline-offset-0 focus-visible:outline-brand-black/20 text-brand-black active:text-white text-xs xs:text-sm rounded-xl" tabindex="0" type="button"><span class="no-gap:ml-2 no-gap:first:ml-0">' . $term->name . '</span></button></a></li>';
              endif;
            }
            echo '</ul>'; 
          ?>
      </aside>
      <a class="w-full " href="<?php the_permalink(); ?>">
          <article class="group isolate relative w-full rounded-3xl bg-brand-black shadow-lg transition overflow-hidden duration-300 overflow-border-transition will-change-contents hover:cursor-pointer hover:shadow-xl hover:scale-[1.025] active:scale-[0.975] active:shadow-md active:transform-none active:outline-none focus:outline-4 focus:outline-brand-blue focus:outline-offset-0 focus:outline">
            <div class="mx-auto flex w-60 flex-col text-center py-28 md:p-16 lg:p-28 transition duration-300 group-hover:scale-[1.025] md:w-4/5 lg:w-3/4">
              <span class="z-10 text-white opacity-70">4 min read</span>
              <h2 class="z-10 text-3xl text-white md:text-4xl "><span class="font-reckless-neue"><?php the_title(); ?></span></h2>
            </div>
            <div class="absolute right-1/2 top-1/2 -translate-y-1/2 translate-x-1/2 select-none transition duration-300 group-hover:scale-[1.05]">
              <object aria-label="Tuum blue thick wheel" class="pointer-events-none h-[1000px] min-w-[1000px] max-w-[1000px] animate-spin-slow md:h-[1250px] md:min-w-[1250px] md:max-w-[1250px] lg:h-[1500px] lg:min-w-[1500px] lg:max-w-[1500px]" data="<?php if(get_field('wheel')): ?><?php the_field('wheel'); ?><?php else: ?><?php bloginfo('template_url'); ?>/assets/img/about-us-bg.svg<?php endif; ?>" tabindex="-1" type="image/svg+xml"></object>
            </div>
          </article>
        </a>
      </div><!-- first loop -->
    <?php elseif($count == 1): ?><!-- second post we open up with new wrapper and section -->
      <section>
        <ul class="grid gap-4 md:gap-x-8 md:gap-y-10 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 pb-10 border-b-2 border-solid border-brand-beige-600">
            <?php get_template_part( 'templates/partials/component','postSmall'); ?>
          <?php else: ?><!-- from there we continue as normal -->
                <?php get_template_part( 'templates/partials/component','postSmall'); ?>
          <?php endif; ?>
          <?php $count++; endwhile; ?>
          <?php wp_reset_postdata(); ?>
        </ul><!-- close the post small boxes list -->
        <div class="navigation-wrap flex justify-center items-center gap-2 rounded-xl select-none min-w-fit tracking-normal px-7 py-4 bg-transparent ring-transparent text-brand-black text-xl rounded-xl font-denim-semibold w-full mt-4 justify-center col-span-2 md:col-span-3 lg:col-span-4">
          <?php echo paginate_links(array(
              'base' => get_pagenum_link(1) . '%_%',
              'format' => 'page/%#%',
              'prev_text' => '',
              'next_text' => ''
          )); ?>
        </div>
      </section><!-- close the small posts section wrap -->

Using Tailwind css but query tested and working to show 1st post highlighted aside with category filter and other posts in a 4post grid. Hope this helps someone struggling with this since I spent a lot of time with the offset solution and decided to still solve with a single loop for simplicity

Pinball answered 23/1, 2023 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.