1

I've been struggling for a few days on implementing a "Related Posts" feature, and unfortunately PHP/Wordpress is not my strong suit.

Currently I am hitting this endpoint on my frontend:

posts?slug=${slug}&orderby=date&order=desc&categories=4&acf_format=standard

This returns the main Post object with things that I would expect such as slug, status, sticky, etc. Within that object is the acf object, where I can see my related_posts array. My issue arises from the fact that these Post Objects are presented with entirely different fields, mostly prefixed with the word "post_".

I found this Github issue over on the acf-to-rest-api plugin that is nearly identical to the issue I'm facing (I even stole their issue title!), however have so far been unable to implement it, and it's also from 2017 so I'm unsure of what is outdated.

Is there a way to return the normal WP Post JSON format?

J. Jackson
  • 3,326
  • 8
  • 34
  • 74

1 Answers1

1

A possible approach would be to add a filter to your Wordpress's theme function.php, using the add_filter() function.

In your case, you could check if the related_posts field exists in the ACF response.
If it does, your filter would map the related_posts array to their corresponding WP Post JSON format and replaces the original related_posts field in the response.

Make sure to adjust the field names and additional fields (e.g., yoast_head if you are using the Yoast SEO plugin) according to your setup.
And you might also have to flush WordPress caching plugins in order to test that filter.

function modify_acf_to_rest_api_response($response, $request) {
    if (isset($response->data['acf']['related_posts'])) {
        $related_posts = $response->data['acf']['related_posts'];

        // Map related_posts to their WP Post JSON format
        $related_posts_json = array_map(function ($related_post) {
            $post_data = get_post($related_post->ID);
            $post_json = array(
                'id' => $post_data->ID,
                'date' => $post_data->post_date,
                'date_gmt' => $post_data->post_date_gmt,
                'guid' => $post_data->guid,
                'modified' => $post_data->post_modified,
                'modified_gmt' => $post_data->post_modified_gmt,
                'slug' => $post_data->post_name,
                'status' => $post_data->post_status,
                'type' => $post_data->post_type,
                'link' => get_permalink($post_data->ID),
                'title' => array(
                    'rendered' => $post_data->post_title
                ),
                'content' => array(
                    'rendered' => apply_filters('the_content', $post_data->post_content),
                    'protected' => false
                ),
                'excerpt' => array(
                    'rendered' => apply_filters('the_excerpt', $post_data->post_excerpt),
                    'protected' => false
                ),
                'author' => (int) $post_data->post_author,
                'featured_media' => (int) get_post_thumbnail_id($post_data->ID),
                'comment_status' => $post_data->comment_status,
                'ping_status' => $post_data->ping_status,
                'sticky' => false,
                'template' => '',
                'format' => get_post_format($post_data->ID),
                'meta' => array(),
                'categories' => wp_get_post_categories($post_data->ID),
                'tags' => wp_get_post_tags($post_data->ID),
                'yoast_head' => '', // If using Yoast SEO plugin
                '_links' => array()
            );

            return $post_json;
        }, $related_posts);

        // Replace the related_posts with the WP Post JSON format
        $response->data['acf']['related_posts'] = $related_posts_json;
    }

    return $response;
}

add_filter('rest_prepare_post', 'modify_acf_to_rest_api_response', 10, 2);

The OP J. Jackson adds in the comments:

I am using a Tags select within ACF rather than WordPress's tags.
How would I go about populating the Tags array with those?

That means you need to populate the Tags array with the ACF tags, by fetching the ACF tags field value and including it in the tags key of the $post_json array.

I will assume the ACF field name for tags is custom_tags, but do replace 'custom_tags' with the actual field name of your ACF tags field if it's different.

function modify_acf_to_rest_api_response($response, $request) {
    if (isset($response->data['acf']['related_posts'])) {
        $related_posts = $response->data['acf']['related_posts'];

        // Map related_posts to their WP Post JSON format
        $related_posts_json = array_map(function ($related_post) {
            $post_data = get_post($related_post->ID);
            $custom_tags = get_field('custom_tags', $related_post->ID); // Fetch ACF custom_tags field value

            $post_json = array(
                // ... other fields remain the same
                'tags' => $custom_tags, // Replace this line with the ACF custom_tags field value
                '_links' => array()
            );

            return $post_json;
        }, $related_posts);

        // Replace the related_posts with the WP Post JSON format
        $response->data['acf']['related_posts'] = $related_posts_json;
    }

    return $response;
}

add_filter('rest_prepare_post', 'modify_acf_to_rest_api_response', 10, 2);
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • This is fantastic, and exactly what I was looking for! One small additional question... I am using a Tags select within ACF rather than Wordpress's tags. How would I go about populating the Tags array with those? – J. Jackson Apr 20 '23 at 16:45
  • 1
    @J.Jackson I have edited the answer to address your comment/question. – VonC Apr 20 '23 at 17:08