[ 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 = $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       * @since 5.6.0 The `$id` parameter can accept a string.
 189       *
 190       * @param int|string      $id      ID of the item to prepare.
 191       * @param WP_REST_Request $request Request object.
 192       * @return WP_REST_Response Response object.
 193       */
 194  	public function prepare_item_for_response( $id, $request ) {
 195          $handler = $this->get_search_handler( $request );
 196          if ( is_wp_error( $handler ) ) {
 197              return new WP_REST_Response();
 198          }
 199  
 200          $fields = $this->get_fields_for_response( $request );
 201  
 202          $data = $handler->prepare_item( $id, $fields );
 203          $data = $this->add_additional_fields_to_object( $data, $request );
 204  
 205          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 206          $data    = $this->filter_response_by_context( $data, $context );
 207  
 208          $response = rest_ensure_response( $data );
 209  
 210          $links               = $handler->prepare_item_links( $id );
 211          $links['collection'] = array(
 212              'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
 213          );
 214          $response->add_links( $links );
 215  
 216          return $response;
 217      }
 218  
 219      /**
 220       * Retrieves the item schema, conforming to JSON Schema.
 221       *
 222       * @since 5.0.0
 223       *
 224       * @return array Item schema data.
 225       */
 226  	public function get_item_schema() {
 227          if ( $this->schema ) {
 228              return $this->add_additional_fields_schema( $this->schema );
 229          }
 230  
 231          $types    = array();
 232          $subtypes = array();
 233  
 234          foreach ( $this->search_handlers as $search_handler ) {
 235              $types[]  = $search_handler->get_type();
 236              $subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
 237          }
 238  
 239          $types    = array_unique( $types );
 240          $subtypes = array_unique( $subtypes );
 241  
 242          $schema = array(
 243              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 244              'title'      => 'search-result',
 245              'type'       => 'object',
 246              'properties' => array(
 247                  self::PROP_ID      => array(
 248                      'description' => __( 'Unique identifier for the object.' ),
 249                      'type'        => array( 'integer', 'string' ),
 250                      'context'     => array( 'view', 'embed' ),
 251                      'readonly'    => true,
 252                  ),
 253                  self::PROP_TITLE   => array(
 254                      'description' => __( 'The title for the object.' ),
 255                      'type'        => 'string',
 256                      'context'     => array( 'view', 'embed' ),
 257                      'readonly'    => true,
 258                  ),
 259                  self::PROP_URL     => array(
 260                      'description' => __( 'URL to the object.' ),
 261                      'type'        => 'string',
 262                      'format'      => 'uri',
 263                      'context'     => array( 'view', 'embed' ),
 264                      'readonly'    => true,
 265                  ),
 266                  self::PROP_TYPE    => array(
 267                      'description' => __( 'Object type.' ),
 268                      'type'        => 'string',
 269                      'enum'        => $types,
 270                      'context'     => array( 'view', 'embed' ),
 271                      'readonly'    => true,
 272                  ),
 273                  self::PROP_SUBTYPE => array(
 274                      'description' => __( 'Object subtype.' ),
 275                      'type'        => 'string',
 276                      'enum'        => $subtypes,
 277                      'context'     => array( 'view', 'embed' ),
 278                      'readonly'    => true,
 279                  ),
 280              ),
 281          );
 282  
 283          $this->schema = $schema;
 284  
 285          return $this->add_additional_fields_schema( $this->schema );
 286      }
 287  
 288      /**
 289       * Retrieves the query params for the search results collection.
 290       *
 291       * @since 5.0.0
 292       *
 293       * @return array Collection parameters.
 294       */
 295  	public function get_collection_params() {
 296          $types    = array();
 297          $subtypes = array();
 298  
 299          foreach ( $this->search_handlers as $search_handler ) {
 300              $types[]  = $search_handler->get_type();
 301              $subtypes = array_merge( $subtypes, $search_handler->get_subtypes() );
 302          }
 303  
 304          $types    = array_unique( $types );
 305          $subtypes = array_unique( $subtypes );
 306  
 307          $query_params = parent::get_collection_params();
 308  
 309          $query_params['context']['default'] = 'view';
 310  
 311          $query_params[ self::PROP_TYPE ] = array(
 312              'default'     => $types[0],
 313              'description' => __( 'Limit results to items of an object type.' ),
 314              'type'        => 'string',
 315              'enum'        => $types,
 316          );
 317  
 318          $query_params[ self::PROP_SUBTYPE ] = array(
 319              'default'           => self::TYPE_ANY,
 320              'description'       => __( 'Limit results to items of one or more object subtypes.' ),
 321              'type'              => 'array',
 322              'items'             => array(
 323                  'enum' => array_merge( $subtypes, array( self::TYPE_ANY ) ),
 324                  'type' => 'string',
 325              ),
 326              'sanitize_callback' => array( $this, 'sanitize_subtypes' ),
 327          );
 328  
 329          return $query_params;
 330      }
 331  
 332      /**
 333       * Sanitizes the list of subtypes, to ensure only subtypes of the passed type are included.
 334       *
 335       * @since 5.0.0
 336       *
 337       * @param string|array    $subtypes  One or more subtypes.
 338       * @param WP_REST_Request $request   Full details about the request.
 339       * @param string          $parameter Parameter name.
 340       * @return array|WP_Error List of valid subtypes, or WP_Error object on failure.
 341       */
 342  	public function sanitize_subtypes( $subtypes, $request, $parameter ) {
 343          $subtypes = wp_parse_slug_list( $subtypes );
 344  
 345          $subtypes = rest_parse_request_arg( $subtypes, $request, $parameter );
 346          if ( is_wp_error( $subtypes ) ) {
 347              return $subtypes;
 348          }
 349  
 350          // 'any' overrides any other subtype.
 351          if ( in_array( self::TYPE_ANY, $subtypes, true ) ) {
 352              return array( self::TYPE_ANY );
 353          }
 354  
 355          $handler = $this->get_search_handler( $request );
 356          if ( is_wp_error( $handler ) ) {
 357              return $handler;
 358          }
 359  
 360          return array_intersect( $subtypes, $handler->get_subtypes() );
 361      }
 362  
 363      /**
 364       * Gets the search handler to handle the current request.
 365       *
 366       * @since 5.0.0
 367       *
 368       * @param WP_REST_Request $request Full details about the request.
 369       * @return WP_REST_Search_Handler|WP_Error Search handler for the request type, or WP_Error object on failure.
 370       */
 371  	protected function get_search_handler( $request ) {
 372          $type = $request->get_param( self::PROP_TYPE );
 373  
 374          if ( ! $type || ! isset( $this->search_handlers[ $type ] ) ) {
 375              return new WP_Error(
 376                  'rest_search_invalid_type',
 377                  __( 'Invalid type parameter.' ),
 378                  array( 'status' => 400 )
 379              );
 380          }
 381  
 382          return $this->search_handlers[ $type ];
 383      }
 384  }


Generated: Sun Oct 25 01:00:03 2020 Cross-referenced by PHPXref 0.7.1