In the saga so far, you can see my
This is my third (and hopefully final) question. I've got everything working except for saving the data in the WP database. Incidentally, this tutorial is a goldmine for an understanding of how things work in the new Gutenberg way, and how all the different pieces fit together and interact.
As stated previously (see links above), I'm working on customizing the featured image block in the standard post editor sidebar. Here's a screenshot of what I have:
When you click the checkbox, the text field appears. And when you uncheck it it goes away. You can type text in the field and the page goes through all the motions of working as it should. When you save, you get a "success" message. However, nothing is being saved to the database, and that's my problem. I have independent SQL access to the DB, and there's nothing in the wp_postmeta table for any test post I create with metadata that should be saved.
I can click the checkbox, enter something in the text field, tab away, and see that the datastore has my information by executing the following in the debug console:
wp.data.select('core/editor').getEditedPostAttribute('meta')
which returns...
Object { _featured_image_video_url: "this is a test", _featured_image_is_video: true }
as expected. But there's no joy if you save the page and look in the DB. Nothing there.
Here's my current JavaScript code
const el = wp.element.createElement;
const { setState, withSelect, dispatch, select} = wp.data;
const { CheckboxControl, TextControl } = wp.components;
const { useState } = wp.element;
const { withState } = wp.compose;
const { __ } = wp.i18n;
//this replaces the default with our custom Featured Image code
wp.hooks.addFilter(
'editor.PostFeaturedImage',
'dsplugin/featured-image-as-video',
wrapPostFeaturedImage
);
//create a checkbox that takes properties
const MyCheckboxControl = (props) => {
const [ isChecked, setChecked ] = useState( false );
return(
<CheckboxControl
label={ __("Image is a video", "dsplugin") }
checked={ isChecked }
onChange={ () =>{
if (isChecked){
setChecked(false);
dispatch('core/editor').editPost({meta: {_featured_image_is_video: false}})
}else{
setChecked(true);
dispatch('core/editor').editPost({meta: {_featured_image_is_video: true}})
}
props.onChange.call();
} }
/>
)
};
// //this the std TextControl from the example in the documentation
// const MyTextControl = withState({ videoURL: '', }) (({ videoURL, setState }) => (
// <TextControl
// // label="Video URL to use with featured image"
// value={ videoURL }
// placeholder={ __("Enter video URL to play when clicked", "dsplugin") }
// onChange={ ( videoURL ) => {
// //update the text field
// setState( { videoURL } );
//
// //save the new value to the DB
// // meta.featured_image_video_url = videoURL;
// // withDispach( 'core/editor' ).editPost( {meta});
// } }
// />
// ) );
class MyTextControl extends wp.element.Component{
constructor(){
super()
this.state = {
videoURL: ''
}
}
render() {
const { videoURL, setState} = this.props;
// const videoURL = select('core/editor').getEditedPostAttribute('meta').featured_image_video_url;
return(
<TextControl
// select('core/editor').getEditedPostAttribute('meta').featured_image_video_url }
value={ videoURL }
placeholder={ __("Enter video URL to play when clicked", "dsplugin") }
onChange={ ( videoURL ) => {
//save the new value to the DB
const currentMeta = select( 'core/editor' ).getEditedPostAttribute( 'meta' );
const newMeta = { ...currentMeta, _featured_image_video_url: videoURL };
dispatch('core/editor').editPost({meta: newMeta})
} }
/>
)
}
};
//we put it all together in a wrapper component with a custom state to show/hide the TextControl
class MyFeaturedImageControls extends wp.element.Component{
constructor(){
super()
this.state = {
isHidden: true
}
}
toggleHidden(){
this.setState({
isHidden: !this.state.isHidden
})
}
render() {
return(
<div>
<h4>{ __("Image Options", "dsplugin") }</h4>
<MyCheckboxControl onChange={this.toggleHidden.bind(this)}/>
{ !this.state.isHidden && <MyTextControl /> }
</div>
)
}
};
//here's the function that wraps the original Featured Image content and adds
//our custom controls below
function wrapPostFeaturedImage( OriginalComponent ) {
// Get meta field information from the DB.
let meta = select( 'core/editor' ).getCurrentPostAttribute( 'meta' );
console.log ("metadata follows:");
console.log(meta);
return function( props ) {
return (
el(
wp.element.Fragment,
{},
// 'Prepend above',
el(
OriginalComponent,
props
),
<MyFeaturedImageControls />
)
);
}
}
And the PHP code that supports it:
//add metadata fields for use with featured image metabox
function register_resource_item_featured_image_metadata() {
register_meta(
'post',
'_featured_image_video_url',
array(
'object_subtype' => 'ds_resource_item',
'show_in_rest' => true, #must be true to work in Guttenberg
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
)
);
register_meta(
'post',
'_featured_image_is_video',
array(
'object_subtype' => 'ds_resource_item',
'show_in_rest' => true, #must be true to work in Guttenberg
'single' => true,
// 'sanitize_callback' => 'rest_sanitize_boolean',
'type' => 'boolean',
// 'auth_callback' => function() {
// return current_user_can('edit_posts');
// }
)
);
}
add_action( 'init', 'register_resource_item_featured_image_metadata' );
Again... being so new to all of this, I think I'm missing some small detail. I realize my code is incomplete, and you'll see a few things commented out for debugging purposes. But as it stands, I think I should at least be saving a newly entered value to the DB based on what's there now. I also realize I still have to put code in place to fetch an initial value from the db and populate the text field. But first things first.
Thanks for the help.
Update: Here's the php code that defines the custom post type I'm using with this code:
$args = array(
"label" => __( "Resource Items", "dstheme" ),
"labels" => $labels,
"description" => "DS Resource Items are the foundational content type for resources.",
"public" => true,
"publicly_queryable" => true,
"show_ui" => true,
"delete_with_user" => false,
"show_in_rest" => true,
"rest_base" => "dsr_item",
"rest_controller_class" => "WP_REST_Posts_Controller",
"has_archive" => false,
"show_in_menu" => true,
"show_in_nav_menus" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
//modify the slug below to change what shows in the URL when an DSResourceItem is accessed
"rewrite" => array( "slug" => "resource-item", "with_front" => true ),
"query_var" => true,
"menu_position" => 5,
"menu_icon" => "dashicons-images-alt2",
"supports" => array( "title", "editor", "thumbnail", "custom-fields" ),
"taxonomies" => array( "post_tag" ),
);
register_post_type( "ds_resource_item", $args );