Make WordPress Core


Ignore:
Timestamp:
06/10/2020 02:20:18 AM (20 months ago)
Author:
TimothyBlynJacobs
Message:

REST API: Fix updating "multiple" meta keys with non-string values.

Previously, the REST API would end up deleting each row of metadata and recreating it unnecessarily. This was caused by a type mismatch where the metadata API would always return a string value, and the REST API operated on a typed value.

The REST API now applies the same sanitization and type casting for "multiple" meta keys and "single" meta keys.

Fixes #49339.
Props renathoc.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

    r47858 r47943  
    270270        }
    271271
    272         $current = get_metadata( $meta_type, $object_id, $meta_key, false );
    273 
    274         $to_remove = $current;
     272        $current_values = get_metadata( $meta_type, $object_id, $meta_key, false );
     273        $subtype        = get_object_subtype( $meta_type, $object_id );
     274
     275        $to_remove = $current_values;
    275276        $to_add    = $values;
    276277
    277278        foreach ( $to_add as $add_key => $value ) {
    278             $remove_keys = array_keys( $to_remove, $value, true );
     279            $remove_keys = array_keys(
     280                array_filter(
     281                    $current_values,
     282                    function ( $stored_value ) use ( $meta_key, $subtype, $value ) {
     283                        return $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $value );
     284                    }
     285                )
     286            );
    279287
    280288            if ( empty( $remove_keys ) ) {
     
    360368        $old_value = get_metadata( $meta_type, $object_id, $meta_key );
    361369        $subtype   = get_object_subtype( $meta_type, $object_id );
    362         $args      = $this->get_registered_fields()[ $meta_key ];
    363 
    364         if ( 1 === count( $old_value ) ) {
    365             $sanitized = sanitize_meta( $meta_key, $value, $meta_type, $subtype );
    366 
    367             if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
    368                 // The return value of get_metadata will always be a string for scalar types.
    369                 $sanitized = (string) $sanitized;
    370             }
    371 
    372             if ( $sanitized === $old_value[0] ) {
    373                 return true;
    374             }
     370
     371        if ( 1 === count( $old_value ) && $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $old_value[0], $value ) ) {
     372            return true;
    375373        }
    376374
     
    388386
    389387        return true;
     388    }
     389
     390    /**
     391     * Checks if the user provided value is equivalent to a stored value for the given meta key.
     392     *
     393     * @since 5.5.0
     394     *
     395     * @param string $meta_key     The meta key being checked.
     396     * @param string $subtype      The object subtype.
     397     * @param mixed  $stored_value The currently stored value retrieved from get_metadata().
     398     * @param mixed  $user_value   The value provided by the user.
     399     * @return bool
     400     */
     401    protected function is_meta_value_same_as_stored_value( $meta_key, $subtype, $stored_value, $user_value ) {
     402        $args      = $this->get_registered_fields()[ $meta_key ];
     403        $sanitized = sanitize_meta( $meta_key, $user_value, $this->get_meta_type(), $subtype );
     404
     405        if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
     406            // The return value of get_metadata will always be a string for scalar types.
     407            $sanitized = (string) $sanitized;
     408        }
     409
     410        return $sanitized === $stored_value;
    390411    }
    391412
Note: See TracChangeset for help on using the changeset viewer.