<?php declare( strict_types=1 ); namespace Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers; use Automattic\WooCommerce\GoogleListingsAndAds\API\PermissionsTrait; use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable; use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper; use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\RESTServer; use WC_REST_Controller; use WP_REST_Request as Request; use WP_REST_Response as Response; /** * Class BaseEndpoint * * @package Automattic\WooCommerce\GoogleListingsAndAds\API\Site */ abstract class BaseController extends WC_REST_Controller implements Registerable { use PluginHelper, PermissionsTrait, ResponseFromExceptionTrait; /** * @var RESTServer */ protected $server; /** * BaseController constructor. * * @param RESTServer $server */ public function __construct( RESTServer $server ) { $this->server = $server; $this->namespace = $this->get_namespace(); } /** * Register a service. */ public function register(): void { $this->register_routes(); } /** * Register a single route. * * @param string $route The route name. * @param array $args The arguments for the route. */ protected function register_route( string $route, array $args ): void { $this->server->register_route( $this->get_namespace(), $route, $args ); } /** * Get the namespace for the current controller. * * @return string */ protected function get_namespace(): string { return "wc/{$this->get_slug()}"; } /** * Get the callback to determine the route's permissions. * * @return callable */ protected function get_permission_callback(): callable { return function() { return $this->can_manage(); }; } /** * Prepare an item schema for sending to the API. * * @param array $properties Array of raw properties. * @param string $schema_title Schema title. * * @return array */ protected function prepare_item_schema( array $properties, string $schema_title ): array { return $this->add_additional_fields_schema( [ '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => $schema_title, 'type' => 'object', 'additionalProperties' => false, 'properties' => $properties, ] ); } /** * Retrieves the item's schema, conforming to JSON Schema. * * @return array Item schema data. */ public function get_item_schema(): array { return $this->prepare_item_schema( $this->get_schema_properties(), $this->get_schema_title() ); } /** * Get a callback function for returning the API schema. * * @return callable */ protected function get_api_response_schema_callback(): callable { return function() { return $this->get_item_schema(); }; } /** * Get a route name which is safe to use as a filter (removes namespace prefix). * * @param Request $request Request object. * * @return string */ protected function get_route_name( Request $request ): string { $route = trim( $request->get_route(), '/' ); if ( 0 === strpos( $route, $this->get_namespace() ) ) { $route = substr( $route, strlen( $this->get_namespace() ) ); } return sanitize_title( $route ); } /** * Prepares the item for the REST response. * * @param mixed $item WordPress representation of the item. * @param Request $request Request object. * * @return Response Response object on success, or WP_Error object on failure. */ public function prepare_item_for_response( $item, $request ) { $prepared = []; $context = $request['context'] ?? 'view'; $schema = $this->get_schema_properties(); foreach ( $schema as $key => $property ) { $item_value = $item[ $key ] ?? $property['default'] ?? null; // Cast empty arrays to empty objects if property is supposed to be an object. if ( is_array( $item_value ) && empty( $item_value ) && isset( $property['type'] ) && 'object' === $property['type'] ) { $item_value = (object) []; } $prepared[ $key ] = $item_value; } $prepared = $this->add_additional_fields_to_object( $prepared, $request ); $prepared = $this->filter_response_by_context( $prepared, $context ); $prepared = apply_filters( 'woocommerce_gla_prepared_response_' . $this->get_route_name( $request ), $prepared, $request ); return new Response( $prepared ); } /** * Prepares one item for create or update operation. * * @param Request $request Request object. * * @return array The prepared item, or WP_Error object on failure. */ protected function prepare_item_for_database( $request ): array { $prepared = []; $schema = $this->get_schema_properties(); foreach ( $schema as $key => $property ) { if ( $property['readonly'] ?? false ) { continue; } $prepared[ $key ] = $request[ $key ] ?? $property['default'] ?? null; } return $prepared; } /** * Get the item schema properties for the controller. * * @return array */ abstract protected function get_schema_properties(): array; /** * Get the item schema name for the controller. * * Used for building the API response schema. * * @return string */ abstract protected function get_schema_title(): string; }