[API] Controller Classes

https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/

Để đăng ký một tuyến REST mới, bạn phải chỉ định một số hàm gọi lại để kiểm soát hành vi của điểm cuối, chẳng hạn như cách một yêu cầu được thực hiện, cách kiểm tra quyền được áp dụng và cách lược đồ cho tài nguyên của bạn được tạo. Mặc dù có thể khai báo tất cả các phương thức này trong một tệp PHP thông thường mà không có bất kỳ vùng tên hoặc lớp bao bọc nào, tất cả các hàm được khai báo theo cách đó cùng tồn tại trong cùng một phạm vi toàn cục. Nếu bạn quyết định sử dụng một tên hàm phổ biến cho logic điểm cuối của mình như get_items () và một plugin khác (hoặc một điểm cuối khác trong plugin của riêng bạn) cũng đăng ký một hàm có cùng tên đó, PHP sẽ không thành công với lỗi nghiêm trọng vì hàm get_items ( ) đang được khai báo hai lần.

Bạn có thể tránh vấn đề này bằng cách đặt tên cho các hàm gọi lại của mình bằng cách sử dụng tiền tố duy nhất như mypluginmyendpoint để tránh mọi rắc rối tiềm ẩn:

function myplugin_myendpoint_register_routes() { /* ... */ }
function myplugin_myendpoint_get_item() { /* ... */ }
function myplugin_myendpoint_get_item_schema() { /* ... */ }
// etcetera
 
add_action( 'rest_api_init', 'myplugin_myendpoint_register_routes' );

Bạn có thể đã quen với cách tiếp cận này vì nó thường được sử dụng trong các tệp tin function.php của theme. Tuy nhiên, các tiền tố này dài dòng một cách không cần thiết và tồn tại một số tùy chọn tốt hơn để nhóm và đóng gói logic của điểm cuối của bạn theo cách dễ bảo trì hơn.

WordPress hiện yêu cầu PHP 5.6 trở lên. PHP 5.6 hỗ trợ không gian tên, cung cấp một cách dễ dàng để đóng gói chức năng của điểm cuối của bạn. Bằng cách khai báo một vùng tên ở đầu tệp PHP của điểm cuối của bạn, tất cả các phương thức trong vùng tên đó sẽ được khai báo trong vùng tên đó và sẽ không còn xung đột với các hàm chung. Sau đó, bạn có thể sử dụng các tên ngắn hơn, dễ đọc hơn cho các lệnh gọi lại điểm cuối của mình.

namespace MyPlugin\API\MyEndpoint;
function register_routes() { /* ... */ }
function get_item() { /* ... */ }
function get_item_schema() { /* ... */ }
// and so on
add_action( 'rest_api_init', __NAMESPACE__ . '\\register_routes' );

Mặc dù các tên hàm ngắn hơn này dễ làm việc hơn nhưng chúng không mang lại bất kỳ lợi ích nào khác so với việc khai báo các hàm toàn cục. Vì lý do này, các điểm cuối REST API cốt lõi trong WordPress đều được triển khai bằng cách sử dụng lớp bộ điều khiển.

Phần còn lại của trang này trình bày chi tiết cách viết lớp bộ điều khiển của riêng bạn và giải thích những lợi ích của việc làm như vậy.

Controllers

Bộ điều khiển nhận đầu vào (đối tượng WP_REST_Request, trong trường hợp của WordPress REST API) và tạo đầu ra phản hồi dưới dạng đối tượng WP_REST_Response. Hãy xem một bộ điều khiển mẫu

class My_REST_Posts_Controller {
 
    // Here initialize our namespace and resource name.
    public function __construct() {
        $this->namespace     = '/my-namespace/v1';
        $this->resource_name = 'posts';
    }
 
    // Register our routes.
    public function register_routes() {
        register_rest_route( $this->namespace, '/' . $this->resource_name, array(
            // Here we register the readable endpoint for collections.
            array(
                'methods'   => 'GET',
                'callback'  => array( $this, 'get_items' ),
                'permission_callback' => array( $this, 'get_items_permissions_check' ),
            ),
            // Register our schema callback.
            'schema' => array( $this, 'get_item_schema' ),
        ) );
        register_rest_route( $this->namespace, '/' . $this->resource_name . '/(?P<id>[\d]+)', array(
            // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
            array(
                'methods'   => 'GET',
                'callback'  => array( $this, 'get_item' ),
                'permission_callback' => array( $this, 'get_item_permissions_check' ),
            ),
            // Register our schema callback.
            'schema' => array( $this, 'get_item_schema' ),
        ) );
    }
 
    /**
     * Check permissions for the posts.
     *
     * @param WP_REST_Request $request Current request.
     */
    public function get_items_permissions_check( $request ) {
        if ( ! current_user_can( 'read' ) ) {
            return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view the post resource.' ), array( 'status' => $this->authorization_status_code() ) );
        }
        return true;
    }
 
    /**
     * Grabs the five most recent posts and outputs them as a rest response.
     *
     * @param WP_REST_Request $request Current request.
     */
    public function get_items( $request ) {
        $args = array(
            'post_per_page' => 5,
        );
        $posts = get_posts( $args );
 
        $data = array();
 
        if ( empty( $posts ) ) {
            return rest_ensure_response( $data );
        }
 
        foreach ( $posts as $post ) {
            $response = $this->prepare_item_for_response( $post, $request );
            $data[] = $this->prepare_response_for_collection( $response );
        }
 
        // Return all of our comment response data.
        return rest_ensure_response( $data );
    }
 
    /**
     * Check permissions for the posts.
     *
     * @param WP_REST_Request $request Current request.
     */
    public function get_item_permissions_check( $request ) {
        if ( ! current_user_can( 'read' ) ) {
            return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view the post resource.' ), array( 'status' => $this->authorization_status_code() ) );
        }
        return true;
    }
 
    /**
     * Grabs the five most recent posts and outputs them as a rest response.
     *
     * @param WP_REST_Request $request Current request.
     */
    public function get_item( $request ) {
        $id = (int) $request['id'];
        $post = get_post( $id );
 
        if ( empty( $post ) ) {
            return rest_ensure_response( array() );
        }
 
        $response = $this->prepare_item_for_response( $post, $request );
 
        // Return all of our post response data.
        return $response;
    }
 
    /**
     * Matches the post data to the schema we want.
     *
     * @param WP_Post $post The comment object whose response is being prepared.
     */
    public function prepare_item_for_response( $post, $request ) {
        $post_data = array();
 
        $schema = $this->get_item_schema( $request );
 
        // We are also renaming the fields to more understandable names.
        if ( isset( $schema['properties']['id'] ) ) {
            $post_data['id'] = (int) $post->ID;
        }
 
        if ( isset( $schema['properties']['content'] ) ) {
            $post_data['content'] = apply_filters( 'the_content', $post->post_content, $post );
        }
 
        return rest_ensure_response( $post_data );
    }
 
    /**
     * Prepare a response for inserting into a collection of responses.
     *
     * This is copied from WP_REST_Controller class in the WP REST API v2 plugin.
     *
     * @param WP_REST_Response $response Response object.
     * @return array Response data, ready for insertion into collection data.
     */
    public function prepare_response_for_collection( $response ) {
        if ( ! ( $response instanceof WP_REST_Response ) ) {
            return $response;
        }
 
        $data = (array) $response->get_data();
        $server = rest_get_server();
 
        if ( method_exists( $server, 'get_compact_response_links' ) ) {
            $links = call_user_func( array( $server, 'get_compact_response_links' ), $response );
        } else {
            $links = call_user_func( array( $server, 'get_response_links' ), $response );
        }
 
        if ( ! empty( $links ) ) {
            $data['_links'] = $links;
        }
 
        return $data;
    }
 
    /**
     * Get our sample schema for a post.
     *
     * @param WP_REST_Request $request Current request.
     */
    public function get_item_schema( $request ) {
        if ( $this->schema ) {
            // Since WordPress 5.3, the schema can be cached in the $schema property.
            return $this->schema;
        }
 
        $this->schema = array(
            // This tells the spec of JSON Schema we are using which is draft 4.
            '$schema'              => 'http://json-schema.org/draft-04/schema#',
            // The title property marks the identity of the resource.
            'title'                => 'post',
            'type'                 => 'object',
            // In JSON Schema you can specify object properties in the properties attribute.
            'properties'           => array(
                'id' => array(
                    'description'  => esc_html__( 'Unique identifier for the object.', 'my-textdomain' ),
                    'type'         => 'integer',
                    'context'      => array( 'view', 'edit', 'embed' ),
                    'readonly'     => true,
                ),
                'content' => array(
                    'description'  => esc_html__( 'The content for the object.', 'my-textdomain' ),
                    'type'         => 'string',
                ),
            ),
        );
 
        return $this->schema;
    }
 
    // Sets up the proper HTTP status code for authorization.
    public function authorization_status_code() {
 
        $status = 401;
 
        if ( is_user_logged_in() ) {
            $status = 403;
        }
 
        return $status;
    }
}
 
// Function to register our new routes from the controller.
function prefix_register_my_rest_routes() {
    $controller = new My_REST_Posts_Controller();
    $controller->register_routes();
}
 
add_action( 'rest_api_init', 'prefix_register_my_rest_routes' );

Benefits of Classes

Lớp này chứa tất cả các thành phần giống nhau mà bạn có thể đã viết bằng các hàm đơn giản. Cấu trúc của một lớp cho chúng ta một cách thuận tiện để tham chiếu đến các phương thức liên quan bằng cách sử dụng cú pháp $ this-> method_name (), nhưng không giống như một vùng tên, lớp này cũng cho phép chúng ta lưu trữ các giá trị và chia sẻ logic.

Trong phương thức get_item_schema, lưu ý rằng chúng ta lưu trữ lược đồ đã tạo trên lớp dưới dạng lược đồ $ this->. Thuộc tính lớp giúp dễ dàng lưu vào bộ nhớ cache các loại giá trị được tạo này. Việc giới thiệu bộ nhớ đệm giản đồ trong WordPress 5.3 đã tăng tốc độ của một số phản hồi thu thập API REST cốt lõi lên tới 40%, vì vậy bạn chắc chắn nên cân nhắc làm theo mẫu này trong bộ điều khiển của riêng mình.

Class Inheritance & WP_REST_Controller

Ở trên chúng ta đã thấy cách các lớp giải quyết vấn đề đóng gói hàm toàn cục và cách một cá thể lớp có thể được sử dụng để lưu vào bộ nhớ cache các giá trị phức tạp nhằm tăng tốc xử lý phản hồi. Ưu điểm chính khác của các lớp là cách thức kế thừa lớp cho phép bạn chia sẻ logic giữa nhiều điểm cuối.

Lớp mẫu của chúng tôi ở đây không mở rộng bất kỳ lớp cơ sở nào, nhưng trong lõi WordPress, tất cả các bộ điều khiển điểm cuối đều mở rộng một lớp bộ điều khiển trừu tượng duy nhất được gọi là WP_REST_Controller. Việc mở rộng lớp này cho phép bạn truy cập vào một số phương pháp hữu ích, bao gồm nhưng không giới hạn ở:

  • standard_response_for_collection (): Chuẩn bị một phản hồi để chèn vào một bộ sưu tập.

  • add_additional_fields_to_object (): nối mọi trường REST đã đăng ký vào đối tượng phản hồi đã chuẩn bị của bạn.

  • get_fields_for_response (): kiểm tra tham số truy vấn _fields để xác định trường phản hồi nào đã được yêu cầu.

  • get_context_param (): Lấy tham số ngữ cảnh.

  • filter_response_by_context (): Lọc hình dạng phản hồi dựa trên tham số ngữ cảnh được cung cấp.

  • get_collection_params (): trả về một tập hợp các định nghĩa tham số cơ bản hữu ích cho các điểm cuối của bộ sưu tập.

Các phương thức dành riêng cho điểm cuối như get_item, register_routes và update_item_permissions_check không được lớp trừu tượng triển khai đầy đủ và phải được định nghĩa trong lớp của riêng bạn.

Điều quan trọng cần lưu ý là WP_REST_Controller được thực hiện như một lớp trừu tượng và chỉ chứa logic rõ ràng cần thiết trong nhiều lớp. Kế thừa kết hợp lớp của bạn với lớp cơ sở mà nó mở rộng và các cây kế thừa được coi là kém có thể khiến điểm cuối của bạn khó duy trì hơn nhiều.

Ví dụ: nếu bạn đã viết một lớp bộ điều khiển cho điểm cuối của bài đăng (như ví dụ ở trên) và cũng muốn hỗ trợ các loại bài đăng tùy chỉnh, bạn KHÔNG nên mở rộng My_REST_Posts_Controller của mình như thế này: class My_CPT_REST_Controller mở rộng My_REST_Posts_Controller. Thay vào đó, bạn nên tạo một lớp bộ điều khiển cơ sở hoàn toàn riêng biệt cho logic được chia sẻ hoặc làm cho My_REST_Posts_Controller xử lý tất cả các loại bài đăng có sẵn. Logic điểm cuối có thể thay đổi các yêu cầu kinh doanh và bạn không muốn phải thay đổi một số bộ điều khiển không liên quan mỗi khi bạn cập nhật bộ điều khiển bài đăng cơ sở của mình.

Trong hầu hết các trường hợp, bạn sẽ muốn tạo một lớp bộ điều khiển cơ sở dưới dạng giao diện hoặc lớp trừu tượng mà mỗi bộ điều khiển điểm cuối của bạn có thể triển khai hoặc mở rộng hoặc mở rộng trực tiếp một trong các lớp REST WordPress cốt lõi.

Internal WordPress REST API

API REST của WordPress tuân theo một mẫu thiết kế có chủ ý cho các lớp bên trong của nó, có thể được phân loại là lớp cơ sở hạ tầng hoặc lớp điểm cuối.

Các lớp điểm cuối đóng gói logic chức năng cần thiết để thực hiện các hoạt động CRUD trên tài nguyên WordPress. WordPress hiển thị nhiều điểm cuối API REST (chẳng hạn như WP_REST_Posts_Controller), nhưng như đã thảo luận ở trên, tất cả các điểm cuối đều mở rộng từ một lớp bộ điều khiển cơ sở chung:

WP_REST_Controller: Lớp cơ sở cho tất cả các điểm cuối cốt lõi của WordPress. Lớp này được thiết kế để đại diện cho một mẫu nhất quán để thao tác các tài nguyên WordPress. Khi tương tác với một điểm cuối triển khai WP_REST_Controller, một máy khách HTTP có thể mong đợi mỗi điểm cuối hoạt động theo một cách nhất quán.

Các lớp cơ sở hạ tầng hỗ trợ các lớp điểm cuối. Chúng xử lý logic cho WordPress REST API mà không thực hiện bất kỳ chuyển đổi dữ liệu nào. API REST của WordPress triển khai ba lớp cơ sở hạ tầng chính:

WP_REST_Server: Bộ điều khiển chính cho WordPress REST API. Các tuyến được đăng ký với máy chủ trong WordPress. Khi WP_REST_Server được gọi để phục vụ một yêu cầu, nó xác định tuyến nào sẽ được gọi và chuyển lệnh gọi lại tuyến một đối tượng WP_REST_Request. WP_REST_Server cũng xử lý xác thực và có thể thực hiện xác thực yêu cầu và kiểm tra quyền. WP_REST_Request: Một đối tượng đại diện cho bản chất của yêu cầu. Đối tượng này bao gồm các chi tiết yêu cầu như tiêu đề yêu cầu, tham số và phương thức, cũng như tuyến đường. Nó cũng có thể thực hiện xác thực và khử trùng yêu cầu. WP_REST_Response: Một đối tượng đại diện cho bản chất của phản hồi. Lớp này mở rộng WP_HTTP_Response, bao gồm tiêu đề, nội dung và trạng thái, đồng thời cung cấp các phương thức trợ giúp như add_link () để thêm phương tiện được liên kết và query_navigation_headers () để nhận tiêu đề điều hướng truy vấn.

Hầu hết các loại ứng dụng hướng API sẽ không yêu cầu bạn mở rộng hoặc tương tác trực tiếp với lớp cơ sở hạ tầng, nhưng nếu bạn đang triển khai các điểm cuối API REST của riêng mình, ứng dụng của bạn có thể sẽ được hưởng lợi từ một hoặc nhiều lớp bộ điều khiển điểm cuối mở rộng WP_REST_Controller.

Last updated