将元数据从古登堡编辑器边栏保存到数据库

时间:2020-04-02 作者:Andrew

在目前为止的传奇故事中,你可以看到我

  1. First post/question
  2. Second post/questionthis tutorial 是一座金矿,有助于了解事物如何以新的古腾堡方式工作,以及所有不同的部分如何组合在一起并相互作用。

    如前所述(参见上面的链接),我正在标准post editor侧栏中定制特色图像块。以下是我的截图:

    [FIXME]

    单击复选框时,将显示文本字段。当你取消选中它时,它就会消失。您可以在字段中键入文本,页面将按照其应有的方式完成所有工作动作。保存时,会收到一条“成功”消息。但是,没有任何内容保存到数据库中,这是我的问题。我对数据库有独立的SQL访问权限,对于我用元数据创建的任何测试帖子,wp\\u postmeta表中都没有应该保存的内容。

    我可以单击复选框,在文本字段中输入一些内容,然后在调试控制台中执行以下操作,以查看数据存储中是否有我的信息:

    wp.data.select(\'core/editor\').getEditedPostAttribute(\'meta\')

    返回。。。Object { _featured_image_video_url: "this is a test", _featured_image_is_video: true }

    正如所料。但是如果保存页面并查看数据库,就没有什么乐趣了。那里什么都没有。

    下面是我当前的JavaScript代码

    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 />
                )
            );
        }
    }
    
    
    以及支持它的PHP代码:

    //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\' );
    
    再次。。。由于对这一切都很陌生,我想我遗漏了一些小细节。我意识到我的代码不完整,您将看到出于调试目的而注释掉的一些内容。但就目前情况而言,我认为我至少应该根据现在的数据将新输入的值保存到DB中。我还意识到,我仍然需要编写代码,以便从db中获取初始值并填充文本字段。但首先要做的事。

    谢谢你的帮助。

    Update:下面是php代码,它定义了我在代码中使用的自定义帖子类型:

        $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 );
    

2 个回复
最合适的回答,由SO网友:Andrew 整理而成

所以我想出来了@Sally说得对,我写的基本代码是有效的。然而,这里有一个小警告值得一提,我还将记录一种从任何编辑后页面测试数据库连接的方法without any UI code.

首先,wordpress不需要javascript来识别基于gutenberg/react的后期编辑页面上是否存在元数据变量。只需要PHP中的register\\u meta()调用。在插件文件中放入一段代码(如以下代码)就足以使元数据变量存在,并使帖子的编辑页面知道如何将其保存到数据库中。

function register_resource_item_featured_image_metadata() {
  register_meta(
    \'post\',
    \'_featured_image_video_url\',
    array(
      \'object_subtype\' => \'optional\', #omit unless you\'re dealing with a custom post type
      \'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\');
      }
    )
  );
}
add_action( \'init\', \'register_resource_item_featured_image_metadata\' );

仅使用上述内容,您无法从WordPress UI设置/更改此元数据字段,但出于测试目的,您可以执行以下操作:

编辑任何帖子(或创建新帖子)inspect element)wp.data.select(\'core/editor\').getEditedPostAttribute(\'meta\')这将返回如下内容Object { _featured_image_video_url: ""}, 这证明系统已经正确注册并识别了您的元数据变量,而DB对它没有任何价值。

现在在控制台中执行以下操作:wp.data.dispatch(\'core/editor\').editPost({meta: {_featured_image_video_url: \'http://some_url\'}})Update 按钮,并等待页面保存wp.data.select(\'core/editor\').getEditedPostAttribute(\'meta\')Object { _featured_image_video_url: "http://some_url"}, 这证明您已成功将该参数的值保存到数据库中。(当您重新加载页面时,wordpress获取该页面的元数据值,并将其与其余页面数据一起存储在内存中)。

如果您有像MySQL Workbench这样的SQL server管理工具,可以通过运行以下查询进一步验证这一点:

select * from wp_postmeta where post_id = 15727

将页面URL中的帖子ID替换为上面查询中的数字。

                                             vvvvv
(https://localhost/wp/wp-admin/post.php?post=15727&action=edit)
                                             ^^^^^
My Problem我正在做这一切,然而,我把我的插件分成了两部分。主插件文件包含所有初始化代码,文件末尾有一个include,如下所示:

//load other plugin code a front-end user won\'t use if admin user
if( is_admin() ){
  require plugin_dir_path( __FILE__ ) . \'admin-code.php\';
}
我不假思索地将register\\u meta()调用及其关联的add\\u action()放入管理代码中。php文件,只有当is\\u admin()返回true时,才会加载该文件。当我在系统上保存帖子时(单击Update按钮),上面的if语句执行3次。第一次,is\\u admin()返回false, 最后两次返回true. 我并不想知道为什么会出现这种情况,但很明显,没有在代码的第一次传递中定义\\u featured\\u image\\u video\\u url元变量是一个问题,这也是我保存变量时出现问题的原因。如果我将register\\u meta()代码移动到文件中每次都执行的位置,那么一切都会运行得很好。

希望这对其他人有所帮助。非常感谢Sally帮我消除噪音,以便我能找出根本原因。在我们两人之间,我认为我们已经填补了一些漏洞,将所有部分整合在一起,以增强古腾堡编辑器UI的现有部分,至少对于那些新加入React/js的人来说是这样。

SO网友:Sally CJ

你的JavaScript代码适合我。元数据确实得到保存

但也许你想试试my code 基本完成,即在页面加载时,复选框会根据当前数据库值自动选中/取消选中(文本框也会显示/隐藏)。其次,我的做法与古腾堡团队对原版的做法一样component for the featured image 其实很简单。。我的意思是,我只是希望你能从我的代码中学到一些好东西

尽管如此,关于这个问题或让您的代码保存元数据,我在发布原始答案之前注意到的一个问题是auth_callback 对于_featured_image_is_video 元数据:

register_meta(
  \'post\',
  \'_featured_image_is_video\',
  array(
    ...
    // \'auth_callback\' => function() {
    //     return current_user_can(\'edit_posts\');
    // }
  )
);
你为什么要把auth_callback? 因为如果这样做,那么它将默认为__return_false() 对于受保护的元(名称以开头_/下划线),这意味着任何人都不允许通过REST API编辑meta!:p

所以试着取消评论。但我不确定这一点,因为如果您在实际代码中对其进行了注释,那么您就会注意到它,因为WordPress/Gutenberg会在帖子编辑屏幕上显示一个通知,说明帖子无法更新(因为您没有编辑meta的权限,或者auth_callback 始终返回false).

To other readers:

确保您的自定义帖子类型支持自定义字段,因为REST API手册says:

请注意,对于在自定义帖子类型上注册的元字段,帖子类型必须具有custom-fields 支持否则,元字段将不会出现在REST API中。

当元字段没有出现在REST API中时,元字段将不会通过REST API保存/更新。

相关推荐

如何从WordPress菜单添加帮助Scout javascript LiveChat

我的客户希望从这里的网站添加实时聊天脚本代码,但他希望它放在特定菜单/联系人菜单上/我不知道如何实现它。请告诉我对此的任何想法,我对javascript不是很熟悉这是用于实时聊天的javascript:<script type=\"text/javascript\">!function(e,t,n){function a(){var e=t.getElementsByTagName(\"script\")[0],n=t.createElement(\"script\");n.type=\"te