Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
*/
class WP_REST_Comment_Meta_Fields extends WP_REST_Meta_Fields {

/**
* Cache invalidation callback for comments.
*
* @since 6.9.0
* @var string
*/
protected $cache_callback = 'wp_cache_set_comments_last_changed';

/**
* Retrieves the comment type for comment meta.
*
Expand Down
33 changes: 33 additions & 0 deletions src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
#[AllowDynamicProperties]
abstract class WP_REST_Meta_Fields {

/**
* Cache invalidation callback for the object type's last_changed key.
*
* @since 6.9.0
* @var string
*/
protected $cache_callback = '';

/**
* Retrieves the object meta type.
*
Expand Down Expand Up @@ -143,6 +151,20 @@ public function update_value( $meta, $object_id ) {
$fields = $this->get_registered_fields();
$error = new WP_Error();

/*
* Temporarily unhook the last_changed cache invalidation to avoid
* firing it once per meta key during a batch update.
*/
$meta_type = $this->get_meta_type();
$has_cache_callback = ! empty( $this->cache_callback )
&& function_exists( $this->cache_callback );

if ( $has_cache_callback ) {
remove_action( "added_{$meta_type}_meta", $this->cache_callback );
remove_action( "updated_{$meta_type}_meta", $this->cache_callback );
remove_action( "deleted_{$meta_type}_meta", $this->cache_callback );
}

foreach ( $fields as $meta_key => $args ) {
$name = $args['name'];
if ( ! array_key_exists( $name, $meta ) ) {
Expand Down Expand Up @@ -212,6 +234,17 @@ public function update_value( $meta, $object_id ) {
}
}

/*
* Re-hook the callback and call it once to invalidate the cache
* for the entire batch, rather than once per meta key.
*/
if ( $has_cache_callback ) {
add_action( "added_{$meta_type}_meta", $this->cache_callback );
add_action( "updated_{$meta_type}_meta", $this->cache_callback );
add_action( "deleted_{$meta_type}_meta", $this->cache_callback );
call_user_func( $this->cache_callback );
}

if ( $error->has_errors() ) {
return $error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ class WP_REST_Post_Meta_Fields extends WP_REST_Meta_Fields {
*/
protected $post_type;

/**
* Cache invalidation callback for posts.
*
* @since 6.9.0
* @var string
*/
protected $cache_callback = 'wp_cache_set_posts_last_changed';

/**
* Constructor.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ class WP_REST_Term_Meta_Fields extends WP_REST_Meta_Fields {
*/
protected $taxonomy;

/**
* Cache invalidation callback for terms.
*
* @since 6.9.0
* @var string
*/
protected $cache_callback = 'wp_cache_set_terms_last_changed';

/**
* Constructor.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
*/
class WP_REST_User_Meta_Fields extends WP_REST_Meta_Fields {

/**
* Cache invalidation callback for users.
*
* @since 6.9.0
* @var string
*/
protected $cache_callback = 'wp_cache_set_users_last_changed';

/**
* Retrieves the user meta type.
*
Expand Down
64 changes: 64 additions & 0 deletions tests/phpunit/tests/rest-api/rest-post-meta-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -4014,4 +4014,68 @@ public static function data_scalar_default_values() {
'string default' => array( 'string', 'string', 'string2' ),
);
}

/**
* Tests that the cache callback is re-hooked after a batch meta update.
*
* @ticket 65486
*/
public function test_update_value_batch_rehooks_cache_callback_after_update() {
register_meta(
'post',
'batch_key_1',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
register_meta(
'post',
'batch_key_2',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
register_meta(
'post',
'batch_key_3',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);

global $wp_rest_server;
$wp_rest_server = new Spy_REST_Server();
do_action( 'rest_api_init', $wp_rest_server );

$this->grant_write_permission();

$data = array(
'meta' => array(
'batch_key_1' => 'value1',
'batch_key_2' => 'value2',
'batch_key_3' => 'value3',
),
);
$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
$request->set_body_params( $data );

$response = rest_get_server()->dispatch( $request );
$this->assertSame( 200, $response->get_status() );

// Verify meta values were persisted.
$this->assertSame( 'value1', get_post_meta( self::$post_id, 'batch_key_1', true ) );
$this->assertSame( 'value2', get_post_meta( self::$post_id, 'batch_key_2', true ) );
$this->assertSame( 'value3', get_post_meta( self::$post_id, 'batch_key_3', true ) );

// The cache callback must be re-hooked on all three meta actions.
$this->assertNotFalse( has_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' ) );
$this->assertNotFalse( has_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' ) );
$this->assertNotFalse( has_action( 'deleted_post_meta', 'wp_cache_set_posts_last_changed' ) );
}
}
Loading