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


Generated: Sun Sep 15 01:00:03 2019 Cross-referenced by PHPXref 0.7.1