Make WordPress Core

Opened 2 years ago

#55600 new defect (bug)

Can't save registered post meta field of type string that equals registered default value via REST API

Reported by: kraftner's profile kraftner Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Options, Meta APIs Keywords:
Focuses: rest-api Cc:

Description

Current behaviour

Currently if you register a post meta key as string and set a default value:

register_post_meta(
  'my_cpt,
  'my_meta',
  [
    'show_in_rest' => true,
    'single' => true,
    'type' => 'string',
    'default' => 'foo'
  ]
);

and then save the default value it is not actually being written to the database.

If you save any other type, e.g. a boolean

register_post_meta(
  'my_cpt,
  'my_meta',
  [
    'show_in_rest' => true,
    'single' => true,
    'type' => 'boolean',
    'default' => true
  ]
);

it is being written to the database.

Also not saving via the REST API, but via update_metadata() works for strings.

What seems to happen

The problem seems to happen like this:

  • Post meta is only saved if it differs from the currently stored value.
  • So before saving each field the new value is compared with the stored value.
  • When doing so the assumption is (rightfully) that all stored data is a string since that is how post meta is stored in the DB.
  • If the key doesn't exist though, the comparison happens with the default value.
  • The default value though isn't necessarily a string, but is handled as such in the strict equality check.
  • So the strict equality check fails on anything but a string field.

-> String fields can't save the default value, anything else can.

How did it come to this?

I am not absolutely sure what is actually the intended behavior, but I assume if there is nothing stored in the DB the value should be saved, even if it equals the default.

I assume this because the behavior of update_metadata() got changed to only consider DB data, not default values for the comparison by retrieving the stored data using get_metadata_raw() instead of get_metadata().

WP_REST_Meta_Fields duplicates some of the checks in update_metadata() (even explicitly states that in a comment) but WP_REST_Meta_Fields::update_meta_value() (and some other places in that class) didn't get the change to use get_metadata_raw() instead of get_metadata().

I assume this got overlooked when introducing the default metadata values in https://core.trac.wordpress.org/changeset/48402

It seems that replacing get_metadata with get_metadata_raw in that class should fix the issue, but I haven't found time yet to prepare a patch, so I thought I'd at least document the issue for now to see if someone else finds time for a fix before me.

Change History (0)

Note: See TracTickets for help on using tickets.