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
|
2 Answers
Reset to default 0Have 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
registerPlugin()
call to create a generic sidebar. I'll update the post again with code. – gardinermichael Commented Oct 17, 2019 at 18:58