最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

pagination - When to check for max_num_pages using Wordpress REST API?

programmeradmin1浏览0评论

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?

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?

Share Improve this question asked Mar 22, 2019 at 7:56 grazdevgrazdev 1,3333 gold badges14 silver badges26 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 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.

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/wp-json/wp/v2/posts

from your command line.

发布评论

评论列表(0)

  1. 暂无评论