Custom ordering of CPT items not matching with the ordering of its pagination

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.