Skip to content

Instantly share code, notes, and snippets.

@kadamwhite
Created September 23, 2022 00:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kadamwhite/d01556aac3ac42840d2b9d08f86e0488 to your computer and use it in GitHub Desktop.
Save kadamwhite/d01556aac3ac42840d2b9d08f86e0488 to your computer and use it in GitHub Desktop.
Cool bug (potential WP core bug?) where meta with multiple rows registered as "single" prevents REST update.

Reproduction Steps

  1. Clone these files into a plugin directory
  2. Activate plugin (will populate several postmeta table rows about most recent posts)
  3. Open that post in the editor
  4. See the textbox in the Publish Status panel with label "Edit me:"
  5. Put any string into that input
  6. Try to save
  7. Encounter error Updating failed. Could not update the meta value of example_post_meta in database.

This is because of multiple values present in the DB for the OTHER meta field (the one not being edited) registered as "single" => true.

If the input is for the meta with existing multiple rows, they are all updated. But if it's for a separate meta value, the presence of multiple rows for the "single" => true meta field prevents saving any other registered meta.

/* global wp:false */
const { PluginPostStatusInfo } = wp.editPost;
const { useSelect } = wp.data;
const { registerPlugin } = wp.plugins;
const { createElement } = wp.element;
const { useEntityProp } = wp.coreData;
const { TextControl } = wp.components;
const OTHER_META_KEY = 'example_other_meta';
/**
* Get the value and a setter for a given meta key registered to this post.
*
* @param {string} metaKey Key of the meta value to get & set.
* @returns {Array.<*, Function>} Array of the meta's value and a setter function.
*/
const useMeta = ( metaKey ) => {
const postType = useSelect(
( select ) => select( 'core/editor' ).getCurrentPostType(),
[]
);
const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );
const metaFieldValue = meta ? meta[ metaKey ] : '';
/**
* Setter function for the meta key specified when calling useMeta.
*
* @param {*} newValue Value to set for the key passed to useMeta.
*/
function updateMetaValue( newValue ) {
setMeta( {
...( meta || {} ),
[ metaKey ]: newValue,
} );
}
return [ metaFieldValue, updateMetaValue ];
};
registerPlugin( 'rest-meta-write', {
icon: 'menu',
render: () => {
const [ value, updateValue ] = useMeta( OTHER_META_KEY );
return createElement(
PluginPostStatusInfo,
{},
createElement(
TextControl,
{
label: 'Edit me:',
value,
onChange: updateValue,
}
)
);
}
} );
<?php
/**
* Plugin Name: Malformed meta demo
* Plugin Version: 0.1.0
*/
namespace Malformed_Meta_Demo;
const META_KEY = 'example_post_meta';
const OTHER_META_KEY = 'example_other_meta';
function bootstrap() : void {
add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\enqueue_script' );
add_action( 'init', __NAMESPACE__ . '\\register_meta' );
register_activation_hook( __FILE__, __NAMESPACE__ . '\\activate_plugin' );
register_deactivation_hook( __FILE__, __NAMESPACE__ . '\\deactivate_plugin' );
}
bootstrap();
function enqueue_script() : void {
wp_enqueue_script(
'malformed-meta',
trailingslashit( plugin_dir_url( __FILE__ ) ) . 'panel.js',
[
'wp-edit-post',
'wp-data',
'wp-element',
'wp-plugins',
'wp-core-data',
],
false,
true
);
}
function register_meta() : void {
register_post_meta( 'post', META_KEY, [
'single' => true,
'show_in_rest' => true,
] );
register_post_meta( 'post', OTHER_META_KEY, [
'single' => false,
'show_in_rest' => true,
] );
}
// ======================================================
// SET UP DATA ON THE LATEST POST TO REPLICATE THE ISSUE.
// ======================================================
function get_latest_post_id_and_hope_that_nothing_has_been_published_since_plugin_activation() : int {
$latest_posts = wp_get_recent_posts( [
'numberposts' => 1,
'post_type' => 'post',
'post_status' => 'publish',
] );
if ( is_array( $latest_posts ) && count( $latest_posts ) > 0 ) {
return $latest_posts[0]['ID'];
}
return 0;
}
function activate_plugin() : void {
global $wpdb;
$post_id = get_latest_post_id_and_hope_that_nothing_has_been_published_since_plugin_activation();
$wpdb->query( $wpdb->prepare(
'insert into wp_postmeta (post_id, meta_key, meta_value) values (%d, "%s", 333)',
$post_id,
META_KEY
) );
$wpdb->query( $wpdb->prepare(
'insert into wp_postmeta (post_id, meta_key, meta_value) values (%d, "%s", 333)',
$post_id,
META_KEY
) );
$wpdb->query( $wpdb->prepare(
'insert into wp_postmeta (post_id, meta_key, meta_value) values (%d, "%s", 333)',
$post_id,
META_KEY
) );
wp_cache_flush();
}
function deactivate_plugin() : void {
global $wpdb;
$wpdb->query( 'delete from wp_postmeta where meta_key="%s"', META_KEY );
$wpdb->query( 'delete from wp_postmeta where meta_key="%s"', OTHER_META_KEY );
wp_cache_flush();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment