[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-search-controller.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Search_Controller class
   4   *
   5   * @package WordPress
   6   * @subpackage REST_API
   7   * @since 5.0.0
   8   */
   9  
  10  /**
  11   * Core class to search through all WordPress content via the REST API.
  12   *
  13   * @since 5.0.0
  14   *
  15   * @see WP_REST_Controller
  16   */
  17  class WP_REST_Search_Controller extends WP_REST_Controller {
  18  
  19      /**
  20       * ID property name.
  21       */
  22      const PROP_ID = 'id';
  23  
  24      /**
  25       * Title property name.
  26       */
  27      const PROP_TITLE = 'title';
  28  
  29      /**
  30       * URL property name.
  31       */
  32      const PROP_URL = 'url';
  33  
  34      /**
  35       * Type property name.
  36       */
  37      const PROP_TYPE = 'type';
  38  
  39      /**
  40       * Subtype property name.
  41       */
  42      const PROP_SUBTYPE = 'subtype';
  43  
  44      /**
  45       * Identifier for the 'any' type.
  46       */
  47      const TYPE_ANY = 'any';
  48  
  49      /**
  50       * Search handlers used by the controller.
  51       *
  52       * @since 5.0.0
  53       * @var array
  54       */
  55      protected $search_handlers = array();
  56  
  57      /**
  58       * Constructor.
  59       *
  60       * @since 5.0.0
  61       *
  62       * @param array $search_handlers List of search handlers to use in the controller. Each search
  63       *                               handler instance must extend the `WP_REST_Search_Handler` class.
  64       */
  65  	public function __construct( array $search_handlers ) {
  66          $this->namespace = 'wp/v2';
  67          $this->rest_base = 'search';
  68  
  69          foreach ( $search_handlers as $search_handler ) {
  70              if ( ! $search_handler instanceof WP_REST_Search_Handler ) {
  71                  _doing_it_wrong(
  72                      __METHOD__,
  73                      /* translators: %s: PHP class name. */
  74                      sprintf( __( 'REST search handlers must extend the %s class.' ), 'WP_REST_Search_Handler' ),
  75                      '5.0.0'
  76                  );
  77                  continue;
  78              }
  79  
  80              $this->search_handlers[ $search_handler->get_type() ] = $search_handler;
  81          }
  82      }
  83  
  84      /**
  85       * Registers the routes for the objects of the controller.
  86       *
  87       * @since 5.0.0
  88       *
  89       * @see register_rest_route()
  90       */
  91  	public function register_routes() {
  92          register_rest_route(
  93              $this->namespace,
  94              '/' . $this->rest_base,
  95              array(
  96                  array(
  97                      'methods'             => WP_REST_Server::READABLE,
  98                      'callback'            => array( $this, 'get_items' ),
  99                      'permission_callback' => array( $this, 'get_items_permission_check' ),
 100                      'args'                => $this->get_collection_params(),
 101                  ),
 102                  'schema' => array( $this, 'get_public_item_schema' ),
 103              )
 104          );
 105      }
 106  
 107      /**
 108       * Checks if a given request has access to search content.
 109       *
 110       * @since 5.0.0
 111       *
 112       * @param WP_REST_Request $request Full details about the request.
 113       * @return true|WP_Error True if the request has search access, WP_Error object otherwise.
 114       */
 115  	public function get_items_permission_check( $request ) {
 116          return true;
 117      }
 118  
 119      /**
 120       * Retrieves a collection of search results.
 121       *
 122       * @since 5.0.0
 123       *
 124       * @param WP_REST_Request $request Full details about the request.
 125       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 126       */
 127  	public function get_items( $request ) {
 128          $handler = $this->get_search_handler( $request );
 129          if ( is_wp_error( $handler ) ) {
 130              return $handler;
 131          }
 132  
 133          $result = $handler->search_items( $request );
 134  
 135          if ( ! isset( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! is_array( $result[ WP_REST_Search_Handler::RESULT_IDS ] ) || ! isset( $result[ WP_REST_Search_Handler::RESULT_TOTAL ] ) ) {
 136              return new WP_Error(
 137                  'rest_search_handler_error',
 138                  __( 'Internal search handler error.' ),
 139                  array( 'status' => 500 )
 140              );
 141          }
 142  
 143          $ids = array_map( 'absint', $result[ WP_REST_Search_Handler::RESULT_IDS ] );
 144  
 145          $results = array();
 146  
 147          foreach ( $ids as $id ) {
 148              $data      = $this->prepare_item_for_response( $id, $request );
 149              $results[] = $this->prepare_response_for_collection( $data );
 150          }
 151  
 152          $total     = (int) $result[ WP_REST_Search_Handler::RESULT_TOTAL ];
 153          $page      = (int) $request['page'];
 154          $per_page  = (int) $request['per_page'];
 155          $max_pages = ceil( $total / $per_page );
 156  
 157          if ( $page > $max_pages && $total > 0 ) {
 158              return new WP_Error(
 159                  'rest_search_invalid_page_number',
 160                  __( 'The page number requested is larger than the number of pages available.' ),
 161                  array( 'status' => 400 )
 162              );
 163          }
 164  
 165          $response = rest_ensure_response( $results );
 166          $response->header( 'X-WP-Total', $total );
 167          $response->header( 'X-WP-TotalPages', $max_pages );
 168  
 169          $request_params = $request->get_query_params();
 170          $base           = add_query_arg( urlencode_deep( $request_params ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
 171  
 172          if ( $page > 1 ) {
 173              $prev_link = add_query_arg( 'page', $page - 1, $base );
 174              $response->link_header( 'prev', $prev_link );
 175          }
 176          if ( $page < $max_pages ) {
 177              $next_link = add_query_arg( 'page', $page + 1, $base );
 178              $response->link_header( 'next', $next_link );
 179          }
 180  
 181          return $response;
 182      }
 183  
 184      /**
 185       * Prepares a single search result for response.
 186       *
 187       * @since 5.0.0
 188       *
 189       * @param int             $id      ID of the item to prepare.
 190       * @param WP_REST_Request $request Request object.
 191       * @return WP_REST_Response Response object.
 192       */
 193  	public function prepare_item_for_response( $id, $request ) {
 194          $handler = $this->get_search_handler( $request );
 195          if ( is_wp_error( $handler ) ) {
 196              return new WP_REST_Response();
 197          }
 198  
 199          $fields = $this->get_fields_for_response( $request );
 200  
 201          $data = $handler->prepare_item( $id, $fields );
 202          $data = $this->add_additional_fields_to_object( $data, $request );
 203  
 204          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 205          $data    = $this->filter_response_by_context( $data, $context );
 206  
 207          $response = rest_ensure_response( $data );
 208  
 209          $links               = $handler->prepare_item_links( $id );
 210          $links['collection'] = array(
 211              'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
 212          );
 213          $response->add_links( $links );
 214  
 215          return $response;
 216      }
 217  
 218      /**
 219       * Retrieves the item schema, conforming to JSON Schema.
 220       *
 221       * @since 5.0.0
 222       *
 223       * @return array Item schema data.
 224       */
 225  	public function get_item_schema() {
 226          if ( $this->schema ) {
 227              return $this->add_additional_fields_schema( $this->schema );
 228          }
 229  
 230          $types    = array();
 231          $subtypes = array();
 232  
 233          foreach ( $this->search_handlers as $search_handler ) {
 234              $types[]  = $search_handler->get_type();
 235              $subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
 236          }
 237  
 238          $types    = array_unique( $types );
 239          $subtypes = array_unique( $subtypes );
 240  
 241          $schema = array(
 242              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 243              'title'      => 'search-result',
 244              'type'       => 'object',
 245              'properties' => array(
 246                  self::PROP_ID      => array(
 247                      'description' => __( 'Unique identifier for the object.' ),
 248                      'type'        => 'integer',
 249                      'context'     => array( 'view', 'embed' ),
 250                      'readonly'    => true,
 251                  ),
 252                  self::PROP_TITLE   => array(
 253                      'description' => __( 'The title for the object.' ),
 254                      'type'        => 'string',
 255                      'context'     => array( 'view', 'embed' ),
 256                      'readonly'    => true,
 257                  ),
 258                  self::PROP_URL     => array(
 259                      'description' => __( 'URL to the object.' ),
 260                      'type'        => 'string',
 261                      'format'      => 'uri',
 262                      'context'     => array( 'view', 'embed' ),
 263                      'readonly'    => true,
 264                  ),
 265                  self::PROP_TYPE    => array(
 266                      'description' => __( 'Object type.' ),
 267                      'type'        => 'string',
 268                      'enum'        => $types,
 269                      'context'     => array( 'view', 'embed' ),
 270                      'readonly'    => true,
 271                  ),
 272                  self::PROP_SUBTYPE => array(
 273                      'description' => __( 'Object subtype.' ),
 274                      'type'        => 'string',
 275                      'enum'        => $subtypes,
 276                      'context'     => array( 'view', 'embed' ),
 277                      'readonly'    => true,
 278                  ),
 279              ),
 280          );
 281  
 282          $this->schema = $schema;
 283  
 284          return $this->add_additional_fields_schema( $this->schema );
 285      }
 286  
 287      /**
 288       * Retrieves the query params for the search results collection.
 289       *
 290       * @since 5.0.0
 291       *
 292       * @return array Collection parameters.
 293       */
 294  	public function get_collection_params() {
 295          $types    = array();
 296          $subtypes = array();
 297  
 298          foreach ( $this->search_handlers as $search_handler ) {
 299              $types[]  = $search_handler->get_type();
 300              $subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
 301          }
 302  
 303          $types    = array_unique( $types );
 304          $subtypes = array_unique( $subtypes );
 305  
 306          $query_params = parent::get_collection_params();
 307  
 308          $query_params['context']['default'] = 'view';
 309  
 310          $query_params[ self::PROP_TYPE ] = array(
 311              'default'     => $types[0],
 312              'description' => __( 'Limit results to items of an object type.' ),
 313              'type'        => 'string',
 314              'enum'        => $types,
 315          );
 316  
 317          $query_params[ self::PROP_SUBTYPE ] = array(
 318              'default'           => self::TYPE_ANY,
 319              'description'       => __( 'Limit results to items of one or more object subtypes.' ),
 320              'type'              => 'array',
 321              'items'             => array(
 322                  'enum' => array_merge( $subtypes, array( self::TYPE_ANY ) ),
 323                  'type' => 'string',
 324              ),
 325              'sanitize_callback' => array( $this, 'sanitize_subtypes' ),
 326          );
 327  
 328          return $query_params;
 329      }
 330  
 331      /**
 332       * Sanitizes the list of subtypes, to ensure only subtypes of the passed type are included.
 333       *
 334       * @since 5.0.0
 335       *
 336       * @param string|array    $subtypes  One or more subtypes.
 337       * @param WP_REST_Request $request   Full details about the request.
 338       * @param string          $parameter Parameter name.
 339       * @return array|WP_Error List of valid subtypes, or WP_Error object on failure.
 340       */
 341  	public function sanitize_subtypes( $subtypes, $request, $parameter ) {
 342          $subtypes = wp_parse_slug_list( $subtypes );
 343  
 344          $subtypes = rest_parse_request_arg( $subtypes, $request, $parameter );
 345          if ( is_wp_error( $subtypes ) ) {
 346              return $subtypes;
 347          }
 348  
 349          // 'any' overrides any other subtype.
 350          if ( in_array( self::TYPE_ANY, $subtypes, true ) ) {
 351              return array( self::TYPE_ANY );
 352          }
 353  
 354          $handler = $this->get_search_handler( $request );
 355          if ( is_wp_error( $handler ) ) {
 356              return $handler;
 357          }
 358  
 359          return array_intersect( $subtypes, $handler->get_subtypes() );
 360      }
 361  
 362      /**
 363       * Gets the search handler to handle the current request.
 364       *
 365       * @since 5.0.0
 366       *
 367       * @param WP_REST_Request $request Full details about the request.
 368       * @return WP_REST_Search_Handler|WP_Error Search handler for the request type, or WP_Error object on failure.
 369       */
 370  	protected function get_search_handler( $request ) {
 371          $type = $request->get_param( self::PROP_TYPE );
 372  
 373          if ( ! $type || ! isset( $this->search_handlers[ $type ] ) ) {
 374              return new WP_Error(
 375                  'rest_search_invalid_type',
 376                  __( 'Invalid type parameter.' ),
 377                  array( 'status' => 400 )
 378              );
 379          }
 380  
 381          return $this->search_handlers[ $type ];
 382      }
 383  }


Generated: Sat Aug 8 01:00:03 2020 Cross-referenced by PHPXref 0.7.1