概要

エンドポイントを書く際には、エンドポイントの機能を処理するためにコントローラークラスを使用することが役立ちます。コントローラークラスは、APIと対話するための標準的な方法を提供し、APIとの対話をより保守可能な方法にします。WordPressの現在の最小PHPバージョンは5.2であり、WordPressエコシステム全体で使用されるエンドポイントを開発する場合は、WordPressの最小要件をサポートすることを検討すべきです。

PHP 5.2には名前空間が組み込まれていません。これは、宣言したすべての関数がグローバルスコープに存在することを意味します。get_items()のようなエンドポイントのために一般的な関数名を使用することに決め、別のプラグインもその関数を登録した場合、PHPは致命的なエラーで失敗します。これは、関数get_items()が二重に宣言されているためです。エンドポイントをラップすることで、これらの名前の衝突を回避し、APIと対話するための一貫した方法を持つことができます。

コントローラー

コントローラーは通常、1つのことを行います。入力を受け取り、出力を生成します。WordPress REST APIのために、私たちのコントローラーはリクエスト入力をWP_REST_Requestオブジェクトとして処理し、レスポンス出力をWP_REST_Responseオブジェクトとして生成します。例としてコントローラークラスを見てみましょう:

  1. class My_REST_Posts_Controller {
  2. // Here initialize our namespace and resource name.
  3. public function __construct() {
  4. $this->namespace = '/my-namespace/v1';
  5. $this->resource_name = 'posts';
  6. }
  7. // Register our routes.
  8. public function register_routes() {
  9. register_rest_route( $this->namespace, '/' . $this->resource_name, array(
  10. // Here we register the readable endpoint for collections.
  11. array(
  12. 'methods' => 'GET',
  13. 'callback' => array( $this, 'get_items' ),
  14. 'permission_callback' => array( $this, 'get_items_permissions_check' ),
  15. ),
  16. // Register our schema callback.
  17. 'schema' => array( $this, 'get_item_schema' ),
  18. ) );
  19. register_rest_route( $this->namespace, '/' . $this->resource_name . '/(?P<id>[\d]+)', array(
  20. // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
  21. array(
  22. 'methods' => 'GET',
  23. 'callback' => array( $this, 'get_item' ),
  24. 'permission_callback' => array( $this, 'get_item_permissions_check' ),
  25. ),
  26. // Register our schema callback.
  27. 'schema' => array( $this, 'get_item_schema' ),
  28. ) );
  29. }
  30. /**
  31. * Check permissions for the posts.
  32. *
  33. * @param WP_REST_Request $request Current request.
  34. */
  35. public function get_items_permissions_check( $request ) {
  36. if ( ! current_user_can( 'read' ) ) {
  37. return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view the post resource.' ), array( 'status' => $this->authorization_status_code() ) );
  38. }
  39. return true;
  40. }
  41. /**
  42. * Grabs the five most recent posts and outputs them as a rest response.
  43. *
  44. * @param WP_REST_Request $request Current request.
  45. */
  46. public function get_items( $request ) {
  47. $args = array(
  48. 'post_per_page' => 5,
  49. );
  50. $posts = get_posts( $args );
  51. $data = array();
  52. if ( empty( $posts ) ) {
  53. return rest_ensure_response( $data );
  54. }
  55. foreach ( $posts as $post ) {
  56. $response = $this->prepare_item_for_response( $post, $request );
  57. $data[] = $this->prepare_response_for_collection( $response );
  58. }
  59. // Return all of our comment response data.
  60. return rest_ensure_response( $data );
  61. }
  62. /**
  63. * Check permissions for the posts.
  64. *
  65. * @param WP_REST_Request $request Current request.
  66. */
  67. public function get_item_permissions_check( $request ) {
  68. if ( ! current_user_can( 'read' ) ) {
  69. return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view the post resource.' ), array( 'status' => $this->authorization_status_code() ) );
  70. }
  71. return true;
  72. }
  73. /**
  74. * Grabs the five most recent posts and outputs them as a rest response.
  75. *
  76. * @param WP_REST_Request $request Current request.
  77. */
  78. public function get_item( $request ) {
  79. $id = (int) $request['id'];
  80. $post = get_post( $id );
  81. if ( empty( $post ) ) {
  82. return rest_ensure_response( array() );
  83. }
  84. $response = prepare_item_for_response( $post );
  85. // Return all of our post response data.
  86. return $response;
  87. }
  88. /**
  89. * Matches the post data to the schema we want.
  90. *
  91. * @param WP_Post $post The comment object whose response is being prepared.
  92. */
  93. public function prepare_item_for_response( $post, $request ) {
  94. $post_data = array();
  95. $schema = $this->get_item_schema( $request );
  96. // We are also renaming the fields to more understandable names.
  97. if ( isset( $schema['properties']['id'] ) ) {
  98. $post_data['id'] = (int) $post->ID;
  99. }
  100. if ( isset( $schema['properties']['content'] ) ) {
  101. $post_data['content'] = apply_filters( 'the_content', $post->post_content, $post );
  102. }
  103. return rest_ensure_response( $post_data );
  104. }
  105. /**
  106. * Prepare a response for inserting into a collection of responses.
  107. *
  108. * This is copied from WP_REST_Controller class in the WP REST API v2 plugin.
  109. *
  110. * @param WP_REST_Response $response Response object.
  111. * @return array Response data, ready for insertion into collection data.
  112. */
  113. public function prepare_response_for_collection( $response ) {
  114. if ( ! ( $response instanceof WP_REST_Response ) ) {
  115. return $response;
  116. }
  117. $data = (array) $response->get_data();
  118. $server = rest_get_server();
  119. if ( method_exists( $server, 'get_compact_response_links' ) ) {
  120. $links = call_user_func( array( $server, 'get_compact_response_links' ), $response );
  121. } else {
  122. $links = call_user_func( array( $server, 'get_response_links' ), $response );
  123. }
  124. if ( ! empty( $links ) ) {
  125. $data['_links'] = $links;
  126. }
  127. return $data;
  128. }
  129. /**
  130. * Get our sample schema for a post.
  131. *
  132. * @param WP_REST_Request $request Current request.
  133. */
  134. public function get_item_schema( $request ) {
  135. $schema = array(
  136. // This tells the spec of JSON Schema we are using which is draft 4.
  137. '$schema' => 'http://json-schema.org/draft-04/schema#',
  138. // The title property marks the identity of the resource.
  139. 'title' => 'post',
  140. 'type' => 'object',
  141. // In JSON Schema you can specify object properties in the properties attribute.
  142. 'properties' => array(
  143. 'id' => array(
  144. 'description' => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
  145. 'type' => 'integer',
  146. 'context' => array( 'view', 'edit', 'embed' ),
  147. 'readonly' => true,
  148. ),
  149. 'content' => array(
  150. 'description' => esc_html__( 'The content for the object.', 'my-textdomain' ),
  151. 'type' => 'string',
  152. ),
  153. ),
  154. );
  155. return $schema;
  156. }
  157. // Sets up the proper HTTP status code for authorization.
  158. public function authorization_status_code() {
  159. $status = 401;
  160. if ( is_user_logged_in() ) {
  161. $status = 403;
  162. }
  163. return $status;
  164. }
  165. }
  166. // Function to register our new routes from the controller.
  167. function prefix_register_my_rest_routes() {
  168. $controller = new My_REST_Posts_Controller();
  169. $controller->register_routes();
  170. }
  171. add_action( 'rest_api_init', 'prefix_register_my_rest_routes' );

概要と未来

コントローラークラスは、エンドポイントを開発する際に2つの大きな問題に対処します。名前空間の欠如と一貫した構造です。エンドポイントの継承を乱用しないことが重要です。例えば、上記の例のように投稿エンドポイントのためにコントローラークラスを書き、カスタム投稿タイプもサポートしたい場合、My_REST_Posts_Controllerをこのようにclass My_CPT_REST_Controller extends My_REST_Posts_Controllerのように拡張してはいけません。

代わりに、まったく別のコントローラークラスを作成するか、My_REST_Posts_Controllerがすべての利用可能な投稿タイプを処理するようにするべきです。継承の暗い深淵に進むときは、親クラスがいつでも変更される可能性があり、サブクラスがそれに依存している場合、大きな頭痛の種になることを理解することが重要です。ほとんどの場合、interfaceまたはabstract classとしてベースコントローラークラスを作成し、各エンドポイントコントローラーが実装または拡張できるようにしたいでしょう。abstract classアプローチは、WP_REST_Controllerクラスのコアへの潜在的な統合のためにWP REST APIチームによって採用されています。

現在、投稿、投稿タイプ、投稿ステータス、リビジョン、タクソノミー、用語、ユーザー、コメント、および添付ファイル/メディアリソースをサポートする「コアエンドポイント」が、最終的にWordPressコアに移行されることを期待している機能プラグインで開発されています。このプラグイン内には、エンドポイント用の独自のコントローラーを構築するために使用できる提案されたWP_REST_Controllerクラスがあります。WP_REST_Controllerは、多くの利点とAPIのためのエンドポイントを作成するための一貫した方法を特徴としています。