0

I have a CPT and I am changing its ordering by a numeric value of a custom field. My custom field name is "ordering". The ordering of my CPT is changed like this:

$posts = get_posts(array(
    'posts_per_page' => -1,
    'post_type'      => 'staff',
    'meta_key'       => 'ordering',
    'orderby'        => 'meta_value',
    'order'          => 'ASC'
));

The ordering of CPT items is working fine but when I click a CPT item, I see the pagination is still follwoing the "Created Date" ordering format. Is there a way to match the pagination ordering the same way the CPT items are ordered?

I am using understrap_post_nav() to output my pagination.

I tried to change the ordering of my pagination with these arguments but this didn't work:

understrap_post_nav($args = array(
    'orderby'        => 'meta_value',
    'meta_key'       => 'ordering',
    'order'          => 'ASC',
    'post_type'      => 'staff',
    'posts_per_page' => -1,
));

I believe understrap_post_nav() code is coming from here:

https://github.com/understrap/understrap/blob/main/inc/template-tags.php

and this is the while loop:

https://github.com/understrap/understrap/blob/main/single.php

Here's how I am getting the posts and I am getting these post in my custom page template called about-page.php:

<?php
while ( have_posts() ) {
    the_post();
    get_template_part( 'loop-templates/content', 'page' );
}
?>
<div class="container" id="team-members-loop">                          
    <?php
    $posts = get_posts(array(
        'posts_per_page' => -1,
        'post_type'      => 'team-members',
        'meta_key'       => 'ordering',
        'orderby'        => 'meta_value',
        'order'          => 'ASC'
    ));

    if( $posts ): ?>
        
        <div class="row">
            <?php foreach( $posts as $post ):
                setup_postdata( $post );
            ?>
            <div class="col-sm-4">
                <a href="<?php the_permalink(); ?>" class="team-link">
                    <div class="card_image_container">
                        <div class="card_image" style="background-image:url(<?php the_field('thumbnail_photo') ?>)"></div>
                    </div>
                    <div class="card_details">
                        <h4 class="card-title"><?php the_title(); ?></h4>
                        <h5 class="card-desc"><?php echo the_field('title') ?></h5>
                    </div>
                </a>
            </div>
            <?php endforeach; ?>
        </div>
        <?php wp_reset_postdata(); ?>
    
    <?php endif; ?>
</div>
7
  • "I am using understrap_post_nav() to output my pagination." - what's the code in understrap_post_nav(), and how you do your loop (what's your "foreach $posts" code)?
    – Sally CJ
    Feb 15 at 4:52
  • @SallyCJ I updated my question
    – hnnnng
    Feb 15 at 16:17
  • Can you edit your question and include the actual code instead of link/URL to the code? Also, I was asking how/when/where are you making the $posts = get_posts(...) call, which I didn't see in the single.php file? So basically, please include all relevant code.
    – Sally CJ
    Feb 16 at 1:09
  • @SallyCJ, sure. Just updated my question. Thanks
    – hnnnng
    Feb 16 at 22:21
  • @SallyCJ are you referring to permalinks under settings>Custom Structure? are are you recommending custom links in the post with code? I don't know how to set the custom links for pagination links, do you have a code sample?
    – hnnnng
    Feb 18 at 19:55

1 Answer 1

1

Is there a way to match the pagination ordering the same way the CPT items are ordered?

The custom pagination function that you're using (understrap_post_nav()) is using the default functions for navigating between singular posts like previous_post_link() and next_post_link() which do not support custom (posts) queries, so custom coding is needed to achieve the pagination you're trying to have.

And one easy way I could think of is by using offset like so:

  1. In your loop in the about-page.php template, add a custom URL parameter to the post permalink.

    Just use any name that you like, but in my example (see the code at the bottom of this answer), the parameter name is current_ordering.

  2. That parameter will store the offset of the current post in the $posts array, which contains all posts since you're using 'posts_per_page' => -1 which retrieves all found posts. I.e. There'd be no LIMIT clause added to the query/SQL.

  3. Then in the single post template like single.php, make the same query in question, but query for 3 posts only starting from the post at the offset of the previous post, i.e. current_ordering - 1.

    And basically, the pagination will be generated based on the above 3 posts, and the pagination links will also need to have the current_ordering parameter which is passed to the next/previous post.

So here's a working example and just follow the steps:

  1. In the about-page.php, change the foreach( $posts as $post ) to foreach( $posts as $i => $post ) , i.e. add the post index/offset ($i).

    Then change the <?php the_permalink(); ?> to <?php my_ordering_permalink( $i ); ?>.

  2. In your functions.php file, add this which defines the above function that outputs the permalink containing the current_ordering parameter:

    /**
     * Display or retrieve the current post permalink with a current_ordering parameter added.
     *
     * @param int         $offset Required. The post's position/offset in the posts array.
     * @param WP_Post|int $post   Optional. The post object or ID. Default null for current post.
     * @param bool        $echo   Optional. Whether to echo or return the URL. Default true.
     *
     * @return void|string Void if `$echo` argument is true, the permalink URL `$echo` is false.
     */
    function my_ordering_permalink( $offset, $post = null, $echo = true ) {
        $url = add_query_arg( 'current_ordering', $offset, get_permalink( $post ) );
    
        if ( ! $echo ) {
            return $url;
        }
        echo $url;
    }
    
  3. Also in the functions file, add this which outputs the pagination: (you can customize the HTML markup later on)

    /**
     * Displays a (simple) pagination to navigate between singular posts in a custom posts query.
     *
     * @uses understrap_post_nav()
     *
     * @param string $sep    Optional. String to use between the next/previous links. Default ' '.
     * @param string $before Optional. String to use before the pagination. Default empty.
     * @param string $after  Optional. String to use after the pagination. Default empty.
     */
    function my_ordering_post_nav( $sep = ' ', $before = '', $after = '' ) {
        // Use understrap_post_nav() if the URL parameter isn't set, or if we're not on a
        // single team-members CPT post page.
        if ( ! isset( $_GET['current_ordering'] ) || ! is_singular( 'team-members' ) ) {
            return understrap_post_nav();
        }
    
        $current = get_queried_object_id();
        $offset  = $_GET['current_ordering'];
    
        $ids = get_posts( array(
            'fields'         => 'ids',
            'offset'         => max( 0, $offset - 1 ),
            'posts_per_page' => 3,
            // the rest below should be the same as the one used with get_posts() in the
            // about-page.php template
            'post_type'      => 'team-members',
            'meta_key'       => 'ordering',
            'orderby'        => 'meta_value_num',
            'order'          => 'ASC',
        ) );
    
        if ( empty( $ids ) ) {
            return;
        }
    
        $current_index = array_search( $current, $ids );
        if ( false === $current_index ) {
            return;
        }
    
        $prev_index    = $current_index + 1;
        $next_index    = $current_index - 1;
        $links         = array();
    
        if ( isset( $ids[ $prev_index ] ) ) {
            $post_id = $ids[ $prev_index ];
    
            $links[] = sprintf( '<a href="%s">&laquo; %s</a>',
                esc_url( my_ordering_permalink( $offset + 1, $post_id, false ) ),
                esc_html( get_the_title( $post_id ) )
            );
        }
    
        if ( isset( $ids[ $next_index ] ) ) {
            $post_id = $ids[ $next_index ];
    
            $links[] = sprintf( '<a href="%s">%s &raquo;</a>',
                esc_url( my_ordering_permalink( $offset - 1, $post_id, false ) ),
                esc_html( get_the_title( $post_id ) )
            );
        }
    
        echo $before . implode( $sep, $links ) . $after;
    }
    
  4. Then in your single post template, change the understrap_post_nav(); to my_ordering_post_nav();.

So that's all, but keep in mind that the query arguments in the my_ordering_post_nav() function must match the ones in the about-page.php template (the args you pass to get_posts()), except for fields, offset and posts_per_page.

Additional Notes

  1. 'posts_per_page' => -1 is equivalent to 'nopaging' => true, which can result in a huge query that can cause the site to run extremely slow if not completely down, depending on whether the database can handle the posts selection (which in your case selecting by a meta value), so use posts_per_page with a high number instead that you know would never actually going to be reached, but that can be handled by the database and that would also not cause timeouts (which can happen when looping over a large array or dataset).

  2. "I am changing its ordering by a numeric value of a custom field" — if the meta value is a number, then you should use 'orderby' => 'meta_value_num' to make sure the values are sorted as numbers and not strings. That way, 1, 3, 10, 2 would correctly be sorted into 1, 2, 3, 10 and not 1, 10, 2, 3 which is what would happen when the values are sorted as strings.

  3. get_posts() suppresses all filters by default, so I suggest you to add 'suppress_filters' => false to your args to ensure plugins can filter the query or run specific actions (for a good reason, of course).

  4. You might want to create a single post template specific to your CPT, i.e. single-team-members.php, and edit that file to use the my_ordering_post_nav() function, which means you would not need to edit the single.php file.

  5. In about-page.php, instead of simply if( $posts ), you should use if( ! empty( $posts ) ) to check or ensure that the $posts/array is not empty.

2
  • I don't know how to thank you. I would LOVE to buy you a beer for all the trouble and the sample code, it worked like a charm! THANK YOU! :D
    – hnnnng
    Feb 22 at 2:57
  • I'm glad to hear that, but I don’t drink beer, just wine 😝 (just kidding 😀)
    – Sally CJ
    Feb 22 at 5:17

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.