WP 5.3 Supports Object and Array Meta Types in the REST API

With WordPress 5.3, the register_meta functions (including register_post_meta) now support the 'object' and 'array' data types. Previously, the recommended solution to create complex meta-based Gutenberg blocks was to JSON encode the block’s complex attribute value, and pass that string to the API. The REST API now natively supports those complex metadata types. This allows leveraging the REST API to perform schema-based validation, and should additionally simplify client code which interacts with these complex values through the REST API.

Importantly, these data types follow the JSON specification, not the PHP definition. For comparison, that means a JSON object type is equivalent to an associative array in PHP. The JSON array type is a numerically indexed array.

When registering a complex meta field, it is almost always required to also specify a JSON schema describing the expected structure. This can be done by switching show_in_rest from a simple true value, to an array that specifies the desired schema under the schema array key.

Object Example

The following code sample registers a post meta field called “release” that accepts the given JSON data.

{
  "meta": {
    "release": {
      "version": "5.2",
      "artist": "Jaco"
    }
  }
}
register_post_meta(
     'post',
     'release',
     array(
         'single'       => true,
         'type'         => 'object',
         'show_in_rest' => array(
             'schema' => array(
                 'type'       => 'object',
                 'properties' => array(
                     'version' => array(
                         'type' => 'string',
                     ),
                     'artist'  => array(
                         'type' => 'string',
                     ),
                 ),
             ),
         ),
     )
 );

By default, only properties that are explicitly specified in the schema will be allowed. The additionalProperties keyword can be used to to change this behavior. additionalProperties should be another JSON schema that is used to validate any unknown object members. For instance, to enforce that any additional properties are numbers, the following code can be used.

{
   "meta": {
     "release": {
       "version": "5.2",
       "artist": "Jaco",
       "unknown_field": 5.3
     }
   }
 }
register_post_meta(
     'post',
     'version',
     array(
         'single'       => true,
         'type'         => 'object',
         'show_in_rest' => array(
             'schema' => array(
                 'type'       => 'object',
                 'properties' => array(
                     'version' => array(
                         'type' => 'string',
                     ),
                     'artist'  => array(
                         'type' => 'string',
                     ),
                 ),
                 'additionalProperties' => array(
                     'type' => 'number',
                 ),
             ),
         ),
     )
 );

Additionally, additionalProperties can be set to true to allow unknown properties of any format, but this is not recommended.

Array Example

The following code sample registers a post meta field called “projects” that contains a list of project names that accepts the given JSON data.

{
   "meta": {
     "projects": [
       "WordPress",
       "BuddyPress"
     ]
   }
 }
register_post_meta(
     'post',
     'projects',
     array(
         'single'       => true,
         'type'         => 'array',
         'show_in_rest' => array(
             'schema' => array(
                 'type'  => 'array',
                 'items' => array(
                     'type' => 'string',
                 ),
             ),
         ),
     )
 );

The “items” keyword is used to define the JSON schema to validate each array member against. It can be a simple type like “string” or a complex type like “object”.

For instance, to accept the given JSON data, the following meta registration would be used.

{
   "meta": {
     "projects": [
       {
         "name": "WordPress",
         "website": "https://wordpress.org"
       },
       {
         "name": "BuddyPress",
         "website": "https://buddypress.org"
       }
     ]
   }
 }
register_post_meta(
     'post',
     'projects',
     array(
         'single'       => true,
         'type'         => 'array',
         'show_in_rest' => array(
             'schema' => array(
                 'items' => array(
                     'type'       => 'object',
                     'properties' => array(
                         'name'    => array(
                             'type' => 'string',
                         ),
                         'website' => array(
                             'type'   => 'string',
                             'format' => 'uri',
                         ),
                     ),
                 ),
             ),
         ),
     )
 );

Non-Single Metadata

Non-single meta fields have an array of values per post, instead of one value per post. Each of those values is stored in a separate row in the postmeta table.

The array and object data types can be used with non-single meta fields as well. For example, if the “release” meta key from earlier had single set to false, the following JSON data could be accepted.

{
   "meta": {
     "release": [
       {
         "version": "5.2",
         "artist": "Jaco"
       },
       {
         "version": "5.1",
         "artist": "Betty"
       }
     ]
   }
 }

This would result in two postmeta database rows. The first containing { "version": "5.2", "artist": "Jaco" } and the second containing { "version": "5.1", "artist": "Betty" }.

Similarly, the following data would be accepted for the “projects” example if it had set single to false.

{
   "meta": {
     "projects": [
       [
         "WordPress",
         "BuddyPress"
       ],
       [
         "bbPress"
       ]
     ]
   }
 }

This would result in two postmeta database rows. The first containing [ "WordPress", "BuddyPress" ] and the second containing [ "bbPress" ].

Invalid Stored Values

If the existing value for a meta field does not validate against the registered type and schema, the value for that meta field will be returned as null. This is a change in 5.3, where previously only the meta type was validated.

#5-3, #dev-notes, #rest-api