I built a custom endpoint for the WordPress REST API to get a post revision. The result is in the Wordpress Post syntax:
[ID] => 478
[post_author] => 1
[post_date] => 2017-11-20 17:22:11
[post_date_gmt] => 2017-11-20 16:22:11
[post_content] => My Post content
Whereas the Wordpress REST API would provide me something like this:
"author": 1,
"title": {
"rendered": "My title"
},
"content": {
"rendered": "",
"protected": false
},
(one is printed via php the other is in a JSON format, but what is important is that in the first example it says: post_content
and in the second it's content
which is then separated in rendered
and protected
for example.
I am pretty sure that it's almost the same thing as described here: https://wordpress.stackexchange.com/questions/236249/wp-api-v2-custom-endpoint-response-formatting?newreg=7edb54e1ae494e528e5e146982469664
But in my case I have revisions.
I tried to prepare the Post Object for the REST API.
I created a new instance of the WP_REST_Revisions_Controller and tried to use its method prepare_item_for_response
. $request ist a WP_Rest_Request. (btw: why do I have to write new \WP_REST_Revisions_Controller
with a backslash \
before).
$latest_revision = wp_get_post_revisions( $id, $args ); //WP Post Object
$postController = new \WP_REST_Revisions_Controller('revision');
$response = $postController->prepare_item_for_response( $latest_revision, $request );
print_r($response);
The problem is that I get Notices:
<b>Notice</b>: Trying to get property of non-object in <b>/Users/USER/Documents/my-api/wp/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php</b> on line <b>350</b>
<b>Notice</b>: Trying to get property of non-object in <b>/Users/USER/Documents/my-api/wp/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php</b> on line <b>354</b>
<b>Notice</b>: Trying to get property of non-object in <b>/Users/USER/Documents/my-api/wp/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php</b> on line <b>354</b>
....
Which must refer to these lines: https://developer.wordpress.org/reference/classes/wp_rest_revisions_controller/ (line 350 and following).
After the notices I print the $response
and I get this:
WP_REST_Response Object
(
[links:protected] => Array
(
)
[matched_route:protected] =>
[matched_handler:protected] =>
[data] => Array
(
[author] => 0
[date] =>
[date_gmt] =>
[id] =>
[modified] =>
[modified_gmt] =>
[parent] => 0
[slug] =>
[guid] => Array
(
[rendered] =>
[raw] =>
)
)
[headers] => Array
(
)
[status] => 200
)
.. somehow the data is missing or something else went wrong.
And here is the whole php script to see what I am doing:
<?php
/**
* Add a new API route for a post or pages preview
*/
class Preview_To_REST_API_Controller extends WP_REST_Controller {
//The namespace and version for the REST SERVER
var $namespace = 'previews/v';
var $version = '1';
public function register_routes() {
$namespace = $this->namespace . $this->version;
$base = 'preview';
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_preview' ),
'permission_callback' => array( $this, 'get_permission' )
)
) );
}
// Register our REST Server
public function hook_rest_server(){
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
public function get_permission(){
if ( ! current_user_can( 'edit_posts' ) ) {
return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permissions to view this data.', 'my-text-domain' ), array( 'status' => 401 ) );
}
// This approach blocks the endpoint operation. You could alternatively do this by an un-blocking approach, by returning false here and changing the permissions check.
return true;
}
public function get_preview( WP_REST_Request $request ){
// use the helper methods to get the parameters
$id = $request->get_param( 'id' );
// Only return the newest
$args = array (
'order' => 'DESC',
'orderby' => 'date',
'posts_per_page' => '1'
);
// Preview version is saved in the latest revision of the page/post
$latest_revision = wp_get_post_revisions( $id, $args );
print_r($latest_revision);
$postController = new \WP_REST_Revisions_Controller('revision');
$response = $postController->prepare_item_for_response( $latest_revision, $request );
print_r($response);
if ($latest_revision) {
// Use the current method to get the only element in the revisions array
// [0] does not return anything, because the Array's value is saved
// as the ID key: Array[$id], but this ID we need to find out first
$revision_id = current($latest_revision) -> ID;
$acf_fields = get_fields($revision_id);
if ( empty($latest_revision) ) {
return null;
}
// Add acf fields to latest revision
$latest_revision[$revision_id] -> acf = $acf_fields;
return $latest_revision;
} else {
return null;
}
}
}
$preview_controller = new Preview_To_REST_API_Controller();
$preview_controller->hook_rest_server();
I would be really glad to receive any hints for solving this problem. Cheers
I haven't seen the result of your print_r
but I'm going to guess that it's an array of WP_Post
objects. prepare_item_for_response
requires a single WP_Post
as the first argument and you're passing it an array.
Try this, after you set $latest_revision
:
if (!is_array($latest_revision) || !count($latest_revision))
return null;
$latest_revision = array_values($latest_revision)[0];
The array_values
call is a quick and easy way to re-index your array.
This should give you a single post and not an array.
Update: per your own answer to your question and the questions you asked.
prepare_item_for_response
rewrites a WP_Post
object as something that can be serialized uniformly by the REST controller to output. For instance, it handles attachments rather than simply ignoring them. If you simply returned a WP_Post
object over, say, JSON, you'd miss out on a lot of the post content. You can think of prepare_response_for_collection
as the array version of the same thing. Really, it's more like the WP_Query
version of the same thing so the WP rest controller can act as one-stop shopping to return lists of WP_Post
objects to a REST consumer.
rest_ensure_response
does something similar for any REST response. It hides the loose typing of PHP (and WP), where actions like function calls can return nothing or indeterminate things, from REST, where every request must have an appropriate response. It's not much more than a wrapper that is aware of WP_Error
.