'How can I use serialized data in Gutenberg?

I am trying to create a Sidebar plugin that stores post meta to be used on the front end. Without getting into the unnecessary details, I need to store all the data as 1 meta entry instead of many rows per post. Here is what I have so far:

// ds-jars.js

const pluginContent = (props) => {

    const productData = () => {
        const categoryId = createElement( PanelRow, null, 
            createElement(TextControl, {
                label: "Category Name",
                value: props.category_id,
                onChange: (content) => {
                    props.set_category_id(content)
                },
            })
        )
        const serialId = createElement( PanelRow, null, 
            createElement( TextControl, {
                label: "Serial Number",
                value: props.serial_id,
                onChange: (content) => { 
                    props.set_serial_id(content)
                }
            })
        )
        const productId = createElement( PanelRow, null, 
            createElement( TextControl, {
                label: "Product ID",
                value: props.product_id,
                onChange: (content) => { 
                    props.set_product_id(content)
                }
            })
        )

        return createElement(PluginDocumentSettingPanel, {
            title: "Product Data",
            name: "ds-jars-productdata",
            icon: 'none',
        }, categoryId, serialId, productId
        )
    }

    return productData()
}

const selectData = (select) => {
    return { 
        category_id:        select("core/editor").getEditedPostAttribute("meta")["category_id"],
        serial_id:          select("core/editor").getEditedPostAttribute("meta")["serial_id"],
        product_id:         select("core/editor").getEditedPostAttribute("meta")["product_id"],
        name:               select("core/editor").getEditedPostAttribute("meta")["name"],
        quantity:           select("core/editor").getEditedPostAttribute("meta")["quantity"],
        color:              select("core/editor").getEditedPostAttribute("meta")["color"],
        height:             select("core/editor").getEditedPostAttribute("meta")["height"],
        width:              select("core/editor").getEditedPostAttribute("meta")["width"],
        depth:              select("core/editor").getEditedPostAttribute("meta")["depth"],
        pattern:            select("core/editor").getEditedPostAttribute("meta")["pattern"],
        date_made:          select("core/editor").getEditedPostAttribute("meta")["date_made"],
        date_updated:       select("core/editor").getEditedPostAttribute("meta")["date_updated"],
        date_expired:       select("core/editor").getEditedPostAttribute("meta")["date_expired"]
    }
}

const dispatchData = (dispatch) => {
    return {
        set_category_id:    (value) => {dispatch("core/editor").editPost({meta:{category_id: value} })},
        set_serial_id:      (value) => {dispatch("core/editor").editPost({meta:{serial_id: value} })},
        set_product_id:     (value) => {dispatch("core/editor").editPost({meta:{product_id: value} })},
        set_name:           (value) => {dispatch("core/editor").editPost({meta:{name: value} })},
        set_quantity:       (value) => {dispatch("core/editor").editPost({meta:{quantity: value} })},
        set_color:          (value) => {dispatch("core/editor").editPost({meta:{color: value} })},
        set_height:         (value) => {dispatch("core/editor").editPost({meta:{height: value} })},
        set_width:          (value) => {dispatch("core/editor").editPost({meta:{width: value} })},
        set_depth:          (value) => {dispatch("core/editor").editPost({meta:{depth: value} })},
        set_pattern:        (value) => {dispatch("core/editor").editPost({meta:{pattern: value} })},
        set_date_made:      (value) => {dispatch("core/editor").editPost({meta:{date_made: value} })},
        set_date_updated:   (value) => {dispatch("core/editor").editPost({meta:{date_updated: value} })},
        set_date_expired:   (value) => {dispatch("core/editor").editPost({meta:{date_expired: value} })}
    }
}

let fieldSelect = withSelect(selectData)(pluginContent)
let fieldDispatch = withDispatch(dispatchData)(fieldSelect)

registerPlugin( "ds-jars", {
    icon: 'store',
    render: fieldDispatch 
})

Obviously this works but saves each field as its own meta row entry. According to this post: WP 5.3 Supports Object and Array Meta Types in the REST API. I should be able to send an object to the meta field by using an array in for "show_in_rest". I have been able to register the field I want properly using the following:

register_post_meta('', 'ds_product', 
            array(
                'type' => 'object',
                'single' => true,
                'show_in_rest' => array(
                    'schema' => array(
                        'type' => 'object',
                        'properties' => array(
                            'category_id'   => array('type' => 'string'),
                            'serial_id'     => array('type' => 'string'),
                            'product_id'    => array('type' => 'string'),
                            'name'          => array('type' => 'string'),
                            'quantity'      => array('type' => 'string'),
                            'color'         => array('type' => 'string'),
                            'height'        => array('type' => 'string'),
                            'width'         => array('type' => 'string'),
                            'depth'         => array('type' => 'string'),
                            'pattern'       => array('type' => 'string'),
                            'date_made'     => array('type' => 'string'),
                            'date_updated'  => array('type' => 'string'),
                            'date_expired'  => array('type' => 'string'),
                        )
                    )
                )
            )
        );

I am able to manually send an object through with console.log so it seems it is ready for my values to be sent to it as an object. The problem I am having is writing/reading to this meta field using the withSelect/withDispatch functions. How can I send all my values to this meta field "ds_product" using withDispatch? Been struggling with this for a week. I have tried many things the closest I got was using

// Create prop to get data from serialized field
category_id: select("core/editor").getEditedPostAttribute("meta")["ds_product"].category_id
category_id: select("core/editor").getEditedPostAttribute("meta")["ds_product"].serial_id
...

// Update serialized field
set_category_id: (value) => {dispatch("core/editor").editPost({meta:{ds_product:{category_id: value} }})},
set_serial_id: (value) => {dispatch("core/editor").editPost({meta:{ds_product:{serial_id: value} }})},
...

Since they update one at a time the field ends up storing the previously changed value only thus wiping all other data before it. Any help would be hugely appreciated. As a final note I am aware of the dangers/limitations of storing serialized data to the database but I still need it to be done this way. Thanks ahead of time!



Solution 1:[1]

I came across this issue and what has worked for me is the following usage of dispatch() and saveEntityRecord.

In the following example I am saving a serialized object to the wp_options table in the WP database.

// Save serialized data to wp_options.
dispatch('core').saveEntityRecord('root', 'site', {
    my_plugin_settings: {
      test_1: 'Test 1 Setting Value',
      test_2: 'Test 1 Setting Value',
    },
  }).then(() => {

  })
  .catch((error) => {
    dispatch('core/notices').createErrorNotice('Error', {
      isDismissible: true,
      type: 'snackbar',
    });
  });
});

First, you have to register the settings like you have above so it's available over the REST API.

register_setting(
        'my_plugin_settings',
        'my_plugin_settings',
        [
            'default'      => '',
            'show_in_rest' => [
                'schema' => [
                    'type'       => 'object',
                    'properties' => [
                        'test_1' => [
                            'type' => 'string',
                        ],
                        'test_2'                 => [
                            'type' => 'string',
                        ],
                    ]
                ],
            ]
        ]
    );

Then using the code in my answer above you can save the serialized value properly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 cigien