Passed
Push — master ( e0d403...4b719c )
by Mike
07:22 queued 20s
created

ProductVariations::prepare_objects_query()   B

Complexity

Conditions 10
Paths 120

Size

Total Lines 57
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 31
nc 120
nop 1
dl 0
loc 57
rs 7.5
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * REST API variations controller
4
 *
5
 * Handles requests to the /products/<product_id>/variations endpoints.
6
 *
7
 * @package WooCommerce/RestApi
8
 */
9
10
namespace WooCommerce\RestApi\Controllers\Version4;
11
12
defined( 'ABSPATH' ) || exit;
13
14
/**
15
 * REST API variations controller class.
16
 */
17
class ProductVariations extends Products {
18
19
	/**
20
	 * Route base.
21
	 *
22
	 * @var string
23
	 */
24
	protected $rest_base = 'products/(?P<product_id>[\d]+)/variations';
25
26
	/**
27
	 * Post type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $post_type = 'product_variation';
32
33
	/**
34
	 * Initialize product actions (parent).
35
	 */
36
	public function __construct() {
37
		add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'add_product_id' ), 9, 2 );
38
		parent::__construct();
39
	}
40
41
	/**
42
	 * Register the routes for products.
43
	 */
44
	public function register_routes() {
45
		register_rest_route(
46
			$this->namespace,
47
			'/' . $this->rest_base,
48
			array(
49
				'args'   => array(
50
					'product_id' => array(
51
						'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
52
						'type'        => 'integer',
53
					),
54
				),
55
				array(
56
					'methods'             => \WP_REST_Server::READABLE,
57
					'callback'            => array( $this, 'get_items' ),
58
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
59
					'args'                => $this->get_collection_params(),
60
				),
61
				array(
62
					'methods'             => \WP_REST_Server::CREATABLE,
63
					'callback'            => array( $this, 'create_item' ),
64
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
65
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
66
				),
67
				'schema' => array( $this, 'get_public_item_schema' ),
68
			),
69
			true
70
		);
71
		register_rest_route(
72
			$this->namespace,
73
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
74
			array(
75
				'args'   => array(
76
					'product_id' => array(
77
						'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
78
						'type'        => 'integer',
79
					),
80
					'id'         => array(
81
						'description' => __( 'Unique identifier for the variation.', 'woocommerce' ),
82
						'type'        => 'integer',
83
					),
84
				),
85
				array(
86
					'methods'             => \WP_REST_Server::READABLE,
87
					'callback'            => array( $this, 'get_item' ),
88
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
89
					'args'                => array(
90
						'context' => $this->get_context_param(
91
							array(
92
								'default' => 'view',
93
							)
94
						),
95
					),
96
				),
97
				array(
98
					'methods'             => \WP_REST_Server::EDITABLE,
99
					'callback'            => array( $this, 'update_item' ),
100
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
101
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
102
				),
103
				array(
104
					'methods'             => \WP_REST_Server::DELETABLE,
105
					'callback'            => array( $this, 'delete_item' ),
106
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
107
					'args'                => array(
108
						'force' => array(
109
							'default'     => false,
110
							'type'        => 'boolean',
111
							'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
112
						),
113
					),
114
				),
115
				'schema' => array( $this, 'get_public_item_schema' ),
116
			),
117
			true
118
		);
119
		register_rest_route(
120
			$this->namespace,
121
			'/' . $this->rest_base . '/batch',
122
			array(
123
				'args'   => array(
124
					'product_id' => array(
125
						'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
126
						'type'        => 'integer',
127
					),
128
				),
129
				array(
130
					'methods'             => \WP_REST_Server::EDITABLE,
131
					'callback'            => array( $this, 'batch_items' ),
132
					'permission_callback' => array( $this, 'batch_items_permissions_check' ),
133
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
134
				),
135
				'schema' => array( $this, 'get_public_batch_schema' ),
136
			),
137
			true
138
		);
139
	}
140
141
	/**
142
	 * Get object.
143
	 *
144
	 * @since  3.0.0
145
	 * @param  int $id Object ID.
146
	 * @return \WC_Data
147
	 */
148
	protected function get_object( $id ) {
149
		return wc_get_product( $id );
150
	}
151
152
	/**
153
	 * Check if a given request has access to update an item.
154
	 *
155
	 * @param  \WP_REST_Request $request Full details about the request.
156
	 * @return \WP_Error|boolean
157
	 */
158
	public function update_item_permissions_check( $request ) {
159
		$object = $this->get_object( (int) $request['id'] );
160
161
		if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $object->get_id() ) ) {
162
			return new \WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
163
		}
164
165
		// Check if variation belongs to the correct parent product.
166
		if ( $object && 0 !== $object->get_parent_id() && absint( $request['product_id'] ) !== $object->get_parent_id() ) {
167
			return new \WP_Error( 'woocommerce_rest_cannot_edit', __( 'Parent product does not match current variation.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
168
		}
169
170
		return true;
171
	}
172
173
	/**
174
	 * Prepare a single variation output for response.
175
	 *
176
	 * @param  \WC_Data         $object  Object data.
177
	 * @param  \WP_REST_Request $request Request object.
178
	 * @return \WP_REST_Response
179
	 */
180
	public function prepare_object_for_response( $object, $request ) {
181
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
182
		$data    = array(
183
			'id'                    => $object->get_id(),
184
			'name'                  => $object->get_name( $context ),
0 ignored issues
show
Bug introduced by
The method get_name() does not exist on WC_Data. Did you maybe mean get_meta()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
			'name'                  => $object->/** @scrutinizer ignore-call */ get_name( $context ),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
185
			'type'                  => $object->get_type(),
0 ignored issues
show
Bug introduced by
The method get_type() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Order_Item or WC_Product or WC_Abstract_Order or WC_Payment_Token. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

185
			'type'                  => $object->/** @scrutinizer ignore-call */ get_type(),
Loading history...
186
			'parent_id'             => $object->get_parent_id( $context ),
0 ignored issues
show
Bug introduced by
The method get_parent_id() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product or WC_Abstract_Order. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

186
			'parent_id'             => $object->/** @scrutinizer ignore-call */ get_parent_id( $context ),
Loading history...
187
			'date_created'          => wc_rest_prepare_date_response( $object->get_date_created(), false ),
0 ignored issues
show
Bug introduced by
The method get_date_created() does not exist on WC_Data. Did you maybe mean get_data()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

187
			'date_created'          => wc_rest_prepare_date_response( $object->/** @scrutinizer ignore-call */ get_date_created(), false ),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
188
			'date_created_gmt'      => wc_rest_prepare_date_response( $object->get_date_created() ),
189
			'date_modified'         => wc_rest_prepare_date_response( $object->get_date_modified(), false ),
0 ignored issues
show
Bug introduced by
The method get_date_modified() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product or WC_Customer or WC_Abstract_Order or WC_Coupon or WC_Webhook. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

189
			'date_modified'         => wc_rest_prepare_date_response( $object->/** @scrutinizer ignore-call */ get_date_modified(), false ),
Loading history...
190
			'date_modified_gmt'     => wc_rest_prepare_date_response( $object->get_date_modified() ),
191
			'description'           => wc_format_content( $object->get_description() ),
0 ignored issues
show
Bug introduced by
The method get_description() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product or WC_Coupon. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
			'description'           => wc_format_content( $object->/** @scrutinizer ignore-call */ get_description() ),
Loading history...
192
			'permalink'             => $object->get_permalink(),
0 ignored issues
show
Bug introduced by
The method get_permalink() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

192
			'permalink'             => $object->/** @scrutinizer ignore-call */ get_permalink(),
Loading history...
193
			'sku'                   => $object->get_sku(),
0 ignored issues
show
Bug introduced by
The method get_sku() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

193
			'sku'                   => $object->/** @scrutinizer ignore-call */ get_sku(),
Loading history...
194
			'price'                 => $object->get_price(),
0 ignored issues
show
Bug introduced by
The method get_price() does not exist on WC_Data. Did you maybe mean get_prop()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

194
			'price'                 => $object->/** @scrutinizer ignore-call */ get_price(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
195
			'regular_price'         => $object->get_regular_price(),
0 ignored issues
show
Bug introduced by
The method get_regular_price() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

195
			'regular_price'         => $object->/** @scrutinizer ignore-call */ get_regular_price(),
Loading history...
196
			'sale_price'            => $object->get_sale_price(),
0 ignored issues
show
Bug introduced by
The method get_sale_price() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

196
			'sale_price'            => $object->/** @scrutinizer ignore-call */ get_sale_price(),
Loading history...
197
			'date_on_sale_from'     => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ),
0 ignored issues
show
Bug introduced by
The method get_date_on_sale_from() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

197
			'date_on_sale_from'     => wc_rest_prepare_date_response( $object->/** @scrutinizer ignore-call */ get_date_on_sale_from(), false ),
Loading history...
198
			'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ),
199
			'date_on_sale_to'       => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ),
0 ignored issues
show
Bug introduced by
The method get_date_on_sale_to() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
			'date_on_sale_to'       => wc_rest_prepare_date_response( $object->/** @scrutinizer ignore-call */ get_date_on_sale_to(), false ),
Loading history...
200
			'date_on_sale_to_gmt'   => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ),
201
			'on_sale'               => $object->is_on_sale(),
0 ignored issues
show
Bug introduced by
The method is_on_sale() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

201
			'on_sale'               => $object->/** @scrutinizer ignore-call */ is_on_sale(),
Loading history...
202
			'status'                => $object->get_status(),
0 ignored issues
show
Bug introduced by
The method get_status() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product or WC_Abstract_Order or WC_Webhook. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

202
			'status'                => $object->/** @scrutinizer ignore-call */ get_status(),
Loading history...
203
			'purchasable'           => $object->is_purchasable(),
0 ignored issues
show
Bug introduced by
The method is_purchasable() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
			'purchasable'           => $object->/** @scrutinizer ignore-call */ is_purchasable(),
Loading history...
204
			'virtual'               => $object->is_virtual(),
0 ignored issues
show
Bug introduced by
The method is_virtual() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

204
			'virtual'               => $object->/** @scrutinizer ignore-call */ is_virtual(),
Loading history...
205
			'downloadable'          => $object->is_downloadable(),
0 ignored issues
show
Bug introduced by
The method is_downloadable() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
			'downloadable'          => $object->/** @scrutinizer ignore-call */ is_downloadable(),
Loading history...
206
			'downloads'             => $this->get_downloads( $object ),
207
			'download_limit'        => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1,
0 ignored issues
show
Bug introduced by
The method get_download_limit() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

207
			'download_limit'        => '' !== $object->/** @scrutinizer ignore-call */ get_download_limit() ? (int) $object->get_download_limit() : -1,
Loading history...
208
			'download_expiry'       => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1,
0 ignored issues
show
Bug introduced by
The method get_download_expiry() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

208
			'download_expiry'       => '' !== $object->/** @scrutinizer ignore-call */ get_download_expiry() ? (int) $object->get_download_expiry() : -1,
Loading history...
209
			'tax_status'            => $object->get_tax_status(),
0 ignored issues
show
Bug introduced by
The method get_tax_status() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Order_Item or WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

209
			'tax_status'            => $object->/** @scrutinizer ignore-call */ get_tax_status(),
Loading history...
210
			'tax_class'             => $object->get_tax_class(),
0 ignored issues
show
Bug introduced by
The method get_tax_class() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Order_Item or WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
			'tax_class'             => $object->/** @scrutinizer ignore-call */ get_tax_class(),
Loading history...
211
			'manage_stock'          => $object->managing_stock(),
0 ignored issues
show
Bug introduced by
The method managing_stock() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

211
			'manage_stock'          => $object->/** @scrutinizer ignore-call */ managing_stock(),
Loading history...
212
			'stock_quantity'        => $object->get_stock_quantity(),
0 ignored issues
show
Bug introduced by
The method get_stock_quantity() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

212
			'stock_quantity'        => $object->/** @scrutinizer ignore-call */ get_stock_quantity(),
Loading history...
213
			'stock_status'          => $object->get_stock_status(),
0 ignored issues
show
Bug introduced by
The method get_stock_status() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

213
			'stock_status'          => $object->/** @scrutinizer ignore-call */ get_stock_status(),
Loading history...
214
			'backorders'            => $object->get_backorders(),
0 ignored issues
show
Bug introduced by
The method get_backorders() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

214
			'backorders'            => $object->/** @scrutinizer ignore-call */ get_backorders(),
Loading history...
215
			'backorders_allowed'    => $object->backorders_allowed(),
0 ignored issues
show
Bug introduced by
The method backorders_allowed() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
			'backorders_allowed'    => $object->/** @scrutinizer ignore-call */ backorders_allowed(),
Loading history...
216
			'backordered'           => $object->is_on_backorder(),
0 ignored issues
show
Bug introduced by
The method is_on_backorder() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

216
			'backordered'           => $object->/** @scrutinizer ignore-call */ is_on_backorder(),
Loading history...
217
			'weight'                => $object->get_weight(),
0 ignored issues
show
Bug introduced by
The method get_weight() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

217
			'weight'                => $object->/** @scrutinizer ignore-call */ get_weight(),
Loading history...
218
			'dimensions'            => array(
219
				'length' => $object->get_length(),
0 ignored issues
show
Bug introduced by
The method get_length() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
				'length' => $object->/** @scrutinizer ignore-call */ get_length(),
Loading history...
220
				'width'  => $object->get_width(),
0 ignored issues
show
Bug introduced by
The method get_width() does not exist on WC_Data. Did you maybe mean get_id()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

220
				'width'  => $object->/** @scrutinizer ignore-call */ get_width(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
221
				'height' => $object->get_height(),
0 ignored issues
show
Bug introduced by
The method get_height() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

221
				'height' => $object->/** @scrutinizer ignore-call */ get_height(),
Loading history...
222
			),
223
			'shipping_class'        => $object->get_shipping_class(),
0 ignored issues
show
Bug introduced by
The method get_shipping_class() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

223
			'shipping_class'        => $object->/** @scrutinizer ignore-call */ get_shipping_class(),
Loading history...
224
			'shipping_class_id'     => $object->get_shipping_class_id(),
0 ignored issues
show
Bug introduced by
The method get_shipping_class_id() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

224
			'shipping_class_id'     => $object->/** @scrutinizer ignore-call */ get_shipping_class_id(),
Loading history...
225
			'image'                 => $this->get_image( $object ),
226
			'attributes'            => $this->get_attributes( $object ),
227
			'menu_order'            => $object->get_menu_order(),
0 ignored issues
show
Bug introduced by
The method get_menu_order() does not exist on WC_Data. It seems like you code against a sub-type of WC_Data such as WC_Product. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
			'menu_order'            => $object->/** @scrutinizer ignore-call */ get_menu_order(),
Loading history...
228
			'meta_data'             => $object->get_meta_data(),
229
		);
230
231
		$context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
232
		$data     = $this->add_additional_fields_to_object( $data, $request );
233
		$data     = $this->filter_response_by_context( $data, $context );
234
		$response = rest_ensure_response( $data );
235
		$response->add_links( $this->prepare_links( $object, $request ) );
236
237
		/**
238
		 * Filter the data for a response.
239
		 *
240
		 * The dynamic portion of the hook name, $this->post_type,
241
		 * refers to object type being prepared for the response.
242
		 *
243
		 * @param \WP_REST_Response $response The response object.
244
		 * @param \WC_Data          $object   Object data.
245
		 * @param \WP_REST_Request  $request  Request object.
246
		 */
247
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request );
248
	}
249
250
	/**
251
	 * Get the image for a product variation.
252
	 *
253
	 * @param \WC_Product_Variation $variation Variation data.
254
	 * @return array
255
	 */
256
	protected function get_image( $variation ) {
257
		if ( ! $variation->get_image_id() ) {
258
			return;
259
		}
260
261
		$attachment_id   = $variation->get_image_id();
262
		$attachment_post = get_post( $attachment_id );
0 ignored issues
show
Bug introduced by
$attachment_id of type string is incompatible with the type WP_Post|integer|null expected by parameter $post of get_post(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

262
		$attachment_post = get_post( /** @scrutinizer ignore-type */ $attachment_id );
Loading history...
263
		if ( is_null( $attachment_post ) ) {
264
			return;
265
		}
266
267
		$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
0 ignored issues
show
Bug introduced by
$attachment_id of type string is incompatible with the type integer expected by parameter $attachment_id of wp_get_attachment_image_src(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
		$attachment = wp_get_attachment_image_src( /** @scrutinizer ignore-type */ $attachment_id, 'full' );
Loading history...
268
		if ( ! is_array( $attachment ) ) {
269
			return;
270
		}
271
272
		if ( ! isset( $image ) ) {
273
			return array(
274
				'id'                => (int) $attachment_id,
275
				'date_created'      => wc_rest_prepare_date_response( $attachment_post->post_date, false ),
276
				'date_created_gmt'  => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ),
277
				'date_modified'     => wc_rest_prepare_date_response( $attachment_post->post_modified, false ),
278
				'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ),
279
				'src'               => current( $attachment ),
280
				'name'              => get_the_title( $attachment_id ),
0 ignored issues
show
Bug introduced by
$attachment_id of type string is incompatible with the type WP_Post|integer expected by parameter $post of get_the_title(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

280
				'name'              => get_the_title( /** @scrutinizer ignore-type */ $attachment_id ),
Loading history...
281
				'alt'               => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
0 ignored issues
show
Bug introduced by
$attachment_id of type string is incompatible with the type integer expected by parameter $post_id of get_post_meta(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

281
				'alt'               => get_post_meta( /** @scrutinizer ignore-type */ $attachment_id, '_wp_attachment_image_alt', true ),
Loading history...
282
			);
283
		}
284
	}
285
286
	/**
287
	 * Set variation image.
288
	 *
289
	 * @throws \WC_REST_Exception REST API exceptions.
290
	 *
291
	 * @param  \WC_Product_Variation $variation Variation instance.
292
	 * @param  array                 $image     Image data.
293
	 * @return \WC_Product_Variation
294
	 */
295
	protected function set_variation_image( $variation, $image ) {
296
		$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0;
297
298
		if ( 0 === $attachment_id && isset( $image['src'] ) ) {
299
			$upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) );
300
301
			if ( is_wp_error( $upload ) ) {
302
				if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $variation->get_id(), array( $image ) ) ) {
303
					throw new \WC_REST_Exception( 'woocommerce_variation_image_upload_error', $upload->get_error_message(), 400 );
304
				}
305
			}
306
307
			$attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $variation->get_id() );
0 ignored issues
show
Bug introduced by
It seems like $upload can also be of type WP_Error; however, parameter $upload of wc_rest_set_uploaded_image_as_attachment() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

307
			$attachment_id = wc_rest_set_uploaded_image_as_attachment( /** @scrutinizer ignore-type */ $upload, $variation->get_id() );
Loading history...
308
		}
309
310
		if ( ! wp_attachment_is_image( $attachment_id ) ) {
311
			/* translators: %s: attachment ID */
312
			throw new \WC_REST_Exception( 'woocommerce_variation_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 );
313
		}
314
315
		$variation->set_image_id( $attachment_id );
316
317
		// Set the image alt if present.
318
		if ( ! empty( $image['alt'] ) ) {
319
			update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) );
320
		}
321
322
		// Set the image name if present.
323
		if ( ! empty( $image['name'] ) ) {
324
			wp_update_post(
325
				array(
326
					'ID'         => $attachment_id,
327
					'post_title' => $image['name'],
328
				)
329
			);
330
		}
331
332
		return $variation;
333
	}
334
335
	/**
336
	 * Prepare objects query.
337
	 *
338
	 * @since  3.0.0
339
	 * @param  \WP_REST_Request $request Full details about the request.
340
	 * @return array
341
	 */
342
	protected function prepare_objects_query( $request ) {
343
		$args = parent::prepare_objects_query( $request );
344
345
		// Set post_status.
346
		$args['post_status'] = $request['status'];
347
348
		// Set custom args to handle later during clauses.
349
		$custom_keys = array(
350
			'sku',
351
			'min_price',
352
			'max_price',
353
			'stock_status',
354
			'low_in_stock',
355
		);
356
		foreach ( $custom_keys as $key ) {
357
			if ( ! empty( $request[ $key ] ) ) {
358
				$args[ $key ] = $request[ $key ];
359
			}
360
		}
361
362
		// Filter by tax class.
363
		if ( ! empty( $request['tax_class'] ) ) {
364
			$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
365
				$args,
366
				array(
367
					'key'   => '_tax_class',
368
					'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '',
369
				)
370
			);
371
		}
372
373
		// Filter by on sale products.
374
		if ( is_bool( $request['on_sale'] ) ) {
375
			$on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in';
376
			$on_sale_ids = wc_get_product_ids_on_sale();
377
378
			// Use 0 when there's no on sale products to avoid return all products.
379
			$on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids;
380
381
			$args[ $on_sale_key ] += $on_sale_ids;
382
		}
383
384
		// Force the post_type argument, since it's not a user input variable.
385
		if ( ! empty( $request['sku'] ) ) {
386
			$args['post_type'] = array( 'product', 'product_variation' );
387
		} else {
388
			$args['post_type'] = $this->post_type;
389
		}
390
391
		$args['post_parent'] = $request['product_id'];
392
393
		if ( ! empty( $request['search'] ) ) {
394
			$args['search'] = $request['search'];
395
			unset( $args['s'] );
396
		}
397
398
		return $args;
399
	}
400
401
	/**
402
	 * Prepare a single variation for create or update.
403
	 *
404
	 * @param  \WP_REST_Request $request Request object.
405
	 * @param  bool             $creating If is creating a new object.
406
	 * @return \WP_Error|\WC_Data
407
	 */
408
	protected function prepare_object_for_database( $request, $creating = false ) {
409
		if ( isset( $request['id'] ) ) {
410
			$variation = wc_get_product( absint( $request['id'] ) );
411
		} else {
412
			$variation = new \WC_Product_Variation();
413
		}
414
415
		$variation->set_parent_id( absint( $request['product_id'] ) );
416
417
		// Status.
418
		if ( isset( $request['status'] ) ) {
419
			$variation->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' );
420
		}
421
422
		// SKU.
423
		if ( isset( $request['sku'] ) ) {
424
			$variation->set_sku( wc_clean( $request['sku'] ) );
0 ignored issues
show
Bug introduced by
It seems like wc_clean($request['sku']) can also be of type array; however, parameter $sku of WC_Product::set_sku() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

424
			$variation->set_sku( /** @scrutinizer ignore-type */ wc_clean( $request['sku'] ) );
Loading history...
425
		}
426
427
		// Thumbnail.
428
		if ( isset( $request['image'] ) ) {
429
			if ( is_array( $request['image'] ) ) {
430
				$variation = $this->set_variation_image( $variation, $request['image'] );
431
			} else {
432
				$variation->set_image_id( '' );
433
			}
434
		}
435
436
		// Virtual variation.
437
		if ( isset( $request['virtual'] ) ) {
438
			$variation->set_virtual( $request['virtual'] );
439
		}
440
441
		// Downloadable variation.
442
		if ( isset( $request['downloadable'] ) ) {
443
			$variation->set_downloadable( $request['downloadable'] );
444
		}
445
446
		// Downloads.
447
		if ( $variation->get_downloadable() ) {
448
			// Downloadable files.
449
			if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) {
450
				$variation = $this->save_downloadable_files( $variation, $request['downloads'] );
451
			}
452
453
			// Download limit.
454
			if ( isset( $request['download_limit'] ) ) {
455
				$variation->set_download_limit( $request['download_limit'] );
456
			}
457
458
			// Download expiry.
459
			if ( isset( $request['download_expiry'] ) ) {
460
				$variation->set_download_expiry( $request['download_expiry'] );
461
			}
462
		}
463
464
		// Shipping data.
465
		$variation = $this->save_product_shipping_data( $variation, $request );
466
467
		// Stock handling.
468
		if ( isset( $request['manage_stock'] ) ) {
469
			$variation->set_manage_stock( $request['manage_stock'] );
470
		}
471
472
		if ( isset( $request['stock_status'] ) ) {
473
			$variation->set_stock_status( $request['stock_status'] );
474
		}
475
476
		if ( isset( $request['backorders'] ) ) {
477
			$variation->set_backorders( $request['backorders'] );
478
		}
479
480
		if ( $variation->get_manage_stock() ) {
481
			if ( isset( $request['stock_quantity'] ) ) {
482
				$variation->set_stock_quantity( $request['stock_quantity'] );
483
			} elseif ( isset( $request['inventory_delta'] ) ) {
484
				$stock_quantity  = wc_stock_amount( $variation->get_stock_quantity() );
485
				$stock_quantity += wc_stock_amount( $request['inventory_delta'] );
486
				$variation->set_stock_quantity( $stock_quantity );
487
			}
488
		} else {
489
			$variation->set_backorders( 'no' );
490
			$variation->set_stock_quantity( '' );
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type double|null expected by parameter $quantity of WC_Product::set_stock_quantity(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

490
			$variation->set_stock_quantity( /** @scrutinizer ignore-type */ '' );
Loading history...
491
		}
492
493
		// Regular Price.
494
		if ( isset( $request['regular_price'] ) ) {
495
			$variation->set_regular_price( $request['regular_price'] );
496
		}
497
498
		// Sale Price.
499
		if ( isset( $request['sale_price'] ) ) {
500
			$variation->set_sale_price( $request['sale_price'] );
501
		}
502
503
		if ( isset( $request['date_on_sale_from'] ) ) {
504
			$variation->set_date_on_sale_from( $request['date_on_sale_from'] );
505
		}
506
507
		if ( isset( $request['date_on_sale_from_gmt'] ) ) {
508
			$variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null );
509
		}
510
511
		if ( isset( $request['date_on_sale_to'] ) ) {
512
			$variation->set_date_on_sale_to( $request['date_on_sale_to'] );
513
		}
514
515
		if ( isset( $request['date_on_sale_to_gmt'] ) ) {
516
			$variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null );
517
		}
518
519
		// Tax class.
520
		if ( isset( $request['tax_class'] ) ) {
521
			$variation->set_tax_class( $request['tax_class'] );
522
		}
523
524
		// Description.
525
		if ( isset( $request['description'] ) ) {
526
			$variation->set_description( wp_kses_post( $request['description'] ) );
527
		}
528
529
		// Update taxonomies.
530
		if ( isset( $request['attributes'] ) ) {
531
			$attributes = array();
532
			$parent     = wc_get_product( $variation->get_parent_id() );
533
534
			if ( ! $parent ) {
0 ignored issues
show
introduced by
$parent is of type WC_Product, thus it always evaluated to true.
Loading history...
535
				return new \WP_Error(
536
					// Translators: %d parent ID.
537
					"woocommerce_rest_{$this->post_type}_invalid_parent", sprintf( __( 'Cannot set attributes due to invalid parent product.', 'woocommerce' ), $variation->get_parent_id() ), array(
538
						'status' => 404,
539
					)
540
				);
541
			}
542
543
			$parent_attributes = $parent->get_attributes();
544
545
			foreach ( $request['attributes'] as $attribute ) {
546
				$attribute_id   = 0;
547
				$attribute_name = '';
548
549
				// Check ID for global attributes or name for product attributes.
550
				if ( ! empty( $attribute['id'] ) ) {
551
					$attribute_id   = absint( $attribute['id'] );
552
					$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
553
				} elseif ( ! empty( $attribute['name'] ) ) {
554
					$attribute_name = sanitize_title( $attribute['name'] );
555
				}
556
557
				if ( ! $attribute_id && ! $attribute_name ) {
558
					continue;
559
				}
560
561
				if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) {
562
					continue;
563
				}
564
565
				$attribute_key   = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() );
566
				$attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';
567
568
				if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) {
569
					// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
570
					$term = get_term_by( 'name', $attribute_value, $attribute_name );
571
572
					if ( $term && ! is_wp_error( $term ) ) {
573
						$attribute_value = $term->slug;
574
					} else {
575
						$attribute_value = sanitize_title( $attribute_value );
576
					}
577
				}
578
579
				$attributes[ $attribute_key ] = $attribute_value;
580
			}
581
582
			$variation->set_attributes( $attributes );
583
		}
584
585
		// Menu order.
586
		if ( $request['menu_order'] ) {
587
			$variation->set_menu_order( $request['menu_order'] );
588
		}
589
590
		// Meta data.
591
		if ( is_array( $request['meta_data'] ) ) {
592
			foreach ( $request['meta_data'] as $meta ) {
593
				$variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
0 ignored issues
show
Bug introduced by
It seems like IssetNode ? $meta['id'] : '' can also be of type string; however, parameter $meta_id of WC_Data::update_meta_data() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

593
				$variation->update_meta_data( $meta['key'], $meta['value'], /** @scrutinizer ignore-type */ isset( $meta['id'] ) ? $meta['id'] : '' );
Loading history...
594
			}
595
		}
596
597
		/**
598
		 * Filters an object before it is inserted via the REST API.
599
		 *
600
		 * The dynamic portion of the hook name, `$this->post_type`,
601
		 * refers to the object type slug.
602
		 *
603
		 * @param \WC_Data         $variation Object object.
604
		 * @param \WP_REST_Request $request   Request object.
605
		 * @param bool            $creating  If is creating a new object.
606
		 */
607
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $variation, $request, $creating );
608
	}
609
610
	/**
611
	 * Clear caches here so in sync with any new variations.
612
	 *
613
	 * @param \WC_Data $object Object data.
614
	 */
615
	public function clear_transients( $object ) {
616
		wc_delete_product_transients( $object->get_parent_id() );
617
		wp_cache_delete( 'product-' . $object->get_parent_id(), 'products' );
618
	}
619
620
	/**
621
	 * Delete a variation.
622
	 *
623
	 * @param \WP_REST_Request $request Full details about the request.
624
	 *
625
	 * @return bool|\WP_Error|\WP_REST_Response
626
	 */
627
	public function delete_item( $request ) {
628
		$force  = (bool) $request['force'];
629
		$object = $this->get_object( (int) $request['id'] );
630
		$result = false;
631
632
		if ( ! $object || 0 === $object->get_id() ) {
0 ignored issues
show
introduced by
$object is of type WC_Product, thus it always evaluated to true.
Loading history...
633
			return new \WP_Error(
634
				"woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array(
635
					'status' => 404,
636
				)
637
			);
638
		}
639
640
		$supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) );
641
642
		/**
643
		 * Filter whether an object is trashable.
644
		 *
645
		 * Return false to disable trash support for the object.
646
		 *
647
		 * @param boolean  $supports_trash Whether the object type support trashing.
648
		 * @param \WC_Data $object         The object being considered for trashing support.
649
		 */
650
		$supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object );
651
652
		if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) {
653
			return new \WP_Error(
654
				/* translators: %s: post type */
655
				"woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array(
656
					'status' => rest_authorization_required_code(),
657
				)
658
			);
659
		}
660
661
		$request->set_param( 'context', 'edit' );
662
663
		// If we're forcing, then delete permanently.
664
		if ( $force ) {
665
			$previous = $this->prepare_object_for_response( $object, $request );
666
667
			$object->delete( true );
668
669
			$result   = 0 === $object->get_id();
670
			$response = new \WP_REST_Response();
671
			$response->set_data(
672
				array(
673
					'deleted'  => true,
674
					'previous' => $previous->get_data(),
675
				)
676
			);
677
		} else {
678
			// If we don't support trashing for this type, error out.
679
			if ( ! $supports_trash ) {
680
				return new \WP_Error(
681
					/* translators: %s: post type */
682
					'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array(
683
						'status' => 501,
684
					)
685
				);
686
			}
687
688
			// Otherwise, only trash if we haven't already.
689
			if ( is_callable( array( $object, 'get_status' ) ) ) {
690
				if ( 'trash' === $object->get_status() ) {
691
					return new \WP_Error(
692
						/* translators: %s: post type */
693
						'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array(
694
							'status' => 410,
695
						)
696
					);
697
				}
698
699
				$object->delete();
700
				$result = 'trash' === $object->get_status();
701
			}
702
703
			$response = $this->prepare_object_for_response( $object, $request );
704
		}
705
706
		if ( ! $result ) {
707
			return new \WP_Error(
708
				/* translators: %s: post type */
709
				'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array(
710
					'status' => 500,
711
				)
712
			);
713
		}
714
715
		/**
716
		 * Fires after a single object is deleted or trashed via the REST API.
717
		 *
718
		 * @param \WC_Data          $object   The deleted or trashed object.
719
		 * @param \WP_REST_Response $response The response data.
720
		 * @param \WP_REST_Request  $request  The request sent to the API.
721
		 */
722
		do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request );
723
724
		return $response;
725
	}
726
727
	/**
728
	 * Bulk create, update and delete items.
729
	 *
730
	 * @since  3.0.0
731
	 * @param \WP_REST_Request $request Full details about the request.
732
	 * @return array Of \WP_Error or \WP_REST_Response.
733
	 */
734
	public function batch_items( $request ) {
735
		$items       = array_filter( $request->get_params() );
736
		$params      = $request->get_url_params();
737
		$product_id  = $params['product_id'];
738
		$body_params = array();
739
740
		foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) {
741
			if ( ! empty( $items[ $batch_type ] ) ) {
742
				$injected_items = array();
743
				foreach ( $items[ $batch_type ] as $item ) {
744
					$injected_items[] = is_array( $item ) ? array_merge(
745
						array(
746
							'product_id' => $product_id,
747
						),
748
						$item
749
					) : $item;
750
				}
751
				$body_params[ $batch_type ] = $injected_items;
752
			}
753
		}
754
755
		$request = new \WP_REST_Request( $request->get_method() );
756
		$request->set_body_params( $body_params );
757
758
		return parent::batch_items( $request );
759
	}
760
761
	/**
762
	 * Prepare links for the request.
763
	 *
764
	 * @param \WC_Data         $object  Object data.
765
	 * @param \WP_REST_Request $request Request object.
766
	 * @return array                   Links for the given post.
767
	 */
768
	protected function prepare_links( $object, $request ) {
769
		$product_id = (int) $request['product_id'];
770
		$base       = str_replace( '(?P<product_id>[\d]+)', $product_id, $this->rest_base );
771
		$links      = array(
772
			'self'       => array(
773
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_id() ) ),
774
			),
775
			'collection' => array(
776
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
777
			),
778
			'up'         => array(
779
				'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product_id ) ),
780
			),
781
		);
782
		return $links;
783
	}
784
785
	/**
786
	 * Get the Variation's schema, conforming to JSON Schema.
787
	 *
788
	 * @return array
789
	 */
790
	public function get_item_schema() {
791
		$weight_unit    = get_option( 'woocommerce_weight_unit' );
792
		$dimension_unit = get_option( 'woocommerce_dimension_unit' );
793
		$schema         = array(
794
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
795
			'title'      => $this->post_type,
796
			'type'       => 'object',
797
			'properties' => array(
798
				'id'                    => array(
799
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
800
					'type'        => 'integer',
801
					'context'     => array( 'view', 'edit' ),
802
					'readonly'    => true,
803
				),
804
				'name'                  => array(
805
					'description' => __( 'Product parent name.', 'woocommerce' ),
806
					'type'        => 'string',
807
					'context'     => array( 'view', 'edit' ),
808
				),
809
				'type'                  => array(
810
					'description' => __( 'Product type.', 'woocommerce' ),
811
					'type'        => 'string',
812
					'default'     => 'variation',
813
					'enum'        => array( 'variation' ),
814
					'context'     => array( 'view', 'edit' ),
815
				),
816
				'parent_id'             => array(
817
					'description' => __( 'Product parent ID.', 'woocommerce' ),
818
					'type'        => 'integer',
819
					'context'     => array( 'view', 'edit' ),
820
				),
821
				'date_created'          => array(
822
					'description' => __( "The date the variation was created, in the site's timezone.", 'woocommerce' ),
823
					'type'        => 'date-time',
824
					'context'     => array( 'view', 'edit' ),
825
					'readonly'    => true,
826
				),
827
				'date_modified'         => array(
828
					'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ),
829
					'type'        => 'date-time',
830
					'context'     => array( 'view', 'edit' ),
831
					'readonly'    => true,
832
				),
833
				'description'           => array(
834
					'description' => __( 'Variation description.', 'woocommerce' ),
835
					'type'        => 'string',
836
					'context'     => array( 'view', 'edit' ),
837
				),
838
				'permalink'             => array(
839
					'description' => __( 'Variation URL.', 'woocommerce' ),
840
					'type'        => 'string',
841
					'format'      => 'uri',
842
					'context'     => array( 'view', 'edit' ),
843
					'readonly'    => true,
844
				),
845
				'sku'                   => array(
846
					'description' => __( 'Unique identifier.', 'woocommerce' ),
847
					'type'        => 'string',
848
					'context'     => array( 'view', 'edit' ),
849
				),
850
				'price'                 => array(
851
					'description' => __( 'Current variation price.', 'woocommerce' ),
852
					'type'        => 'string',
853
					'context'     => array( 'view', 'edit' ),
854
					'readonly'    => true,
855
				),
856
				'regular_price'         => array(
857
					'description' => __( 'Variation regular price.', 'woocommerce' ),
858
					'type'        => 'string',
859
					'context'     => array( 'view', 'edit' ),
860
				),
861
				'sale_price'            => array(
862
					'description' => __( 'Variation sale price.', 'woocommerce' ),
863
					'type'        => 'string',
864
					'context'     => array( 'view', 'edit' ),
865
				),
866
				'date_on_sale_from'     => array(
867
					'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ),
868
					'type'        => 'date-time',
869
					'context'     => array( 'view', 'edit' ),
870
				),
871
				'date_on_sale_from_gmt' => array(
872
					'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ),
873
					'type'        => 'date-time',
874
					'context'     => array( 'view', 'edit' ),
875
				),
876
				'date_on_sale_to'       => array(
877
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
878
					'type'        => 'date-time',
879
					'context'     => array( 'view', 'edit' ),
880
				),
881
				'date_on_sale_to_gmt'   => array(
882
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
883
					'type'        => 'date-time',
884
					'context'     => array( 'view', 'edit' ),
885
				),
886
				'on_sale'               => array(
887
					'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ),
888
					'type'        => 'boolean',
889
					'context'     => array( 'view', 'edit' ),
890
					'readonly'    => true,
891
				),
892
				'status'                => array(
893
					'description' => __( 'Variation status.', 'woocommerce' ),
894
					'type'        => 'string',
895
					'default'     => 'publish',
896
					'enum'        => array_keys( get_post_statuses() ),
897
					'context'     => array( 'view', 'edit' ),
898
				),
899
				'purchasable'           => array(
900
					'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ),
901
					'type'        => 'boolean',
902
					'context'     => array( 'view', 'edit' ),
903
					'readonly'    => true,
904
				),
905
				'virtual'               => array(
906
					'description' => __( 'If the variation is virtual.', 'woocommerce' ),
907
					'type'        => 'boolean',
908
					'default'     => false,
909
					'context'     => array( 'view', 'edit' ),
910
				),
911
				'downloadable'          => array(
912
					'description' => __( 'If the variation is downloadable.', 'woocommerce' ),
913
					'type'        => 'boolean',
914
					'default'     => false,
915
					'context'     => array( 'view', 'edit' ),
916
				),
917
				'downloads'             => array(
918
					'description' => __( 'List of downloadable files.', 'woocommerce' ),
919
					'type'        => 'array',
920
					'context'     => array( 'view', 'edit' ),
921
					'items'       => array(
922
						'type'       => 'object',
923
						'properties' => array(
924
							'id'   => array(
925
								'description' => __( 'File ID.', 'woocommerce' ),
926
								'type'        => 'string',
927
								'context'     => array( 'view', 'edit' ),
928
							),
929
							'name' => array(
930
								'description' => __( 'File name.', 'woocommerce' ),
931
								'type'        => 'string',
932
								'context'     => array( 'view', 'edit' ),
933
							),
934
							'file' => array(
935
								'description' => __( 'File URL.', 'woocommerce' ),
936
								'type'        => 'string',
937
								'context'     => array( 'view', 'edit' ),
938
							),
939
						),
940
					),
941
				),
942
				'download_limit'        => array(
943
					'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ),
944
					'type'        => 'integer',
945
					'default'     => -1,
946
					'context'     => array( 'view', 'edit' ),
947
				),
948
				'download_expiry'       => array(
949
					'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ),
950
					'type'        => 'integer',
951
					'default'     => -1,
952
					'context'     => array( 'view', 'edit' ),
953
				),
954
				'tax_status'            => array(
955
					'description' => __( 'Tax status.', 'woocommerce' ),
956
					'type'        => 'string',
957
					'default'     => 'taxable',
958
					'enum'        => array( 'taxable', 'shipping', 'none' ),
959
					'context'     => array( 'view', 'edit' ),
960
				),
961
				'tax_class'             => array(
962
					'description' => __( 'Tax class.', 'woocommerce' ),
963
					'type'        => 'string',
964
					'context'     => array( 'view', 'edit' ),
965
				),
966
				'manage_stock'          => array(
967
					'description' => __( 'Stock management at variation level.', 'woocommerce' ),
968
					'type'        => 'boolean',
969
					'default'     => false,
970
					'context'     => array( 'view', 'edit' ),
971
				),
972
				'stock_quantity'        => array(
973
					'description' => __( 'Stock quantity.', 'woocommerce' ),
974
					'type'        => 'integer',
975
					'context'     => array( 'view', 'edit' ),
976
				),
977
				'stock_status'          => array(
978
					'description' => __( 'Controls the stock status of the product.', 'woocommerce' ),
979
					'type'        => 'string',
980
					'default'     => 'instock',
981
					'enum'        => array_keys( wc_get_product_stock_status_options() ),
982
					'context'     => array( 'view', 'edit' ),
983
				),
984
				'backorders'            => array(
985
					'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
986
					'type'        => 'string',
987
					'default'     => 'no',
988
					'enum'        => array( 'no', 'notify', 'yes' ),
989
					'context'     => array( 'view', 'edit' ),
990
				),
991
				'backorders_allowed'    => array(
992
					'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ),
993
					'type'        => 'boolean',
994
					'context'     => array( 'view', 'edit' ),
995
					'readonly'    => true,
996
				),
997
				'backordered'           => array(
998
					'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ),
999
					'type'        => 'boolean',
1000
					'context'     => array( 'view', 'edit' ),
1001
					'readonly'    => true,
1002
				),
1003
				'weight'                => array(
1004
					/* translators: %s: weight unit */
1005
					'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ),
0 ignored issues
show
Bug introduced by
It seems like $weight_unit can also be of type false; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1005
					'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), /** @scrutinizer ignore-type */ $weight_unit ),
Loading history...
1006
					'type'        => 'string',
1007
					'context'     => array( 'view', 'edit' ),
1008
				),
1009
				'dimensions'            => array(
1010
					'description' => __( 'Variation dimensions.', 'woocommerce' ),
1011
					'type'        => 'object',
1012
					'context'     => array( 'view', 'edit' ),
1013
					'properties'  => array(
1014
						'length' => array(
1015
							/* translators: %s: dimension unit */
1016
							'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ),
1017
							'type'        => 'string',
1018
							'context'     => array( 'view', 'edit' ),
1019
						),
1020
						'width'  => array(
1021
							/* translators: %s: dimension unit */
1022
							'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ),
1023
							'type'        => 'string',
1024
							'context'     => array( 'view', 'edit' ),
1025
						),
1026
						'height' => array(
1027
							/* translators: %s: dimension unit */
1028
							'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ),
1029
							'type'        => 'string',
1030
							'context'     => array( 'view', 'edit' ),
1031
						),
1032
					),
1033
				),
1034
				'shipping_class'        => array(
1035
					'description' => __( 'Shipping class slug.', 'woocommerce' ),
1036
					'type'        => 'string',
1037
					'context'     => array( 'view', 'edit' ),
1038
				),
1039
				'shipping_class_id'     => array(
1040
					'description' => __( 'Shipping class ID.', 'woocommerce' ),
1041
					'type'        => 'string',
1042
					'context'     => array( 'view', 'edit' ),
1043
					'readonly'    => true,
1044
				),
1045
				'image'                 => array(
1046
					'description' => __( 'Variation image data.', 'woocommerce' ),
1047
					'type'        => 'object',
1048
					'context'     => array( 'view', 'edit' ),
1049
					'properties'  => array(
1050
						'id'                => array(
1051
							'description' => __( 'Image ID.', 'woocommerce' ),
1052
							'type'        => 'integer',
1053
							'context'     => array( 'view', 'edit' ),
1054
						),
1055
						'date_created'      => array(
1056
							'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),
1057
							'type'        => 'date-time',
1058
							'context'     => array( 'view', 'edit' ),
1059
							'readonly'    => true,
1060
						),
1061
						'date_created_gmt'  => array(
1062
							'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ),
1063
							'type'        => 'date-time',
1064
							'context'     => array( 'view', 'edit' ),
1065
							'readonly'    => true,
1066
						),
1067
						'date_modified'     => array(
1068
							'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),
1069
							'type'        => 'date-time',
1070
							'context'     => array( 'view', 'edit' ),
1071
							'readonly'    => true,
1072
						),
1073
						'date_modified_gmt' => array(
1074
							'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ),
1075
							'type'        => 'date-time',
1076
							'context'     => array( 'view', 'edit' ),
1077
							'readonly'    => true,
1078
						),
1079
						'src'               => array(
1080
							'description' => __( 'Image URL.', 'woocommerce' ),
1081
							'type'        => 'string',
1082
							'format'      => 'uri',
1083
							'context'     => array( 'view', 'edit' ),
1084
						),
1085
						'name'              => array(
1086
							'description' => __( 'Image name.', 'woocommerce' ),
1087
							'type'        => 'string',
1088
							'context'     => array( 'view', 'edit' ),
1089
						),
1090
						'alt'               => array(
1091
							'description' => __( 'Image alternative text.', 'woocommerce' ),
1092
							'type'        => 'string',
1093
							'context'     => array( 'view', 'edit' ),
1094
						),
1095
					),
1096
				),
1097
				'attributes'            => array(
1098
					'description' => __( 'List of attributes.', 'woocommerce' ),
1099
					'type'        => 'array',
1100
					'context'     => array( 'view', 'edit' ),
1101
					'items'       => array(
1102
						'type'       => 'object',
1103
						'properties' => array(
1104
							'id'     => array(
1105
								'description' => __( 'Attribute ID.', 'woocommerce' ),
1106
								'type'        => 'integer',
1107
								'context'     => array( 'view', 'edit' ),
1108
							),
1109
							'name'   => array(
1110
								'description' => __( 'Attribute name.', 'woocommerce' ),
1111
								'type'        => 'string',
1112
								'context'     => array( 'view', 'edit' ),
1113
							),
1114
							'option' => array(
1115
								'description' => __( 'Selected attribute term name.', 'woocommerce' ),
1116
								'type'        => 'string',
1117
								'context'     => array( 'view', 'edit' ),
1118
							),
1119
						),
1120
					),
1121
				),
1122
				'menu_order'            => array(
1123
					'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ),
1124
					'type'        => 'integer',
1125
					'context'     => array( 'view', 'edit' ),
1126
				),
1127
				'meta_data'             => array(
1128
					'description' => __( 'Meta data.', 'woocommerce' ),
1129
					'type'        => 'array',
1130
					'context'     => array( 'view', 'edit' ),
1131
					'items'       => array(
1132
						'type'       => 'object',
1133
						'properties' => array(
1134
							'id'    => array(
1135
								'description' => __( 'Meta ID.', 'woocommerce' ),
1136
								'type'        => 'integer',
1137
								'context'     => array( 'view', 'edit' ),
1138
								'readonly'    => true,
1139
							),
1140
							'key'   => array(
1141
								'description' => __( 'Meta key.', 'woocommerce' ),
1142
								'type'        => 'string',
1143
								'context'     => array( 'view', 'edit' ),
1144
							),
1145
							'value' => array(
1146
								'description' => __( 'Meta value.', 'woocommerce' ),
1147
								'type'        => 'mixed',
1148
								'context'     => array( 'view', 'edit' ),
1149
							),
1150
						),
1151
					),
1152
				),
1153
			),
1154
		);
1155
		return $this->add_additional_fields_schema( $schema );
1156
	}
1157
1158
	/**
1159
	 * Get the query params for collections of attachments.
1160
	 *
1161
	 * @return array
1162
	 */
1163
	public function get_collection_params() {
1164
		$params = parent::get_collection_params();
1165
1166
		unset(
1167
			$params['in_stock'],
1168
			$params['type'],
1169
			$params['featured'],
1170
			$params['category'],
1171
			$params['tag'],
1172
			$params['shipping_class'],
1173
			$params['attribute'],
1174
			$params['attribute_term']
1175
		);
1176
1177
		$params['stock_status'] = array(
1178
			'description'       => __( 'Limit result set to products with specified stock status.', 'woocommerce' ),
1179
			'type'              => 'string',
1180
			'enum'              => array_keys( wc_get_product_stock_status_options() ),
1181
			'sanitize_callback' => 'sanitize_text_field',
1182
			'validate_callback' => 'rest_validate_request_arg',
1183
		);
1184
1185
		$params['search'] = array(
1186
			'description'       => __( 'Search by similar product name or sku.', 'woocommerce' ),
1187
			'type'              => 'string',
1188
			'validate_callback' => 'rest_validate_request_arg',
1189
		);
1190
1191
		return $params;
1192
	}
1193
}
1194