0

I'm building a mobile app and website on a WP backend, which is the reason why I'm looking for a uniform JSON Request-Response pattern.

Everything's working fine, and the wp installation does not use any plugin / particular theme, every plugin / theme it uses is the result of custom development.

The app has users that each have a profile, which I registered and generate as a custom post type, solely for that purpose.

The last step I'm thinking of now is to store the wp post contents of that custom post type as JSON only. Like this I'll be able to use them without any additionally needed parsing, both for the web and mobile app development.

Before doing this, I wanted to double check:could this be a problem for the wp core, or break some other functionality of the wp core? I also intend to use the WP REST api for posts to query for posts of that custom type, using WP's, built-in REST API features for the query interfaces, includin tags, metas, and whatever else it offers.

Will using only JSON post contents be a problem here?

UPDATE

I've implemented the suggestion below, but can't get it working, actually. The value that I retrieve from the rest API, if I retrieve the according meta field, is always null.

I've now checked in the wp_postmeta db, and can see that this seems to be a retrieval error. Because, when I for example store ['hello' => 'there'] as a meta value, I get the following according value in the mentioned table:

a:1:{s:5:"hello";s:5:"there";}

so the serialized string. For some reason, wp fails to retrieve that back as a normal array. This even occurs when I omit the schema and the auth callbacks or whatever fancy additional function you can add to your register_post_meta call. So it seems like I have to add some custom parsing anyway for the REST retrieval? Hence, I should rather use register_rest_field for this? Or am I missing something?

UPDATE 2

Okay, found it. The first problem is that this here:

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.

Simply inserts null into your db, while the call to add_post_meta() actually returns true in that case, falsely indicating that your metavalue has been properly created. In the same way, you can even have valid serialized object data in your DB (which was the case for me); but if your schema does not match with the serialized data present within your DB, the rest API will retrieve null, although you actually have properly serialized data within your DB. Here again, without indicating any error. Because the schema seems to be applied for retrieval too, not only for storage.

With this, I can think about two problems now:

  • For everything to work properly, you have to assure that your schema matches all possible cases of your meta value.

  • The rather bigger problem I see here is: What if your schema of that value may change over time with updates of your application? This would mean that you would need to update your schema accordingly. This would in turn however turn the retrieval of all of your post meta fields that are still based on the old schema into null values. Except if you implement some compromise schema like setting properties of your new schema, that are mandatory in the new schema, as required => false, to still be able to retrieve your old metas, not yet affected by the updates. But imho that will cause a mess.

So make sure you think about this and consider eventual future cases when using post meta fields. Or am I misunderstanding something?

13
  • in the REST API your JSON post content will appear as an escaped string that would need to be separately parsed, so you would need to parse JSON to extract and parse more JSON
    – Tom J Nowell
    Aug 25, 2022 at 18:27
  • Hmmmm aight thx for that. Do you maybe have a better idea? Maybe store an empty string as all post contents, and store the json as the value of a specific custom meta key associated to the post?
    – DevelJoe
    Aug 25, 2022 at 19:18
  • To best work with the REST API, each profile field should be broken down into a respective WordPress mechanism. If a field is used to classify profiles, it would make sense to implement it as a taxonomy. If it's a custom status or some such, then perhaps post meta-data. In this fashion, every field integrates nicely with WordPress's core REST API functionality, including filtering, field selection, and REST CRUD capabilities, among other things. I don't think there's much of a reason to try and store all of the data in a single field when WP is built to handle complex data across multiple.
    – bosco
    Aug 25, 2022 at 19:44
  • Hmmm the thing is that the profiles will always be retrieved entirely, and never partially. Breaking up the profile contents into different fields is thus not an ideal approach for me.
    – DevelJoe
    Aug 25, 2022 at 19:49
  • 1
    Yup, just wanted to highlight it, the fact that the schema is also used for retrieval actually makes sense, but was totally not obvious to me..
    – DevelJoe
    Aug 28, 2022 at 16:43

1 Answer 1

2

The core WordPress data structures and the REST API's core controllers aren't really designed to ever treat post content as anything other than one big string. As Tom mentions in the comments, this means that storing JSON in that field will result in it being presented as an escaped string in REST responses rather than structurally part of JSON response itself - you would need to perform a second pass of JSON-parsing on the field in the client.

Nothing will really "break" - it will just be a bit uncomfortable to see a serialized JSON string included in a JSON REST response instead of as part of the response, in addition to the special parsing case required on the client-side. Another drawback is that if the client is capable of setting the data in this JSON string, then you will likely need to write a fair bit of custom server-side logic to handle validation and sanitization of submitted values (or otherwise find a way to direct that value through the mechanisms which WordPress offers).

A better option might be to store the data in post-meta, as WordPress is already capable of handling complex data stored in meta-values. This also means that within server-side PHP you can interact with the structured data (if necessary) via traditional means without manually unserializing/serializing the data:

update_post_meta(
  $profile_post_id,
  'user_profile_data',
  [
    'status'   => 'Eating a kabob',
    'bio'      => 'Lorem Ipsum dolor sit amet...',
    'bg_color' => '#efefef',
  ]
);

A post meta-field can be exposed on the core REST routes via the register_post_meta() function, which also offers a lot of configurability to assist in validating and sanitizing the data, custom authorization checks, and more. See the Modifying Responses page in the REST API Handbook for more information.

function wpse408959_register_profile_meta() {
  register_post_meta(
    'user_profile',
    'user_profile_data',
    [
      'type'          => 'object',
      'single'        => true,
      'auth_callback' => function( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ) {
        return current_user_can( 'edit_post', $object_id );
      },
      'show_in_rest'  => [
        'schema' => [
          'type'       => 'object',
          'properties' => [
            'status'   => [
              'type'      => 'string',
              'maxLength' => 120,
            ],
            'bio'      => [
              'type'      => 'string',
              'maxLength' => 1024,
            ],
            'bg_color' => [
              'type'      => 'string',
              'format'    => 'hex-color',
            ],
          ],
        ],
      ],
    ]
  );
}

add_action( 'rest_api_init', 'wpse408959_register_profile_meta' );

The Schema page will help in determining how to best write and leverage the schema.

The meta-data will then be available for REST interactions through a property in the meta object:

GET /wp-json/wp/v2/user_profile/1234
{
  // ...
  "meta": {
    "user_profile_data": {
      "status": "Eating a kabob",
      "bio": "Lorem Ipsum dolor sit amet...",
      "bg_color": "#efefef"
    }
  }
  // ...
}

As a final note, if you intend to use any of the complex data within core WordPress systems - say, to filter or sort profiles, or perform a search based on specific fields - then the data would be better organized into WordPress's core data structures - taxonomy terms or individual post-meta with primitive values, for example. WordPress has facilities for storing and retrieving complex meta-data, but it doesn't really offer the means to interact with it beyond that, owed to the complexity and overhead involved in operating on serialized values in SQL.

9
  • 1
    Thanks so much for your answer and careful hints! In my current code, I actually more or less did what you said: all the profile characteristics that I use to order query results or similar are stored in custom post meta fields. Custom stuff used to query the profiles is implemented via custom taxonomies. So now the only missing part is the store JSON content thing, and looks like I know what to do now. Thanks again!
    – DevelJoe
    Aug 25, 2022 at 21:40
  • 1
    @Tom, I know it's outta scope, but not the wrong place to show gratitude. Thanks a lot to you too, as you repeatedly insisted in using the wp rest api in some of my older questions. Indeed facilitated quite everything.
    – DevelJoe
    Aug 25, 2022 at 21:42
  • I always get null as the meta value with your code, no matter what I try; even if I omit the auth callback; any idea?
    – DevelJoe
    Aug 28, 2022 at 15:40
  • @DevelJoe does the profile post-type have custom-fields support?
    – bosco
    Aug 28, 2022 at 15:46
  • 1
    Found the problem; will detail it above! A tricky one, this one!
    – DevelJoe
    Aug 28, 2022 at 16:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.