Skip to content

Commit b14837c

Browse files
ryellentsekouras
andauthored
Pattern Directory API: Add support for pagination parameters (#45293)
* Pattern Directory API: Add support for pagination parameters * Fix linter & php compat issues * Remove the 6.0 filter * Mirror GB 6.0 to also pass through the gutenberg version * Add 'per_page', 'page', 'offset', 'order', and 'orderby' to collection params * Add initial tests for new query parameters * Bump the default per_page to 100 to match w.org API * Update function name * Fix linter issues * remove obsolete `get_items` Co-authored-by: ntsekouras <ntsekouras@outlook.com>
1 parent 1386ec3 commit b14837c

6 files changed

+375
-79
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
/**
3+
* REST API: Gutenberg_REST_Pattern_Directory_Controller_6_0 class
4+
*
5+
* @package Gutenberg
6+
* @subpackage REST_API
7+
*/
8+
9+
/**
10+
* Controller which provides REST endpoint for block patterns from wordpress.org/patterns.
11+
*/
12+
class Gutenberg_REST_Pattern_Directory_Controller_6_0 extends WP_REST_Pattern_Directory_Controller {
13+
/**
14+
* Include a hash of the query args, so that different requests are stored in
15+
* separate caches.
16+
*
17+
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
18+
* under the character limit for `_site_transient_timeout_{...}` keys.
19+
*
20+
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
21+
*
22+
* @since 6.0.0
23+
* @todo This should be removed when the minimum required WordPress version is >= 6.0.
24+
*
25+
* @param array $query_args Query arguments to generate a transient key from.
26+
* @return string Transient key.
27+
*/
28+
protected function get_transient_key( $query_args ) {
29+
if ( method_exists( get_parent_class( $this ), __FUNCTION__ ) ) {
30+
return parent::get_transient_key( $query_args );
31+
}
32+
33+
if ( isset( $query_args['slug'] ) ) {
34+
// This is an additional precaution because the "sort" function expects an array.
35+
$query_args['slug'] = wp_parse_list( $query_args['slug'] );
36+
37+
// Empty arrays should not affect the transient key.
38+
if ( empty( $query_args['slug'] ) ) {
39+
unset( $query_args['slug'] );
40+
} else {
41+
// Sort the array so that the transient key doesn't depend on the order of slugs.
42+
sort( $query_args['slug'] );
43+
}
44+
}
45+
46+
return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
47+
}
48+
}

lib/compat/wordpress-6.0/rest-api.php

-10
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,6 @@ function gutenberg_register_global_styles_endpoints() {
1414
}
1515
add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' );
1616

17-
18-
/**
19-
* Registers the block pattern directory.
20-
*/
21-
function gutenberg_register_rest_pattern_directory() {
22-
$pattern_directory_controller = new Gutenberg_REST_Pattern_Directory_Controller();
23-
$pattern_directory_controller->register_routes();
24-
}
25-
add_action( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' );
26-
2717
/**
2818
* Registers the Edit Site's Export REST API routes.
2919
*
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
<?php
22
/**
3-
* REST API: Gutenberg_REST_Global_Styles_Controller class
3+
* REST API: Gutenberg_REST_Pattern_Directory_Controller_6_2 class
44
*
55
* @package Gutenberg
66
* @subpackage REST_API
77
*/
88

99
/**
10-
* Controller which provides REST endpoint for block patterns.
10+
* Controller which provides REST endpoint for block patterns from wordpress.org/patterns.
1111
*/
12-
class Gutenberg_REST_Pattern_Directory_Controller extends WP_REST_Pattern_Directory_Controller {
12+
class Gutenberg_REST_Pattern_Directory_Controller_6_2 extends Gutenberg_REST_Pattern_Directory_Controller_6_0 {
1313
/**
1414
* Search and retrieve block patterns metadata
1515
*
16-
* @since 6.0.0
16+
* @since 5.8.0
17+
* @since 6.0.0 Added 'slug' to request.
18+
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
1719
*
1820
* @param WP_REST_Request $request Full details about the request.
19-
*
20-
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
21+
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
2122
*/
2223
public function get_items( $request ) {
2324
/**
@@ -30,36 +31,24 @@ public function get_items( $request ) {
3031

3132
$gutenberg_data = get_plugin_data( dirname( dirname( dirname( __DIR__ ) ) ) . '/gutenberg.php', false );
3233

33-
$query_args = array(
34-
'locale' => get_user_locale(),
35-
'wp-version' => $wp_version, // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- it's defined in `version.php` above.
36-
'gutenberg-version' => $gutenberg_data['Version'],
34+
$valid_query_args = array( 'offset', 'order', 'orderby', 'page', 'per_page', 'search', 'slug' );
35+
$query_args = array_merge(
36+
array_intersect_key( $request->get_params(), array_flip( $valid_query_args ) ),
37+
array(
38+
'locale' => get_user_locale(),
39+
'wp-version' => $wp_version, // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- it's defined in `version.php` above.
40+
'gutenberg-version' => $gutenberg_data['Version'],
41+
)
3742
);
3843

39-
$category_id = $request['category'];
40-
$keyword_id = $request['keyword'];
41-
$search_term = $request['search'];
42-
$slug = $request['slug'];
43-
44-
if ( $category_id ) {
45-
$query_args['pattern-categories'] = $category_id;
46-
}
47-
48-
if ( $keyword_id ) {
49-
$query_args['pattern-keywords'] = $keyword_id;
50-
}
51-
52-
if ( $search_term ) {
53-
$query_args['search'] = $search_term;
54-
}
44+
$query_args['pattern-categories'] = isset( $request['category'] ) ? $request['category'] : false;
45+
$query_args['pattern-keywords'] = isset( $request['keyword'] ) ? $request['keyword'] : false;
5546

56-
if ( $slug ) {
57-
$query_args['slug'] = $slug;
58-
}
47+
$query_args = array_filter( $query_args );
5948

6049
$transient_key = $this->get_transient_key( $query_args );
6150

62-
/**
51+
/*
6352
* Use network-wide transient to improve performance. The locale is the only site
6453
* configuration that affects the response, and it's included in the transient key.
6554
*/
@@ -71,7 +60,7 @@ public function get_items( $request ) {
7160
$api_url = set_url_scheme( $api_url, 'https' );
7261
}
7362

74-
/**
63+
/*
7564
* Default to a short TTL, to mitigate cache stampedes on high-traffic sites.
7665
* This assumes that most errors will be short-lived, e.g., packet loss that causes the
7766
* first request to fail, but a follow-up one will succeed. The value should be high
@@ -90,7 +79,7 @@ public function get_items( $request ) {
9079
$raw_patterns = new WP_Error(
9180
'pattern_api_failed',
9281
sprintf(
93-
/* translators: %s: Support forums URL. */
82+
/* translators: %s: Support forums URL. */
9483
__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.', 'gutenberg' ),
9584
__( 'https://wordpress.org/support/forums/', 'gutenberg' )
9685
),
@@ -125,40 +114,4 @@ public function get_items( $request ) {
125114

126115
return new WP_REST_Response( $response );
127116
}
128-
129-
/**
130-
* Include a hash of the query args, so that different requests are stored in
131-
* separate caches.
132-
*
133-
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
134-
* under the character limit for `_site_transient_timeout_{...}` keys.
135-
*
136-
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
137-
*
138-
* @since 6.0.0
139-
* @todo This should be removed when the minimum required WordPress version is >= 6.0.
140-
*
141-
* @param array $query_args Query arguments to generate a transient key from.
142-
* @return string Transient key.
143-
*/
144-
protected function get_transient_key( $query_args ) {
145-
if ( method_exists( get_parent_class( $this ), __FUNCTION__ ) ) {
146-
return parent::get_transient_key( $query_args );
147-
}
148-
149-
if ( isset( $query_args['slug'] ) ) {
150-
// This is an additional precaution because the "sort" function expects an array.
151-
$query_args['slug'] = wp_parse_list( $query_args['slug'] );
152-
153-
// Empty arrays should not affect the transient key.
154-
if ( empty( $query_args['slug'] ) ) {
155-
unset( $query_args['slug'] );
156-
} else {
157-
// Sort the array so that the transient key doesn't depend on the order of slugs.
158-
sort( $query_args['slug'] );
159-
}
160-
}
161-
162-
return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
163-
}
164117
}

lib/compat/wordpress-6.2/rest-api.php

+70
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,73 @@ function gutenberg_register_rest_block_pattern_categories() {
1313
$block_patterns->register_routes();
1414
}
1515
add_action( 'rest_api_init', 'gutenberg_register_rest_block_pattern_categories' );
16+
17+
/**
18+
* Registers the block pattern directory.
19+
*/
20+
function gutenberg_register_rest_pattern_directory() {
21+
$pattern_directory_controller = new Gutenberg_REST_Pattern_Directory_Controller_6_2();
22+
$pattern_directory_controller->register_routes();
23+
}
24+
add_action( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' );
25+
26+
/**
27+
* Add extra collection params to pattern directory requests.
28+
*
29+
* @param array $query_params JSON Schema-formatted collection parameters.
30+
* @return array Updated parameters.
31+
*/
32+
function gutenberg_pattern_directory_collection_params_6_2( $query_params ) {
33+
$query_params['page'] = array(
34+
'description' => __( 'Current page of the collection.', 'gutenberg' ),
35+
'type' => 'integer',
36+
'default' => 1,
37+
'sanitize_callback' => 'absint',
38+
'validate_callback' => 'rest_validate_request_arg',
39+
'minimum' => 1,
40+
);
41+
42+
$query_params['per_page'] = array(
43+
'description' => __( 'Maximum number of items to be returned in result set.', 'gutenberg' ),
44+
'type' => 'integer',
45+
'default' => 100,
46+
'minimum' => 1,
47+
'maximum' => 100,
48+
'sanitize_callback' => 'absint',
49+
'validate_callback' => 'rest_validate_request_arg',
50+
);
51+
52+
$query_params['offset'] = array(
53+
'description' => __( 'Offset the result set by a specific number of items.', 'gutenberg' ),
54+
'type' => 'integer',
55+
);
56+
57+
$query_params['order'] = array(
58+
'description' => __( 'Order sort attribute ascending or descending.', 'gutenberg' ),
59+
'type' => 'string',
60+
'default' => 'desc',
61+
'enum' => array( 'asc', 'desc' ),
62+
);
63+
64+
$query_params['orderby'] = array(
65+
'description' => __( 'Sort collection by post attribute.', 'gutenberg' ),
66+
'type' => 'string',
67+
'default' => 'date',
68+
'enum' => array(
69+
'author',
70+
'date',
71+
'id',
72+
'include',
73+
'modified',
74+
'parent',
75+
'relevance',
76+
'slug',
77+
'include_slugs',
78+
'title',
79+
'favorite_count',
80+
),
81+
);
82+
83+
return $query_params;
84+
}
85+
add_filter( 'rest_pattern_directory_collection_params', 'gutenberg_pattern_directory_collection_params_6_2' );

lib/load.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function gutenberg_is_experiment_enabled( $name ) {
3737

3838
// WordPress 6.0 compat.
3939
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php';
40-
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller.php';
40+
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller-6-0.php';
4141
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-edit-site-export-controller.php';
4242
if ( ! class_exists( 'WP_REST_Block_Pattern_Categories_Controller' ) ) {
4343
require_once __DIR__ . '/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php';
@@ -51,6 +51,7 @@ function gutenberg_is_experiment_enabled( $name ) {
5151

5252
// WordPress 6.2 compat.
5353
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php';
54+
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php';
5455
require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php';
5556
require_once __DIR__ . '/compat/wordpress-6.2/block-patterns.php';
5657

0 commit comments

Comments
 (0)