为 WP REST API 中的输出准备 Wordpress Post 修订版

Prepare Wordpress Post Revision for Output in WP REST API

我为 WordPress REST API 构建了一个自定义端点以获得 post 修订。结果在 Wordpress Post 语法中:

[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

而 Wordpress REST API 会给我这样的东西:

"author": 1,
"title": {
    "rendered": "My title"
},
"content": {
    "rendered": "",
    "protected": false
},

(一个是通过 php 打印的,另一个是 JSON 格式,但重要的是在第一个例子中它说: post_content 而在第二个例子中它是例如 content 然后在 renderedprotected 中分开。

我很确定它与此处描述的几乎相同: https://wordpress.stackexchange.com/questions/236249/wp-api-v2-custom-endpoint-response-formatting?newreg=7edb54e1ae494e528e5e146982469664

但就我而言,我有修改。

我试图为 REST API 准备 Post 对象。 我创建了一个 WP_REST_Revisions_Controller 的新实例并尝试使用它的方法 prepare_item_for_response。 $request 是 WP_Rest_Request。 (顺便说一句:为什么我必须在 new \WP_REST_Revisions_Controller 之前写一个反斜杠 \)。

$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);

问题是我收到通知:

<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>
....    

其中必须引用这些行: https://developer.wordpress.org/reference/classes/wp_rest_revisions_controller/(第 350 行及以下)。

在我打印 $response 通知后,我得到了这个:

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
)

..不知何故数据丢失或其他错误。

这里是整个 php 脚本,看看我在做什么:

<?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();

如果能收到解决此问题的任何提示,我将非常高兴。 干杯

我还没有看到你的 print_r 的结果,但我猜它是一个 WP_Post 对象的数组。 prepare_item_for_response 需要一个 WP_Post 作为第一个参数,您要将它传递给一个数组。

设置 $latest_revision 后试试这个:

if (!is_array($latest_revision) || !count($latest_revision))
    return null;
$latest_revision = array_values($latest_revision)[0];

array_values 调用是重新索引数组的一种快速简便的方法。

这应该给你一个 post 而不是一个数组。

更新: 根据你自己对问题的回答和你提出的问题。

prepare_item_for_response将一个WP_Post对象重写为可以被REST控制器统一序列化输出的东西。例如,它处理附件而不是简单地忽略它们。如果您只是 return 编辑一个 WP_Post 对象,比如说 JSON,您会错过很多 post 内容。您可以将 prepare_response_for_collection 视为同一事物的数组版本。真的,它更像是同一事物的 WP_Query 版本,因此 WP rest 控制器可以作为一站式购物,向 REST 消费者提供 return WP_Post 对象列表。

rest_ensure_responseany REST 响应做了类似的事情。它隐藏了 PHP(和 WP)的松散类型,其中像函数调用这样的操作可以 return 没有或不确定的东西,来自 REST,其中每个请求 必须 适当的回应。它只不过是一个知道 WP_Error.

的包装器

我想我找到了解决办法: 使用 $postController = new \WP_REST_Revisions_Controller('revision'); 不会 return 内容字段,所以我不得不使用 $postController = new \WP_REST_Posts_Controller('post'); 尽管我的结果实际上是 'revision'.

此外,我使用了prepare_item_for_responseprepare_response_for_collectionrest_ensure_response。不幸的是,我真的不知道这些方法实际上在做什么......?

我的新密码:

/**
 * 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 );

        if (!is_array($latest_revision) || !count($latest_revision)){
            return null;
        }
        $latest_revision = array_values($latest_revision)[0];

        $postController = new \WP_REST_Posts_Controller('post');
        $response = $postController->prepare_item_for_response( $latest_revision, $request );
        $data = $postController->prepare_response_for_collection( $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 = $latest_revision -> ID;
            $acf_fields = get_fields($revision_id);

            if ( empty($latest_revision) ) {
                return null;
            }

            // Add acf fields to latest revision
            $data['acf'] = $acf_fields;

            return rest_ensure_response($data);

        } else {
            return null;
        }
    }
}

$preview_controller = new Preview_To_REST_API_Controller();
$preview_controller->hook_rest_server();

这给了我整洁的结果,如:

{
    "id": 478,
    "date": "2017-11-20T23:51:10",
    "date_gmt": "2017-11-20T22:51:10",
    "guid": {
        "rendered": "http://localhost:3000/51-autosave-v1/"
    },
    "modified": "2017-11-20T23:51:10",
    "modified_gmt": "2017-11-20T22:51:10",
    "slug": "51-autosave-v1",
    "status": "inherit",
    "type": "revision",
    "link": "http://localhost:3000/51-autosave-v1/",
    "title": {
        "rendered": "my title"
    },
        "content": {
        "rendered": "",
        "protected": false
    },
    "excerpt": {
        "rendered": "",
        "protected": false
    },
    "author": 1,
    "featured_media": 0,
    "comment_status": "closed",
    "ping_status": "closed",
    "sticky": false,
    "template": "", ... etc.

如果有人想解释一下我的所作所为,我很乐意阅读。

干杯