A quick run down about extending the API (see notes inside, there aren’t many right now, I need to add more, that’s my bad).
This covers several scenarios and items that you may encounter yourself.
- Next Post
- Previous Post
- Getting the Featured Image
- Title
- Link
- ID
- Get the posts first image
- Get Next/Previous posts first image
- Getting custom fields
<?php if( class_exists( 'WP_REST_Posts_Controller' ) ){ Class WP_REST_DT_Posts_Controller extends WP_REST_Posts_Controller { /** * WP_REST_DT_Posts_Controller constructor. */ public function __construct() { parent::__construct( 'post' ); $this->namespace = 'deep-thoughts/v2'; } /** * Register our custom route * * We */ public function register_routes() { register_rest_route( $this->namespace, '/posts', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => $this->get_collection_params(), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Get items to return in API call * * This method is used to override the parent get_items method so we can format, * add custom fields, and return the data the way we want. * * @param \WP_REST_Request $request * * @return \WP_Error|\WP_REST_Response */ public function get_items( $request ) { /** * Use parent get_items to pull all post data and retain support * for all arguments */ $posts = parent::get_items( $request ); /** * As long as data is returned, we can now loop through it and customize the output, * add custom fields, etc. */ if ( $posts instanceof WP_REST_Response && isset( $posts->data ) && ! empty( $posts->data ) ) { $new_post_data = []; foreach ( $posts->data as $post_index => $post_data ) { $new_post = [ 'id' => $this->extract_post_data( 'id', $post_data ), 'title' => $this->extract_post_data( 'title', $post_data ), 'link' => $this->extract_post_data( 'link', $post_data ), ]; $featured_image = wp_get_attachment_url( $this->extract_post_data( 'featured_media', $post_data ) ); if ( $featured_image ) { $new_post['featured_image'] = $featured_image; } // Current post image $image_src = catch_post_image( $this->extract_post_data( 'content', $post_data ) ); if ( $image_src ) { $new_post['image_src'] = $image_src; } // Next post image if ( isset( $posts->data[ $post_index + 1 ] ) ) { $next_post_data = $posts->data[ $post_index + 1 ]; $image_src = catch_post_image( $this->extract_post_data( 'content', $next_post_data ) ); if ( $image_src ) { $new_post['Next_Post_Obj']['next_image_src'] = $image_src; } } // Prev post image if ( isset( $posts->data[ $post_index - 1 ] ) ) { $prev_post_data = $posts->data[ $post_index - 1 ]; $image_src = catch_post_image( $this->extract_post_data( 'content', $prev_post_data ) ); if ( $image_src ) { $new_post['Prev_Post_Obj']['prev_image_src'] = $image_src; } } $new_post_data[ $post_index ] = $new_post; $next_post = $posts->data[$post_index + 1]; $prev_post = $posts->data[$post_index - 1]; if ( ! empty( $next_post ) ) { $new_post_data[ $post_index ]['Next_Post_Obj']['Next_ID'] = $next_post['id']; $new_post_data[ $post_index ]['Next_Post_Obj']['Next_Title'] = $next_post['title']; $new_post_data[ $post_index ]['Next_Post_Obj']['Next_Link'] = $next_post['link']; } if ( ! empty( $prev_post ) ) { $new_post_data[ $post_index ]['Prev_Post_Obj']['Prev_ID'] = $prev_post['id']; } if ( function_exists( 'get_fields' ) ) { /** * Get specific ACF field * Last argument in get_field set to FALSE to return unformatted value * * @see https://www.advancedcustomfields.com/resources/get_field/ */ $new_post_data[ $post_index ]['_edit_lock'] = get_field( '_edit_lock', $post_data['id'], false ); /** * Get all ACF custom fields and add to post data * * @see https://www.advancedcustomfields.com/resources/get_fields/ */ $custom_fields = get_fields( $post_data['id'] ); // As long as there are custom fields, loop through each adding it to the new data to return if ( ! empty( $custom_fields ) ) { foreach ( $custom_fields as $custom_field_key => $custom_field_value ) { $new_post_data[ $post_index ][ $custom_field_key ] = $custom_field_value; } } } /** * You can also add any custom fields you want by using the core WordPress function. * We surround the get_post_meta() function with maybe_unserialize() in case the value is a serialized array */ $new_post_data[ $post_index ]['_edit_lock_core'] = maybe_unserialize( get_post_meta( $post_data['id'], '_edit_lock', true ) ); } $posts->data = $new_post_data; } return $posts; } /** * Return value for specific key from passed data * * This method is used to return a value (if found in post data), or return * an empty string if not found. * * @param $key * @param $data * * @return mixed|string */ public function extract_post_data( $key, $data ) { // If passed data is not an array, it's empty, or the requested key does not exist, return empty string if ( ! is_array( $data ) || empty( $data ) || ! isset( $data[ $key ] ) ) return ''; // If value is not an array, go ahead and return the value if ( ! is_array( $data[ $key ] ) ) { return $data[ $key ]; } elseif ( array_key_exists( 'rendered', $data[ $key ] ) ) { // If value is an array, chances are the value is under the 'rendered' key, if so return that value return $data[ $key ][ 'rendered' ]; } // All else fails, return empty string. return ''; } } function catch_post_image( $post_content ) { preg_match_all( '/<img.+src=[\'"]([^\'"]+)[\'"].*>/i', $post_content, $matches ); $first_img = $matches [1] [0]; // no image found, return false if ( empty( $first_img ) ) { return false; } return $first_img; } /** * Since we're extending the existing post API class, we need to initialize our * class on the rest_api_init action */ add_action( 'rest_api_init', 'init_deep_thoughts'); /** * Custom function to initialize our extending class. Used a function to make sure * code is compatible with older versions of PHP (instead of using anonymous function) */ function init_deep_thoughts(){ $deep_thoughts = new WP_REST_DT_Posts_Controller(); $deep_thoughts->register_routes(); } }