Opened 17 months 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: |
|
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.