Skip to content
This repository was archived by the owner on Oct 4, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 287 additions & 9 deletions api/class-wc-rest-dev-product-variations-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,303 @@ class WC_REST_Dev_Product_Variations_Controller extends WC_REST_Product_Variatio
*/
protected $namespace = 'wc/v3';

/**
* Prepare a single variation output for response.
*
* @param WC_Data $object Object data.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response
*/
public function prepare_object_for_response( $object, $request ) {
$data = array(
'id' => $object->get_id(),
'date_created' => wc_rest_prepare_date_response( $object->get_date_created(), false ),
'date_created_gmt' => wc_rest_prepare_date_response( $object->get_date_created() ),
'date_modified' => wc_rest_prepare_date_response( $object->get_date_modified(), false ),
'date_modified_gmt' => wc_rest_prepare_date_response( $object->get_date_modified() ),
'description' => wc_format_content( $object->get_description() ),
'permalink' => $object->get_permalink(),
'sku' => $object->get_sku(),
'price' => $object->get_price(),
'regular_price' => $object->get_regular_price(),
'sale_price' => $object->get_sale_price(),
'date_on_sale_from' => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ),
'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ),
'date_on_sale_to' => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ),
'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ),
'on_sale' => $object->is_on_sale(),
'status' => $object->get_status(),
'purchasable' => $object->is_purchasable(),
'virtual' => $object->is_virtual(),
'downloadable' => $object->is_downloadable(),
'downloads' => $this->get_downloads( $object ),
'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1,
'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1,
'tax_status' => $object->get_tax_status(),
'tax_class' => $object->get_tax_class(),
'manage_stock' => $object->managing_stock(),
'stock_quantity' => $object->get_stock_quantity(),
'in_stock' => $object->is_in_stock(),
'backorders' => $object->get_backorders(),
'backorders_allowed' => $object->backorders_allowed(),
'backordered' => $object->is_on_backorder(),
'weight' => $object->get_weight(),
'dimensions' => array(
'length' => $object->get_length(),
'width' => $object->get_width(),
'height' => $object->get_height(),
),
'shipping_class' => $object->get_shipping_class(),
'shipping_class_id' => $object->get_shipping_class_id(),
'image' => $this->get_image( $object ),
'attributes' => $this->get_attributes( $object ),
'menu_order' => $object->get_menu_order(),
'meta_data' => $object->get_meta_data(),
);

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
$response = rest_ensure_response( $data );
$response->add_links( $this->prepare_links( $object, $request ) );

/**
* Filter the data for a response.
*
* The dynamic portion of the hook name, $this->post_type,
* refers to object type being prepared for the response.
*
* @param WP_REST_Response $response The response object.
* @param WC_Data $object Object data.
* @param WP_REST_Request $request Request object.
*/
return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request );
}

/**
* Prepare a single variation for create or update.
*
* @param WP_REST_Request $request Request object.
* @param bool $creating If is creating a new object.
* @return WP_Error|WC_Data
*/
protected function prepare_object_for_database( $request, $creating = false ) {
if ( isset( $request['id'] ) ) {
$variation = wc_get_product( absint( $request['id'] ) );
} else {
$variation = new WC_Product_Variation();
}

$variation->set_parent_id( absint( $request['product_id'] ) );

// Status.
if ( isset( $request['status'] ) ) {
$variation->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' );
}

// SKU.
if ( isset( $request['sku'] ) ) {
$variation->set_sku( wc_clean( $request['sku'] ) );
}

// Thumbnail.
if ( isset( $request['image'] ) ) {
if ( is_array( $request['image'] ) ) {
$image = $request['image'];
$variation = $this->set_product_images( $variation, array( $image ) );
} else {
$variation->set_image_id( '' );
}
}

// Virtual variation.
if ( isset( $request['virtual'] ) ) {
$variation->set_virtual( $request['virtual'] );
}

// Downloadable variation.
if ( isset( $request['downloadable'] ) ) {
$variation->set_downloadable( $request['downloadable'] );
}

// Downloads.
if ( $variation->get_downloadable() ) {
// Downloadable files.
if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) {
$variation = $this->save_downloadable_files( $variation, $request['downloads'] );
}

// Download limit.
if ( isset( $request['download_limit'] ) ) {
$variation->set_download_limit( $request['download_limit'] );
}

// Download expiry.
if ( isset( $request['download_expiry'] ) ) {
$variation->set_download_expiry( $request['download_expiry'] );
}
}

// Shipping data.
$variation = $this->save_product_shipping_data( $variation, $request );

// Stock handling.
if ( isset( $request['manage_stock'] ) ) {
$variation->set_manage_stock( $request['manage_stock'] );
}

if ( isset( $request['in_stock'] ) ) {
$variation->set_stock_status( true === $request['in_stock'] ? 'instock' : 'outofstock' );
}

if ( isset( $request['backorders'] ) ) {
$variation->set_backorders( $request['backorders'] );
}

if ( $variation->get_manage_stock() ) {
if ( isset( $request['stock_quantity'] ) ) {
$variation->set_stock_quantity( $request['stock_quantity'] );
} elseif ( isset( $request['inventory_delta'] ) ) {
$stock_quantity = wc_stock_amount( $variation->get_stock_quantity() );
$stock_quantity += wc_stock_amount( $request['inventory_delta'] );
$variation->set_stock_quantity( $stock_quantity );
}
} else {
$variation->set_backorders( 'no' );
$variation->set_stock_quantity( '' );
}

// Regular Price.
if ( isset( $request['regular_price'] ) ) {
$variation->set_regular_price( $request['regular_price'] );
}

// Sale Price.
if ( isset( $request['sale_price'] ) ) {
$variation->set_sale_price( $request['sale_price'] );
}

if ( isset( $request['date_on_sale_from'] ) ) {
$variation->set_date_on_sale_from( $request['date_on_sale_from'] );
}

if ( isset( $request['date_on_sale_from_gmt'] ) ) {
$variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null );
}

if ( isset( $request['date_on_sale_to'] ) ) {
$variation->set_date_on_sale_to( $request['date_on_sale_to'] );
}

if ( isset( $request['date_on_sale_to_gmt'] ) ) {
$variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null );
}

// Tax class.
if ( isset( $request['tax_class'] ) ) {
$variation->set_tax_class( $request['tax_class'] );
}

// Description.
if ( isset( $request['description'] ) ) {
$variation->set_description( wp_kses_post( $request['description'] ) );
}

// Update taxonomies.
if ( isset( $request['attributes'] ) ) {
$attributes = array();
$parent = wc_get_product( $variation->get_parent_id() );
$parent_attributes = $parent->get_attributes();

foreach ( $request['attributes'] as $attribute ) {
$attribute_id = 0;
$attribute_name = '';

// Check ID for global attributes or name for product attributes.
if ( ! empty( $attribute['id'] ) ) {
$attribute_id = absint( $attribute['id'] );
$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
} elseif ( ! empty( $attribute['name'] ) ) {
$attribute_name = sanitize_title( $attribute['name'] );
}

if ( ! $attribute_id && ! $attribute_name ) {
continue;
}

if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) {
continue;
}

$attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() );
$attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';

if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) {
// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
$term = get_term_by( 'name', $attribute_value, $attribute_name );

if ( $term && ! is_wp_error( $term ) ) {
$attribute_value = $term->slug;
} else {
$attribute_value = sanitize_title( $attribute_value );
}
}

$attributes[ $attribute_key ] = $attribute_value;
}

$variation->set_attributes( $attributes );
}

// Menu order.
if ( $request['menu_order'] ) {
$variation->set_menu_order( $request['menu_order'] );
}

// Meta data.
if ( is_array( $request['meta_data'] ) ) {
foreach ( $request['meta_data'] as $meta ) {
$variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
}
}

/**
* Filters an object before it is inserted via the REST API.
*
* The dynamic portion of the hook name, `$this->post_type`,
* refers to the object type slug.
*
* @param WC_Data $variation Object object.
* @param WP_REST_Request $request Request object.
* @param bool $creating If is creating a new object.
*/
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $variation, $request, $creating );
}

/**
* Get the image for a product variation.
*
* @param WC_Product_Variation $variation Variation
* @return array
*/
protected function get_images( $variation ) {
protected function get_image( $variation ) {
if ( ! has_post_thumbnail( $variation->get_id() ) ) {
return;
}

$attachment_id = $variation->get_image_id();
$attachment_post = get_post( $attachment_id );
if ( is_null( $attachment_post ) ) {
$image = array();
return;
}

$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
if ( ! is_array( $attachment ) ) {
$image = array();
return;
}

if ( ! isset ( $image ) ) {
$image = array(
return array(
'id' => (int) $attachment_id,
'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ),
'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ),
Expand All @@ -59,7 +336,7 @@ protected function get_images( $variation ) {
);
}

return array( $image );
return;
}

/**
Expand Down Expand Up @@ -152,10 +429,11 @@ public function get_item_schema() {
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'visible' => array(
'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),
'type' => 'boolean',
'default' => true,
'status' => array(
'description' => __( 'Variation status.', 'woocommerce' ),
'type' => 'string',
'default' => 'publish',
'enum' => array_keys( get_post_statuses() ),
'context' => array( 'view', 'edit' ),
),
'purchasable' => array(
Expand Down
5 changes: 4 additions & 1 deletion tests/unit-tests/product-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ public function test_update_variation() {
$this->assertEquals( 10, $variation['regular_price'] );
$this->assertEmpty( $variation['sale_price'] );
$this->assertEquals( 'small', $variation['attributes'][0]['option'] );
$this->assertEquals( 'publish', $variation['status'] );

$request = new WP_REST_Request( 'PUT', '/wc/v3/products/' . $product->get_id() . '/variations/' . $variation_id );
$request->set_body_params( array(
Expand All @@ -177,6 +178,7 @@ public function test_update_variation() {
'description' => 'O_O',
'image' => array( 'position' => 0, 'src' => 'https://cldup.com/Dr1Bczxq4q.png', 'alt' => 'test upload image' ),
'attributes' => array( array( 'name' => 'pa_size', 'option' => 'medium' ) ),
'status' => 'private',
) );
$response = $this->server->dispatch( $request );
$variation = $response->get_data();
Expand All @@ -189,6 +191,7 @@ public function test_update_variation() {
$this->assertEquals( 'medium', $variation['attributes'][0]['option'] );
$this->assertContains( 'Dr1Bczxq4q', $variation['image']['src'] );
$this->assertContains( 'test upload image', $variation['image']['alt'] );
$this->assertEquals( 'private', $variation['status'] );
$product->delete( true );
}

Expand Down Expand Up @@ -356,13 +359,13 @@ public function test_variation_schema() {
$this->assertArrayHasKey( 'date_on_sale_from', $properties );
$this->assertArrayHasKey( 'date_on_sale_to', $properties );
$this->assertArrayHasKey( 'on_sale', $properties );
$this->assertArrayHasKey( 'visible', $properties );
$this->assertArrayHasKey( 'purchasable', $properties );
$this->assertArrayHasKey( 'virtual', $properties );
$this->assertArrayHasKey( 'downloadable', $properties );
$this->assertArrayHasKey( 'downloads', $properties );
$this->assertArrayHasKey( 'download_limit', $properties );
$this->assertArrayHasKey( 'download_expiry', $properties );
$this->assertArrayHasKey( 'status', $properties );
$this->assertArrayHasKey( 'tax_status', $properties );
$this->assertArrayHasKey( 'tax_class', $properties );
$this->assertArrayHasKey( 'manage_stock', $properties );
Expand Down