最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

plugins - Set Multiple Meta Values as an Array Using dispatch( 'coreeditor' ).editPost() Call in GutenbergJS

programmeradmin0浏览0评论

I'm attempting to store both an Image's URL and ID in a post's meta field via a Gutenberg component, but am having trouble figuring out how to store those multiple values in an array.

I can store a single value as a meta value without a problem. I can even store a single value in the first spot of an array without a problem. But I cannot seem to work out the proper syntax to take advantage of Wordpress's ability to store an array as a meta value.

The overarching goal is to override the image being used when a post is shared on social media. The issue is that I need to transfer both the URL and ID over to a PHP hook for it to work properly with a plugin (The SEO Framework). Unfortunately, I can't just store the ID because I need the URL to render the image within the Gutenberg component.

Full Component Code:

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ props.metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ]
        }
    } ) )( function( props ) {
            const fallbackInstructions = <p>{ __( 'To edit the OG image, you need permission to upload media.', 'atlantis') }</p>;
            const onUpdateImage = img => { props.setMetaValue( img.url ); };
            const onRemoveImage = () => { props.setMetaValue( null ); };
        return (
        <div className="og-image__wrapper">
            <MediaUploadCheck fallback={ fallbackInstructions }>
                <MediaUpload
                    title={ OG_IMAGE_LABEL }
                    onSelect={ onUpdateImage }
                    allowedTypes={ ALLOWED_MEDIA_TYPES }
                    render={ ( { open } ) => (
                        <Button
                            className={ ! props.metaValue ? 'og-image__toggle' : 'og-image__preview' }
                            onClick={ open }
                            aria-label={ ! props.metaValue ? null : EDIT_OR_UPDATE_OG_IMAGE_LABEL }>
                            { !! props.metaValue &&
                                    <img className='og-image__button' src={ props.metaValue } alt="" />
                            }
                            { ! props.metaValue && SET_OG_IMAGE_LABEL }
                        </Button>
                    ) }
                />
            </MediaUploadCheck>
            { !! props.metaValue &&
                    <MediaUploadCheck>
                        <MediaUpload
                            title={ OG_IMAGE_LABEL }
                            onSelect={ onUpdateImage }
                            allowedTypes={ ALLOWED_MEDIA_TYPES }
                            render={ ( { open } ) => (
                                <Button onClick={ open } isDefault isLarge>
                                    { REPLACE_OG_IMAGE_LABEL }
                                </Button>
                            ) }
                        />
                    </MediaUploadCheck>
                }
                { !! props.metaValue &&
                    <MediaUploadCheck>
                        <Button onClick={ onRemoveImage } isLink isDestructive>
                            { REMOVE_OG_IMAGE_LABEL }
                        </Button>
                    </MediaUploadCheck>
                }
        </div>
    );
    }
);

export default OGImage;

Bits that matter:

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ props.metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ]
        }
    } ) )( function( props ) {

            const onUpdateImage = img => { props.setMetaValue( img.url ); };

        return (

            <img className='og-image__button' src={ props.metaValue } alt="" />

    );
    }
);

export default OGImage;

My optimal solution would be to do something like this, but this just stores the first value specified (so in this case, [img.url]):

const onUpdateImage = img => { props.setMetaValue( img.url, img.id ); };

...

<img className='og-image__button' src={ props.metaValue[0] } alt="" />

or even:

const onUpdateImage = img => { props.setMetaValue( [img.url, img.id] ); };

...

<img className='og-image__button' src={ props.metaValue[0] } alt="" />

I'm declaring the meta value as:

  register_meta(
    'post',
    '_og_image',
    [
      'show_in_rest' => true,
      'single' => false,
      'type' => 'string'
    ]
  );

As a last ditch effort, I'm considering combining both the Image URL and ID in a string and parsing it out on both ends. This solution feels hacky to me and I would prefer to store the two values independently as an array.

This is a crosspost.

Appendix for Tom. This worked once -- out of some strange fluke I'm assuming -- but I'm unable to reproduce it. The image url is being changed, but the image id refuses to save in the meta value.

PHP

function set_og_image() {
  register_meta(
    'post',
    'og_image_url',
    [
      'show_in_rest' => true,
      'single' => true,
      'type' => 'string'
    ]
  );
  register_meta(
    'post',
    'og_image_id',
    [
      'show_in_rest' => true,
      'single' => true,
      'type' => 'string' // Solution (thank you to Tom): This needs to be an integer type.
    ]
  );
}

JS

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaKey, metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValueURL: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ 'og_image_url' ],
            metaValueID: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ 'og_image_id' ]
        }
    } ) )( function( props ) {
            const onUpdateImage = img => {
                props.setMetaValue( 'og_image_url', img.url );
                props.setMetaValue( 'og_image_id', img.id );
      };
            const onRemoveImage = () => {
                props.setMetaValue( props.metaKeyURL, null );
                props.setMetaValue( props.metaKeyID, null );
       };
        return (
                ...
    );
    }
);

Parent Component:

const { __ } = wp.i18n;
const { Fragment } = wp.element;
const { PanelBody, PanelRow } = wpponents;
const { registerPlugin } = wp.plugins;
const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost;

import OGImage from './components/og-image';

const GenericSidebar = props => {
  return (
    <Fragment>
      <PluginSidebarMoreMenuItem target="generic-sidebar">
        {__("Generic Sidebar", "atlantis")}
      </PluginSidebarMoreMenuItem>
      <PluginSidebar
        name="generic-sidebar"
        title={__("Generic Sidebar", "atlantis")}
      >
        <PanelBody title={__("OG Image", "atlantis")} >
          <PanelRow>
            <OGImage
              metaKeyURL = { 'og_image_url' }
              metaKeyID = { 'og_image_id' }
             />
          </PanelRow>
        </PanelBody>
      </PluginSidebar>
    </Fragment>
  );
};

registerPlugin("generic-sidebar", {
  icon: "visibility",
  render: GenericSidebar
});

I'm attempting to store both an Image's URL and ID in a post's meta field via a Gutenberg component, but am having trouble figuring out how to store those multiple values in an array.

I can store a single value as a meta value without a problem. I can even store a single value in the first spot of an array without a problem. But I cannot seem to work out the proper syntax to take advantage of Wordpress's ability to store an array as a meta value.

The overarching goal is to override the image being used when a post is shared on social media. The issue is that I need to transfer both the URL and ID over to a PHP hook for it to work properly with a plugin (The SEO Framework). Unfortunately, I can't just store the ID because I need the URL to render the image within the Gutenberg component.

Full Component Code:

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ props.metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ]
        }
    } ) )( function( props ) {
            const fallbackInstructions = <p>{ __( 'To edit the OG image, you need permission to upload media.', 'atlantis') }</p>;
            const onUpdateImage = img => { props.setMetaValue( img.url ); };
            const onRemoveImage = () => { props.setMetaValue( null ); };
        return (
        <div className="og-image__wrapper">
            <MediaUploadCheck fallback={ fallbackInstructions }>
                <MediaUpload
                    title={ OG_IMAGE_LABEL }
                    onSelect={ onUpdateImage }
                    allowedTypes={ ALLOWED_MEDIA_TYPES }
                    render={ ( { open } ) => (
                        <Button
                            className={ ! props.metaValue ? 'og-image__toggle' : 'og-image__preview' }
                            onClick={ open }
                            aria-label={ ! props.metaValue ? null : EDIT_OR_UPDATE_OG_IMAGE_LABEL }>
                            { !! props.metaValue &&
                                    <img className='og-image__button' src={ props.metaValue } alt="" />
                            }
                            { ! props.metaValue && SET_OG_IMAGE_LABEL }
                        </Button>
                    ) }
                />
            </MediaUploadCheck>
            { !! props.metaValue &&
                    <MediaUploadCheck>
                        <MediaUpload
                            title={ OG_IMAGE_LABEL }
                            onSelect={ onUpdateImage }
                            allowedTypes={ ALLOWED_MEDIA_TYPES }
                            render={ ( { open } ) => (
                                <Button onClick={ open } isDefault isLarge>
                                    { REPLACE_OG_IMAGE_LABEL }
                                </Button>
                            ) }
                        />
                    </MediaUploadCheck>
                }
                { !! props.metaValue &&
                    <MediaUploadCheck>
                        <Button onClick={ onRemoveImage } isLink isDestructive>
                            { REMOVE_OG_IMAGE_LABEL }
                        </Button>
                    </MediaUploadCheck>
                }
        </div>
    );
    }
);

export default OGImage;

Bits that matter:

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ props.metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ]
        }
    } ) )( function( props ) {

            const onUpdateImage = img => { props.setMetaValue( img.url ); };

        return (

            <img className='og-image__button' src={ props.metaValue } alt="" />

    );
    }
);

export default OGImage;

My optimal solution would be to do something like this, but this just stores the first value specified (so in this case, [img.url]):

const onUpdateImage = img => { props.setMetaValue( img.url, img.id ); };

...

<img className='og-image__button' src={ props.metaValue[0] } alt="" />

or even:

const onUpdateImage = img => { props.setMetaValue( [img.url, img.id] ); };

...

<img className='og-image__button' src={ props.metaValue[0] } alt="" />

I'm declaring the meta value as:

  register_meta(
    'post',
    '_og_image',
    [
      'show_in_rest' => true,
      'single' => false,
      'type' => 'string'
    ]
  );

As a last ditch effort, I'm considering combining both the Image URL and ID in a string and parsing it out on both ends. This solution feels hacky to me and I would prefer to store the two values independently as an array.

This is a crosspost.

Appendix for Tom. This worked once -- out of some strange fluke I'm assuming -- but I'm unable to reproduce it. The image url is being changed, but the image id refuses to save in the meta value.

PHP

function set_og_image() {
  register_meta(
    'post',
    'og_image_url',
    [
      'show_in_rest' => true,
      'single' => true,
      'type' => 'string'
    ]
  );
  register_meta(
    'post',
    'og_image_id',
    [
      'show_in_rest' => true,
      'single' => true,
      'type' => 'string' // Solution (thank you to Tom): This needs to be an integer type.
    ]
  );
}

JS

const OGImage = compose(
    withDispatch( function( dispatch, props ) {
        return {
            setMetaValue: function( metaKey, metaValue ) {
                dispatch( 'core/editor' ).editPost(
                    { meta: { [ metaKey ]: metaValue } }
                );
            }
        }
    } ),
    withSelect( function( select, props ) {
        return {
            metaValueURL: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ 'og_image_url' ],
            metaValueID: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ 'og_image_id' ]
        }
    } ) )( function( props ) {
            const onUpdateImage = img => {
                props.setMetaValue( 'og_image_url', img.url );
                props.setMetaValue( 'og_image_id', img.id );
      };
            const onRemoveImage = () => {
                props.setMetaValue( props.metaKeyURL, null );
                props.setMetaValue( props.metaKeyID, null );
       };
        return (
                ...
    );
    }
);

Parent Component:

const { __ } = wp.i18n;
const { Fragment } = wp.element;
const { PanelBody, PanelRow } = wpponents;
const { registerPlugin } = wp.plugins;
const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost;

import OGImage from './components/og-image';

const GenericSidebar = props => {
  return (
    <Fragment>
      <PluginSidebarMoreMenuItem target="generic-sidebar">
        {__("Generic Sidebar", "atlantis")}
      </PluginSidebarMoreMenuItem>
      <PluginSidebar
        name="generic-sidebar"
        title={__("Generic Sidebar", "atlantis")}
      >
        <PanelBody title={__("OG Image", "atlantis")} >
          <PanelRow>
            <OGImage
              metaKeyURL = { 'og_image_url' }
              metaKeyID = { 'og_image_id' }
             />
          </PanelRow>
        </PanelBody>
      </PluginSidebar>
    </Fragment>
  );
};

registerPlugin("generic-sidebar", {
  icon: "visibility",
  render: GenericSidebar
});
Share Improve this question edited Oct 17, 2019 at 19:29 gardinermichael asked Oct 16, 2019 at 23:00 gardinermichaelgardinermichael 155 bronze badges 2
  • Can you provide some context as to how this fits into Gutenberg? It looks like an arbitrary React component that calls the main data store, but it's not clear how it gets its props, where it gets them, or what it's doing – Tom J Nowell Commented Oct 17, 2019 at 18:56
  • Sure. It's part of a fragment that goes inside a registerPlugin() call to create a generic sidebar. I'll update the post again with code. – gardinermichael Commented Oct 17, 2019 at 18:58
Add a comment  | 

2 Answers 2

Reset to default 0

Have you tried using:

            setImage: function( image_url, image_id ) {
                dispatch( 'core/editor' ).editPost(
                    {
                        meta: {
                            'og_image_url': image_url,
                            'og_image_id': image_id
                        }
                    }
                );
            }

Instead of using a generic setMetaValue function?

The short answer is, you shouldn't.

Post meta/etc store a single value, and to get around that some people PHP serialize the data. So much so that core started doing it automatically if you passed an array or object so that it could store a string.

The problem is this opens you up to PHP serialisation attacks, and is a security concern. Additionally, it makes it incredibly difficult to query for those values.

So instead, the correct solution, is to store 2 meta key/value pairs, not 1. That means calling register_meta twice, with different key names for the ID and the URL.

There is also the alternative of retrieving the URL given an attachment ID via the REST API, or the local storage. You don't need to store the URL in post meta to be able to show an attachment IDs preview. Gutenberg manages it with the featured image, so it's possible

发布评论

评论列表(0)

  1. 暂无评论