Improve pagination header generation

This commit is contained in:
Mike Jolley 2019-06-19 15:35:05 +01:00
parent 3a6de36db7
commit 767719c329
8 changed files with 108 additions and 139 deletions

View File

@ -40,7 +40,8 @@ class WC_REST_Product_Attribute_Terms_V1_Controller extends WC_REST_Terms_Contro
* Register the routes for terms. * Register the routes for terms.
*/ */
public function register_routes() { public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, array( register_rest_route( $this->namespace, '/' . $this->rest_base,
array(
'args' => array( 'args' => array(
'attribute_id' => array( 'attribute_id' => array(
'description' => __( 'Unique identifier for the attribute of the terms.', 'woocommerce' ), 'description' => __( 'Unique identifier for the attribute of the terms.', 'woocommerce' ),

View File

@ -9,7 +9,8 @@ namespace WooCommerce\RestApi\Controllers\Version4;
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
use \WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions; use WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* CRUD Object Controller. * CRUD Object Controller.
@ -296,42 +297,11 @@ abstract class AbstractObjectsController extends AbstractController {
$objects[] = $this->prepare_response_for_collection( $data ); $objects[] = $this->prepare_response_for_collection( $data );
} }
$page = (int) $query_args['paged']; $total = $query_results['total'];
$max_pages = $query_results['pages']; $max_pages = $query_results['pages'];
$response = rest_ensure_response( $objects ); $response = rest_ensure_response( $objects );
$response->header( 'X-WP-Total', $query_results['total'] ); $response = Pagination::add_pagination_headers( $response, $request, $total, $max_pages );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$base = $this->rest_base;
$attrib_prefix = '(?P<';
if ( strpos( $base, $attrib_prefix ) !== false ) {
$attrib_names = array();
preg_match( '/\(\?P<[^>]+>.*\)/', $base, $attrib_names, PREG_OFFSET_CAPTURE );
foreach ( $attrib_names as $attrib_name_match ) {
$beginning_offset = strlen( $attrib_prefix );
$attrib_name_end = strpos( $attrib_name_match[0], '>', $attrib_name_match[1] );
$attrib_name = substr( $attrib_name_match[0], $beginning_offset, $attrib_name_end - $beginning_offset );
if ( isset( $request[ $attrib_name ] ) ) {
$base = str_replace( "(?P<$attrib_name>[\d]+)", $request[ $attrib_name ], $base );
}
}
}
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ) );
if ( $page > 1 ) {
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $page ) {
$next_page = $page + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }

View File

@ -9,7 +9,8 @@ namespace WooCommerce\RestApi\Controllers\Version4;
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
use \WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions; use WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* Terms controller class. * Terms controller class.
@ -203,31 +204,12 @@ abstract class AbstractTermsContoller extends AbstractController {
$response[] = $this->prepare_response_for_collection( $data ); $response[] = $this->prepare_response_for_collection( $data );
} }
$response = rest_ensure_response( $response );
// Store pagination values for headers then unset for count query. // Store pagination values for headers then unset for count query.
$per_page = (int) $prepared_args['number']; $per_page = (int) $prepared_args['number'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
$response->header( 'X-WP-Total', (int) $total_terms );
$max_pages = ceil( $total_terms / $per_page ); $max_pages = ceil( $total_terms / $per_page );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$base = str_replace( '(?P<attribute_id>[\d]+)', $request['attribute_id'], $this->rest_base ); $response = rest_ensure_response( $response );
$base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $base ) ); $response = Pagination::add_pagination_headers( $response, $request, $total_terms, $max_pages );
if ( $page > 1 ) {
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $page ) {
$next_page = $page + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }

View File

@ -14,6 +14,7 @@ defined( 'ABSPATH' ) || exit;
use \WP_REST_Server; use \WP_REST_Server;
use WooCommerce\RestApi\Controllers\Version4\Requests\CustomerRequest; use WooCommerce\RestApi\Controllers\Version4\Requests\CustomerRequest;
use WooCommerce\RestApi\Controllers\Version4\Responses\CustomerResponse; use WooCommerce\RestApi\Controllers\Version4\Responses\CustomerResponse;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* REST API Customers controller class. * REST API Customers controller class.
@ -190,8 +191,6 @@ class Customers extends AbstractController {
$users[] = $this->prepare_response_for_collection( $data ); $users[] = $this->prepare_response_for_collection( $data );
} }
$response = rest_ensure_response( $users );
// Store pagination values for headers then unset for count query. // Store pagination values for headers then unset for count query.
$per_page = (int) $prepared_args['number']; $per_page = (int) $prepared_args['number'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
@ -199,6 +198,7 @@ class Customers extends AbstractController {
$prepared_args['fields'] = 'ID'; $prepared_args['fields'] = 'ID';
$total_users = $query->get_total(); $total_users = $query->get_total();
if ( $total_users < 1 ) { if ( $total_users < 1 ) {
// Out-of-bounds, run the query again without LIMIT for total count. // Out-of-bounds, run the query again without LIMIT for total count.
unset( $prepared_args['number'] ); unset( $prepared_args['number'] );
@ -206,24 +206,9 @@ class Customers extends AbstractController {
$count_query = new \ WP_User_Query( $prepared_args ); $count_query = new \ WP_User_Query( $prepared_args );
$total_users = $count_query->get_total(); $total_users = $count_query->get_total();
} }
$response->header( 'X-WP-Total', (int) $total_users );
$max_pages = ceil( $total_users / $per_page );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); $response = rest_ensure_response( $users );
if ( $page > 1 ) { $response = Pagination::add_pagination_headers( $response, $request, $total_users, ceil( $total_users / $per_page ) );
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $page ) {
$next_page = $page + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }

View File

@ -13,6 +13,7 @@ defined( 'ABSPATH' ) || exit;
use WooCommerce\RestApi\Controllers\Version4\Responses\ProductReviewResponse; use WooCommerce\RestApi\Controllers\Version4\Responses\ProductReviewResponse;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions; use WooCommerce\RestApi\Controllers\Version4\Utilities\Permissions;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* REST API Product Reviews controller class. * REST API Product Reviews controller class.
@ -242,28 +243,7 @@ class ProductReviews extends AbstractController {
} }
$response = rest_ensure_response( $reviews ); $response = rest_ensure_response( $reviews );
$response->header( 'X-WP-Total', $total_reviews ); $response = Pagination::add_pagination_headers( $response, $request, $total_reviews, $max_pages );
$response->header( 'X-WP-TotalPages', $max_pages );
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
if ( $request['page'] > 1 ) {
$prev_page = $request['page'] - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $request['page'] ) {
$next_page = $request['page'] + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }

View File

@ -11,6 +11,8 @@ namespace WooCommerce\RestApi\Controllers\Version4;
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* REST API Taxes controller class. * REST API Taxes controller class.
*/ */
@ -175,8 +177,6 @@ class Taxes extends AbstractController {
$taxes[] = $this->prepare_response_for_collection( $data ); $taxes[] = $this->prepare_response_for_collection( $data );
} }
$response = rest_ensure_response( $taxes );
// Store pagination values for headers then unset for count query. // Store pagination values for headers then unset for count query.
$per_page = (int) $prepared_args['number']; $per_page = (int) $prepared_args['number'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
@ -186,24 +186,10 @@ class Taxes extends AbstractController {
// Calculate totals. // Calculate totals.
$total_taxes = (int) $wpdb->num_rows; $total_taxes = (int) $wpdb->num_rows;
$response->header( 'X-WP-Total', (int) $total_taxes ); $max_pages = ceil( $total_taxes / $per_page );
$max_pages = ceil( $total_taxes / $per_page );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); $response = rest_ensure_response( $taxes );
if ( $page > 1 ) { $response = Pagination::add_pagination_headers( $response, $request, $total_taxes, $max_pages );
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $page ) {
$next_page = $page + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }

View File

@ -0,0 +1,81 @@
<?php
/**
* Pagination helper.
*
* Handles permission checks for endpoints.
*
* @package WooCommerce/RestApi
*/
namespace WooCommerce\RestApi\Controllers\Version4\Utilities;
defined( 'ABSPATH' ) || exit;
/**
* Permissions class.
*/
class Pagination {
/**
* Add pagination headers to a response object.
*
* @param \WP_REST_Response $response Reference to the response object.
* @param \WP_REST_Request $request The request object.
* @param int $total_items Total items found.
* @param int $total_pages Total pages found.
* @return \WP_REST_Response
*/
public static function add_pagination_headers( $response, $request, $total_items, $total_pages ) {
$response->header( 'X-WP-Total', $total_items );
$response->header( 'X-WP-TotalPages', $total_pages );
$current_page = self::get_current_page( $request );
$link_base = self::get_link_base( $request );
if ( $current_page > 1 ) {
$previous_page = $current_page - 1;
if ( $previous_page > $total_pages ) {
$previous_page = $total_pages;
}
self::add_page_link( $response, 'prev', $previous_page, $link_base );
}
if ( $total_pages > $current_page ) {
self::add_page_link( $response, 'next', ( $current_page + 1 ), $link_base );
}
return $response;
}
/**
* Get current page.
*
* @param \WP_REST_Request $request The request object.
* @return int Get the page from the request object.
*/
protected static function get_current_page( $request ) {
return (int) $request->get_param( 'page' );
}
/**
* Get base for links from the request object.
*
* @param \WP_REST_Request $request The request object.
* @return string
*/
protected static function get_link_base( $request ) {
return add_query_arg( $request->get_query_params(), rest_url( $request->get_route() ) );
}
/**
* Add a page link.
*
* @param \WP_REST_Response $response Reference to the response object.
* @param string $name Page link name. e.g. prev.
* @param int $page Page number.
* @param string $link_base Base URL.
*/
protected static function add_page_link( &$response, $name, $page, $link_base ) {
$response->link_header( $name, add_query_arg( 'page', $page, $link_base ) );
}
}

View File

@ -11,6 +11,8 @@ namespace WooCommerce\RestApi\Controllers\Version4;
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
use WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
/** /**
* REST API Webhooks controller class. * REST API Webhooks controller class.
*/ */
@ -179,29 +181,11 @@ class Webhooks extends AbstractController {
$webhooks[] = $this->prepare_response_for_collection( $data ); $webhooks[] = $this->prepare_response_for_collection( $data );
} }
$response = rest_ensure_response( $webhooks );
$per_page = (int) $prepared_args['limit'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
$total_webhooks = $results->total; $total_webhooks = $results->total;
$max_pages = $results->max_num_pages; $max_pages = $results->max_num_pages;
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); $response = rest_ensure_response( $webhooks );
$response = Pagination::add_pagination_headers( $response, $request, $total_webhooks, $max_pages );
$response->header( 'X-WP-Total', $total_webhooks );
$response->header( 'X-WP-TotalPages', $max_pages );
if ( $page > 1 ) {
$prev_page = $page - 1;
if ( $prev_page > $max_pages ) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg( 'page', $prev_page, $base );
$response->link_header( 'prev', $prev_link );
}
if ( $max_pages > $page ) {
$next_page = $page + 1;
$next_link = add_query_arg( 'page', $next_page, $base );
$response->link_header( 'next', $next_link );
}
return $response; return $response;
} }