1

As far as I can tell, the following code should make the video custom field of my CPT project available through REST API. Yet the custom field is still not exposed.

Why not?

add_action('rest_api_init', 'register_featured_video');

function register_featured_video() {
    register_rest_field(
    'project',
    'video',
    array(
      'get_callback'    => 'get_featured_video',
      'update_callback' => null,
      'schema'          => null,
      )
    );
}

function get_featured_video($object) {   
  $post_id = $object['id']; 
  return get_post_meta($post_id); 
}

EDIT: My current functions.php: https://pastecode.dev/s/uIkZtiqkZn

Object returned through REST API: enter image description here

14
  • When you created the CPT with register_post_type did you set 'show_in_rest' => true?
    – rudtek
    Commented Oct 20, 2023 at 2:18
  • 1. Are you sure that $post_id gets a value? (It's possible that $object['id'] should be $object['ID'].) 2. get_post_meta() called without a $key parameter will return all of a post's metadata; is that your intent?
    – Pat J
    Commented Oct 20, 2023 at 2:23
  • @PatJ 1. I'd like to check that but how? I can echo stuff from the "root" of function.php but not from inside functions like register_featured_video() or get_featured_video().
    – drake035
    Commented Oct 20, 2023 at 18:44
  • You could use error_log() to print it to your error log, if you've got debugging enabled. (By default the error log would be /wp-content/debug.log if WP_DEBUG_LOG is true.)
    – Pat J
    Commented Oct 20, 2023 at 19:43
  • Just added the following my wp-config.php: define( 'WP_DEBUG', true ); define( 'WP_DEBUG_DISPLAY', false ); define( 'WP_DEBUG_LOG', true ); but I still don't see debug.log file appearing anywhere in my folder tree after reloading my site's page many times. Any idea?
    – drake035
    Commented Oct 20, 2023 at 22:12

1 Answer 1

1

I think the problem here is how you register the rest routes and how you return data for the single project endpoint.

When you register the route and the callback like this.

function single_project($data) {
    $post_ID = $data['id'];
    return get_post($post_ID);
}

add_action('rest_api_init', function () {
    register_rest_route( 'project/v1', 'post/(?P<id>\d+)', array(
        'methods' => 'GET',
        'callback' => 'single_project',
        'args' => ['id']
    ));
});

You'll just get the default fields the WP_Postobject has. The data does not get processed the same way as regular posts do. This means that register_rest_field() does not have a chance to add the additional field to the response.

If you did something like

class WP_REST_Projects_Controller extends WP_REST_Posts_Controller {
    public function register_routes() {
        register_rest_route( 'project/v1', 'post/(?P<id>\d+)', array(
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => array( $this, 'get_item' ),
            'permission_callback' => array( $this, 'get_item_permissions_check' ),
            'args'                => [],
        ));
    }
}

add_action('rest_api_init', function () {
    (new WP_REST_Projects_Controller('project'))->register_routes();
});

then the additional field would get added to the response, as the global variable which register_rest_field() affects is used inside WP_REST_Controller which WP_REST_Posts_Controller extends. Inside get_additional_fields() method to be specific.

You should either extend the controller class and override it as needed or modify the single_project() callback to return additional fields along the post fields.

6
  • Thanks, that sort of works in the sense that I know see my custom field returned inside the object which is great! The only thing is that it returns not just the post requested but all posts. How to fix that, and how should this code integrate with the rest of my route declarations?
    – drake035
    Commented Oct 25, 2023 at 18:57
  • @drake035 Sorry about that, I had a copy-paste error on the above class example. The callbacks were supposed to be in singular form, not plural. I fixed the example. Commented Oct 25, 2023 at 20:04
  • I'll try to have another look at your code tomorrow for suggestions on how to integrate the controller with the other route declarations. You can also have a look at the class docs, if you want to give it a go yourself developer.wordpress.org/reference/classes/… Commented Oct 25, 2023 at 20:06
  • thanks. I think I can just use your code for the single endpoint, and keep my definitions for the other endpoints. I'm bewildered though: isn't the desire to get custom fields through REST API extremely common, that is, isn't this feature super basic? If so, why does it require such convoluted workaround? And why is there so few threads/articles about it? I really can't understand this.
    – drake035
    Commented Oct 26, 2023 at 20:09
  • 1
    @drake035 Ok, do post another question, if you have further questions regarding the other endpoints. register_rest_field() is the right tool for the job when you're working with the default WP post/page endpoints. In a case like this where you define custom endpoints and callbacks, then you need to decide and explicitly define the schema, how data is retrieved and handled, etc. yourself and can't rely too much on filters as the callback spits out exactly, and only, what you tell it to. If on the other hand you extend the default controllers, then you can piggyback WP and utilize the filters. Commented Oct 27, 2023 at 15:11

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.