Create an API that manage the custom field names (#48504)
* Create WC_REST_Product_Custom_Fields_Controller * Register WC_REST_Product_Custom_Fields_Controller * Add pagination to the /product-custom-fields/names request * Add WC_REST_Product_Custom_Fields_Controller_Tests * Fix linter errors * Add changelog file * Fix linter error * Change endpoint path to matches the product related endpoints set * Fix controller file description * Use more descriptive table alias in the sql query
This commit is contained in:
parent
c77dfe5030
commit
5e997435bd
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add a rest api to manage the product custom fields
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API CustomFields controller
|
||||
*
|
||||
* Handles requests to the /products/custom-fields endpoint.
|
||||
*
|
||||
* @package WooCommerce\RestApi
|
||||
* @since 9.2.0
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Utilities\I18nUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* REST API Product Custom Fields controller class.
|
||||
*
|
||||
* @package WooCommerce\RestApi
|
||||
* @extends WC_REST_Controller
|
||||
*/
|
||||
class WC_REST_Product_Custom_Fields_Controller extends WC_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v3';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/custom-fields';
|
||||
|
||||
/**
|
||||
* Post type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $post_type = 'product';
|
||||
|
||||
/**
|
||||
* Register the routes for products.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/names',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item_names' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of custom field names.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item_names( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$search = trim( $request['search'] );
|
||||
$order = strtoupper( $request['order'] ) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = (int) $request['page'];
|
||||
$limit = (int) $request['per_page'];
|
||||
$offset = ( $page - 1 ) * $limit;
|
||||
|
||||
$base_query = $wpdb->prepare(
|
||||
"SELECT DISTINCT post_metas.meta_key
|
||||
FROM {$wpdb->postmeta} post_metas LEFT JOIN {$wpdb->posts} posts ON post_metas.post_id = posts.id
|
||||
WHERE posts.post_type = %s AND post_metas.meta_key NOT LIKE %s AND post_metas.meta_key LIKE %s",
|
||||
$this->post_type,
|
||||
$wpdb->esc_like( '_' ) . '%',
|
||||
"%{$search}%"
|
||||
);
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $base_query has been prepared already and $order is a static value.
|
||||
$query = $wpdb->prepare(
|
||||
"$base_query ORDER BY post_metas.meta_key $order LIMIT %d, %d",
|
||||
$offset,
|
||||
$limit
|
||||
);
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $base_query has been prepared already.
|
||||
$total_query = "SELECT COUNT(1) FROM ($base_query) AS total";
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- $query has been prepared already.
|
||||
$query_result = $wpdb->get_results( $query );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- $total_query has been prepared already.
|
||||
$total_items = $wpdb->get_var( $total_query );
|
||||
|
||||
$custom_field_names = array();
|
||||
foreach ( $query_result as $custom_field_name ) {
|
||||
$custom_field_names[] = $custom_field_name->meta_key;
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $custom_field_names );
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $total_items );
|
||||
$max_pages = ceil( $total_items / $limit );
|
||||
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/names' ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read items.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( $this->post_type, 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new options for 'order' to the collection params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort items ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'asc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -157,6 +157,7 @@ class Server {
|
|||
'product-attribute-terms' => 'WC_REST_Product_Attribute_Terms_Controller',
|
||||
'product-attributes' => 'WC_REST_Product_Attributes_Controller',
|
||||
'product-categories' => 'WC_REST_Product_Categories_Controller',
|
||||
'product-custom-fields' => 'WC_REST_Product_Custom_Fields_Controller',
|
||||
'product-reviews' => 'WC_REST_Product_Reviews_Controller',
|
||||
'product-shipping-classes' => 'WC_REST_Product_Shipping_Classes_Controller',
|
||||
'product-tags' => 'WC_REST_Product_Tags_Controller',
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class WC_REST_Product_Custom_Fields_Controller_Tests.
|
||||
* Product Custom Fields Controller tests for V3 REST API.
|
||||
*/
|
||||
class WC_REST_Product_Custom_Fields_Controller_Tests extends WC_REST_Unit_Test_Case {
|
||||
/**
|
||||
* @var WC_Product_Simple[]
|
||||
*/
|
||||
protected static $products = array();
|
||||
|
||||
/**
|
||||
* Create products for tests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function wpSetUpBeforeClass() {
|
||||
self::$products[] = WC_Helper_Product::create_simple_product(
|
||||
true,
|
||||
array(
|
||||
'name' => 'Pancake',
|
||||
'sku' => 'pancake-1',
|
||||
)
|
||||
);
|
||||
self::$products[] = WC_Helper_Product::create_simple_product(
|
||||
true,
|
||||
array(
|
||||
'name' => 'Waffle 1',
|
||||
'sku' => 'pancake-2',
|
||||
)
|
||||
);
|
||||
self::$products[] = WC_Helper_Product::create_simple_product(
|
||||
true,
|
||||
array(
|
||||
'name' => 'French Toast',
|
||||
'sku' => 'waffle-2',
|
||||
)
|
||||
);
|
||||
self::$products[] = WC_Helper_Product::create_simple_product(
|
||||
true,
|
||||
array(
|
||||
'name' => 'Waffle 3',
|
||||
'sku' => 'waffle-3',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( self::$products as $product ) {
|
||||
for ( $i = 0; $i < 20; $i++ ) {
|
||||
$product->add_meta_data( "Custom field $i", "Value $i", true );
|
||||
}
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up products after tests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function wpTearDownAfterClass() {
|
||||
foreach ( self::$products as $product ) {
|
||||
WC_Helper_Product::delete_product( $product->get_id() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup our test server, endpoints, and user info.
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->endpoint = new WC_REST_Product_Custom_Fields_Controller();
|
||||
$this->user = $this->factory->user->create(
|
||||
array(
|
||||
'role' => 'administrator',
|
||||
)
|
||||
);
|
||||
wp_set_current_user( $this->user );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the default custom field names endpoint response.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_custom_fields_without_params() {
|
||||
$request = new WP_REST_Request( 'GET', '/wc/v3/products/custom-fields/names' );
|
||||
|
||||
$response = $this->server->dispatch( $request );
|
||||
$this->assertEquals( 200, $response->get_status() );
|
||||
$data = $response->get_data();
|
||||
|
||||
$this->assertEquals( 10, count( $data ) );
|
||||
$this->assertEquals( 'Custom field 0', $data[0] );
|
||||
$this->assertEquals( 'Custom field 1', $data[1] );
|
||||
$this->assertEquals( 'Custom field 10', $data[2] );
|
||||
$this->assertEquals( 'Custom field 11', $data[3] );
|
||||
$this->assertEquals( 'Custom field 12', $data[4] );
|
||||
$this->assertEquals( 'Custom field 13', $data[5] );
|
||||
$this->assertEquals( 'Custom field 14', $data[6] );
|
||||
$this->assertEquals( 'Custom field 15', $data[7] );
|
||||
$this->assertEquals( 'Custom field 16', $data[8] );
|
||||
$this->assertEquals( 'Custom field 17', $data[9] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the custom field names searching.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_custom_fields_searching() {
|
||||
$request = new WP_REST_Request( 'GET', '/wc/v3/products/custom-fields/names' );
|
||||
$request->set_query_params(
|
||||
array(
|
||||
'search' => '0',
|
||||
)
|
||||
);
|
||||
$response = $this->server->dispatch( $request );
|
||||
$this->assertEquals( 200, $response->get_status() );
|
||||
$data = $response->get_data();
|
||||
|
||||
$this->assertEquals( 2, count( $data ) );
|
||||
$this->assertEquals( 'Custom field 0', $data[0] );
|
||||
$this->assertEquals( 'Custom field 10', $data[1] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the custom field names ordering.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_custom_fields_ordering() {
|
||||
$request = new WP_REST_Request( 'GET', '/wc/v3/products/custom-fields/names' );
|
||||
$request->set_query_params(
|
||||
array(
|
||||
'order' => 'desc',
|
||||
'search' => 'Custom',
|
||||
)
|
||||
);
|
||||
$response = $this->server->dispatch( $request );
|
||||
$this->assertEquals( 200, $response->get_status() );
|
||||
$data = $response->get_data();
|
||||
|
||||
$this->assertEquals( 10, count( $data ) );
|
||||
$this->assertEquals( 'Custom field 9', $data[0] );
|
||||
$this->assertEquals( 'Custom field 8', $data[1] );
|
||||
$this->assertEquals( 'Custom field 7', $data[2] );
|
||||
$this->assertEquals( 'Custom field 6', $data[3] );
|
||||
$this->assertEquals( 'Custom field 5', $data[4] );
|
||||
$this->assertEquals( 'Custom field 4', $data[5] );
|
||||
$this->assertEquals( 'Custom field 3', $data[6] );
|
||||
$this->assertEquals( 'Custom field 2', $data[7] );
|
||||
$this->assertEquals( 'Custom field 19', $data[8] );
|
||||
$this->assertEquals( 'Custom field 18', $data[9] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the custom field names pagination.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_custom_fields_pagination() {
|
||||
$request = new WP_REST_Request( 'GET', '/wc/v3/products/custom-fields/names' );
|
||||
$request->set_query_params(
|
||||
array(
|
||||
'per_page' => 3,
|
||||
'page' => 2,
|
||||
'search' => 'Custom',
|
||||
)
|
||||
);
|
||||
$response = $this->server->dispatch( $request );
|
||||
$this->assertEquals( 200, $response->get_status() );
|
||||
$data = $response->get_data();
|
||||
|
||||
$this->assertEquals( 3, count( $data ) );
|
||||
$this->assertEquals( 'Custom field 11', $data[0] );
|
||||
$this->assertEquals( 'Custom field 12', $data[1] );
|
||||
$this->assertEquals( 'Custom field 13', $data[2] );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue