2

I'm implementing infinite loading using the WP API, so I pull in the new posts via an API request. Because the default API response is fine with me and I only really need to filter the posts returned, I'm not creating a custom route but rather customizing the default query:

add_filter( 'rest_post_query', array( $this, 'get_posts' ), 10, 2 );

public function get_posts( $args, $req ) {    

    /*
    This function is used to retrieve posts of the 'post' and 'news' type
    */

    $cat = $req[ 'category' ];
    $page = $req[ 'page' ];  
    $tag = $req[ 'tag' ];
    $type = $req[ 'type' ]; 

    $args[ 'paged' ] = isset( $page ) ? $page : 1;
    $args[ 'posts_per_page' ] = 8;  
    $args[ 'category_name' ] = isset( $cat ) && ! empty( $cat ) ? $cat : null;
    $args[ 'tag' ] = isset( $tag ) && ! empty( $tag ) ? $tag : null;
    $args[ 'post_type' ] = 
        isset( $type ) && ! empty( $type ) ? array( $type ) : array( 'post', 'news' ); 

    return $args;

}

I have a problem with pagination, though. Imagine that I have 10 pages of results and I request page 20: the API's default behavior is to throw the following error:

{
    "code": "rest_post_invalid_page_number",
    "message": "The page number requested is larger than the number of pages available.",
    "data": {
        "status": 400
    }
}

What I would like to do is return an empty array instead, because it would be easier and more intuitive to deal with in the frontend. So I thought I'd check the max_num_pages property of the query, but I don't know where to do that.

I tried doing this:

add_action( 'pre_get_posts', array( $this, 'check_pagination_limit' ) ); 

public function check_pagination_limit( $query ) {
    if( ! is_admin() ) {
        $currentPage = $query->get('paged'); 
        $lastPage = $query->max_num_pages; 
        if( $currentPage > $lastPage ) {
            $query->set('post__in', array(0)); 
        }
    }
}

But pre_get_posts doesn't seem to work well when rest_post_query is being used... Is there any rest_ filter or hook that I can use to access the query before the response is sent?

2 Answers 2

2

pre_get_posts does fire for REST requests.

After copying your function and stepping through it, your code is actually working -- the first time. However the WP REST controller has the following bit:

        if ( $total_posts < 1 ) {
            // Out-of-bounds, run the query again without LIMIT for total count.
            unset( $query_args['paged'] );

            $count_query = new WP_Query();
            $count_query->query( $query_args );
            $total_posts = $count_query->found_posts;
        }

This re-runs the query, which in turn re-runs your function. However as you can see, it has intentionally unset the 'paged' argument, so this time when you compare $currentPage > $lastPage you are comparing 0 > 0 which is false, so your post__in argument is not set, and posts are returned. You know the rest of the story -- WordPress then catches that you can't have that page because there aren't enough posts.

You could get that parameter more directly since it is part of your GET request, like:

    if( ! is_admin()
        && isset($_GET['page']) ) {
        $currentPage = $_GET['page'];
        $lastPage = $query->max_num_pages;
        if( $currentPage > $lastPage ) {
            $query->set('post__in', array(0));
        }
    }

This seemed to work, but I didn't test it thoroughly.

To be honest, you are really swimming upstream here, and I think a better solution might be to take a hint from WordPress and just build in handling for that error response. It's nicely packaged as JSON, and you will probably want to watch for other errors too anyways.

Also, I think your function should check and make sure it is only firing on REST requests. Right now as it is written it would fire on others as well.

1
  • Yes, I soon realized that it's probably easier to handle the error and that's what I'll do, although in my opinion it would be nicer if the API returned an empty array rather than an error.
    – grazdev
    Mar 22, 2019 at 15:03
2

Note that the rest response header for naitve endpoints contains information about the number of found items and total number of pages:

X-WP-Total: 10
X-WP-TotalPages: 1

This would be available via javascript and can help with the pagination on the front-end.

Check it with e.g.

curl -I https://example.org/wp-json/wp/v2/posts

from your command line.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.