WordPress как на ладони
Хостинг, VPS/VDS и отдельные сервера только на SSD дисках. 7 дней бесплатного тестирования.

Классы контроллеров

В этом разделе описаны все PHP классы, которые используются в WP API. Они делятся на два типа: Классы инфраструктуры и Классы контроллеров (маршрутов, конечных точек, ресурсов).

Общие сведения

В WP API используется принцип Model-View-Controller — это стандартная модель в разработке приложений. Суть этой модели в том, что логика приложения разделена таким образом, что можно изменять одну его часть, так чтобы изменения не повлияли на другую его часть.

По такой схеме, логика кода WP-API разделена на независимые компоненты (классы):

  • Класс получения запроса.
  • Классы контроллеров (обработка запросы и создание ответа).
  • Класс ответа на запрос (получает подготовленный контроллером ответ и формирует его в стандартный JSON вид, устанавливает заголовки ответа и код ответа).

Класс запроса и ответа - это Классы инфраструктуры WP API, с ними как правило работать не нужно. Мы, разработчики, почти всегда будем работать с Классом контроллера.

Задача Класса контроллера объединить отдельные части обработки запроса API в единый механизм. В них должны создаваться маршруты, они получают запросы обрабатывают их и генерируют ответы. Так же там описывается схема ресурса.

Концепция контроллера принята в рамках WP-API для того, чтобы иметь стандартный шаблон для Классов контроллера - классов представляющих ресурсы (конечные точки). Шаблоном класса контроллера является абстрактный класс WP_REST_Controller. Каждый класс контроллера должен иметь аналогичную схему методов, сделано так для того, чтобы все конечные точки имели одинаковые названия PHP методов.

меню

Классы инфраструктуры и Классы контроллеров

Классы инфраструктуры дополняют классы контроллеров - они обрабатывают логику API, но не выполняют преобразований данных. Также и классы контроллеров (конечных точек) инкапсулируют логику необходимую для выполнения CRUD операций с ресурсами, но не связаны с логикой инфраструктуры. Это и есть упомянутая выше модель «Model-View-Controller».

К классам инфраструктуры относятся:

  • WP_REST_Server — фундаментальный контроллер REST API, который обслуживает запрос. При любом запросе к API сначала вызывается WP_REST_Server и определяет, какой запрашивается маршрут, а затем передает коллбэк маршрута в объект WP_REST_Request. WP_REST_Server также выполняет проверку аутентификации и проверки валидации и доступа (validation и permissions).
  • WP_REST_Request — этот объект содержит сведения о запросе, такие как заголовки запроса, параметры и метод, а также сам маршрут. Он также может проверять и очищать запрос (validation и sanitization).
  • WP_REST_Response — отвечает за ответ на запрос. Устанавливает заголовки ответа, статус, тело ответа (JSON). Предоставляет вспомогательные методы add_link() (добавляет связанные с ответом ссылки) и query_navigation_headers() (данные навигации в заголовках).

К классам контроллеров относятся:

Все классы связанные с REST API можно посмотреть по этой ссылке.

меню

Пример регистрации маршрута через Класс контроллера

Подробное описание этого примера смотрите в разделе Создание маршрутов.

// Запускаем наш контроллер и регистрируем маршруты
add_action( 'rest_api_init', 'prefix_register_my_rest_routes' );
function prefix_register_my_rest_routes() {
	$controller = new My_REST_Posts_Controller();
	$controller->register_routes();
}

class My_REST_Posts_Controller extends WP_REST_Controller {

	## Инициализация, во время которой указываем namespace и resource_name
	function __construct() {
		$this->namespace     = 'my-namespace/v1';
		$this->resource_name = 'posts';
	}

	## Регистрация маршрутов
	function register_routes() {

		register_rest_route( $this->namespace, "/$this->resource_name", array(
			// конечная точка для чтения коллекции ресурсов
			array(
				'methods'   => 'GET',
				'callback'  => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
			),
			// схема ресурса
			'schema' => array( $this, 'get_item_schema' ),
		) );

		register_rest_route( $this->namespace, "/$this->resource_name/(?P<id>[\d]+)", array(
			// конечная точка для чтения отдельного ресурса
			array(
				'methods'   => 'GET',
				'callback'  => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
			),
			// схема ресурса
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	## Проверяет доступ к чтению ресурсов.
	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->error_status_code() ) );

		return true;
	}

	/**
	 * Получает последние посты и отдает их в виде rest ответа.
	 *
	 * @param WP_REST_Request $request Текущий запрос.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	function get_items( $request ) {
		$data = array();

		$posts = get_posts( array(
			'post_per_page' => 5,
		) );

		if ( empty( $posts ) )
			return $data;

		foreach ( $posts as $post ) {
			$response = $this->prepare_item_for_response( $post, $request );
			$data[] = $this->prepare_response_for_collection( $response );
		}

		return $data;
	}

	## Проверка права доступа.
	function get_item_permissions_check( $request ) {
		return $this->get_items_permissions_check();
	}

	/**
	 * Получает отдельный ресурс.
	 *
	 * @param WP_REST_Request $request Текущий запрос.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	function get_item( $request ) {
		$id = (int) $request['id'];
		$post = get_post( $id );

		if ( empty( $post ) )
			return array();

		return prepare_item_for_response( $post, $request );
	}

	/**
	 * Собирает данные ресурса в соответствии со схемой ресурса.
	 *
	 * @param WP_Post         $post    Объект ресурса, из которого будут взяты оригинальные данные.
	 * @param WP_REST_Request $request Текущий запрос.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	function prepare_item_for_response( $post, $request ) {
		$post_data = array();

		$schema = $this->get_item_schema();

		// 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 $post_data;
	}

	## Подготавливает ответ отдельного ресурса для добавления его в коллекцию ресурсов.
	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;
	}

	## Схема ресурса.
	function get_item_schema() {
		$schema = array(
			// // показывает какую версию схемы мы используем - это draft 4
			'$schema'              => 'http://json-schema.org/draft-04/schema#',
			// определяет ресурс который описывает схема
			'title'                => 'post',
			'type'                 => 'object',
			// в JSON схеме нужно указывать свойства в атрибуете 'properties'.
			'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 $schema;
	}

	## Устанавливает HTTP статус код для авторизации.
	function error_status_code() {
		return is_user_logged_in() ? 403 : 401;
	}

}
меню

Принципы Классов контроллеров в WP API

Классы контроллеров решают две проблемы при разработке эндпоинтов:

  • Отсутствие пространств имен — WordPress не использует пространства имён PHP (для поддержки PHP 5.2). Оборачивание группы PHP функций которые описывают конечную точку в класс, позволяет избежать конфликтов с названиями функций. Например, мы назвали функцию get_items() и другой разработчик сделал тоже самое, в результате сайт перестанет работать из-за фатальной ошибки PHP.
  • Согласование структур — Один раз разобравшись со структурой класса контроллера, разработчики смогут быстро понимать структуру класса контроллера, которую писал другой человек.

Для классов контроллеров разработчиками создан специальный шаблон: абстрактный класс WP_REST_Controller. На его основе рекомендуется создавать свои контроллеры маршрутов. Так например, все классы конечных точек WP расширяют WP_REST_Controller, например:

class WP_REST_Posts_Controller extends WP_REST_Controller {
	// ...
}

Методы класса-шаблона WP_REST_Controller:

Метод Описание метода
register_routes() вызывается при первом создания экземпляра класса и регистрирует маршруты.
get_items() получает коллекцию ресустов (постов, рубрик и т.д.).
get_item() получает отдельный ресурс (пост, рубрику и т.д.). Если ресурса не существует, будет возвращен HTTP статус 404. Если нет права просматривать ресурс, то статус будет 403.
create_item() создает новый ресурс. Если создание прошло успешно, то WP_REST_Response вернет HTTP статус 201 и ссылку на созданный ресурс. Если создание не удалось, то будет возвращен соответствующий код ошибки в HTTP заголовке.
update_item() обновляет существующий ресурс.
delete_item() удаляет существующий ресурс. Если удаление не удалось, то будет возвращен соответствующий код ошибки в HTTP заголовке.
get_items_permissions_check() перед вызовом коллбэк функции проверяет есть ли право у текущего запроса получать коллекцию ресурсов.
get_item_permissions_check() перед вызовом коллбэк функции проверяет есть ли право у текущего запроса получать отдельный ресурс.
create_item_permissions_check() перед вызовом коллбэк функции проверяет есть ли право у текущего запроса создавать ресурс.
update_item_permissions_check() перед вызовом коллбэк функции проверяет есть ли право у текущего запроса обновлять ресурс.
delete_item_permissions_check() перед вызовом коллбэк функции проверяет есть ли право у текущего запроса удалять ресурс.
prepare_item_for_response() изменяет данные ресурса, чтобы они подходили под схему ответа.
prepare_response_for_collection() prepare_item_for_response() возвращает объект WP_REST_Response. Это вспомогательная функция, которая собирает такие ответы в общую коллекцию ресурсов и возвращает эту коллекцию в качестве ответа.
filter_response_by_context() фильтрует данные ресурса в соответствии с указанным параметром context.
get_item_schema() получает объект схемы ресурса.
меню

Заметка про наследование классов

Не следует злоупотреблять наследованием классов. Например: если мы написали класс контроллера для эндпоинта записей (пример выше) и хотим также поддерживать произвольные типы записей, то не следует расширять свой класс My_REST_Posts_Controller следующим образом:

class My_CPT_REST_Controller extends My_REST_Posts_Controller {
	...
}

Вместо этого нужно либо создать отдельный класс контроллера, либо заставить My_REST_Posts_Controller обрабатывать все доступные типы записей. Дело в том, что родительский класс (тот который наследуется) может измениться, а наши подклассы зависят от него, в результате получаем головную боль. Поэтому, если нужна общая структура для классов, необходимо создать свой базовый класс контроллера в виде интерфейса или абстрактного класса, а затем использовать его как основу для подклассов. Такой подход абстрактного класса используется во многих конечных точках REST API WordPress. Например, классы WP_REST_Posts_Controller, WP_REST_Terms_Controller и т.д. расширяют абстрактный класс WP_REST_Controller.

Комментариев нет
    Здравствуйте, !     Войти . Зарегистрироваться