Completed
Push — master ( 6a32d5...8709d3 )
by Claudio
12:09
created

WC_REST_Product_Attributes_Controller   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 599
Duplicated Lines 8.85 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 0
Metric Value
dl 53
loc 599
rs 7.0642
c 0
b 0
f 0
wmc 54
lcom 2
cbo 1

18 Methods

Rating   Name   Duplication   Size   Complexity  
A register_routes() 0 50 1
A get_items_permissions_check() 7 7 2
A create_item_permissions_check() 7 7 2
A get_item_permissions_check() 11 11 3
A update_item_permissions_check() 11 11 3
A delete_item_permissions_check() 11 11 3
A get_items() 0 11 2
B create_item() 0 63 5
A get_item() 0 13 2
C update_item() 3 72 8
B delete_item() 3 56 7
B prepare_item_for_response() 0 29 2
A prepare_links() 0 13 1
A get_item_schema() 0 53 1
A get_collection_params() 0 6 1
A get_taxonomy() 0 13 3
A get_attribute() 0 15 3
B validate_attribute_slug() 0 11 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_REST_Product_Attributes_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_REST_Product_Attributes_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 23 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * REST API Product Attributes controller
4
 *
5
 * Handles requests to the products/attributes endpoint.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.6.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Product Attributes controller class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WC_REST_Controller
22
 */
23
class WC_REST_Product_Attributes_Controller extends WC_REST_Controller {
24
25
	/**
26
	 * Endpoint namespace.
27
	 *
28
	 * @var string
29
	 */
30
	protected $namespace = 'wc/v1';
31
32
	/**
33
	 * Route base.
34
	 *
35
	 * @var string
36
	 */
37
	protected $rest_base = 'products/attributes';
38
39
	/**
40
	 * Attribute name.
41
	 *
42
	 * @var string
43
	 */
44
	protected $attribute = '';
45
46
	/**
47
	 * Register the routes for product attributes.
48
	 */
49
	public function register_routes() {
50
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
51
			array(
52
				'methods'             => WP_REST_Server::READABLE,
53
				'callback'            => array( $this, 'get_items' ),
54
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
55
				'args'                => $this->get_collection_params(),
56
			),
57
			array(
58
				'methods'             => WP_REST_Server::CREATABLE,
59
				'callback'            => array( $this, 'create_item' ),
60
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
61
				'args'                => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
62
					'name' => array(
63
						'required' => true,
64
					),
65
				) ),
66
			),
67
			'schema' => array( $this, 'get_public_item_schema' ),
68
		));
69
70
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
71
			array(
72
				'methods'             => WP_REST_Server::READABLE,
73
				'callback'            => array( $this, 'get_item' ),
74
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
75
				'args'                => array(
76
					'context'         => $this->get_context_param( array( 'default' => 'view' ) ),
77
				),
78
			),
79
			array(
80
				'methods'             => WP_REST_Server::EDITABLE,
81
				'callback'            => array( $this, 'update_item' ),
82
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
83
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
84
			),
85
			array(
86
				'methods'             => WP_REST_Server::DELETABLE,
87
				'callback'            => array( $this, 'delete_item' ),
88
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
89
				'args'                => array(
90
					'force' => array(
91
						'default'     => false,
92
						'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
93
					),
94
				),
95
			),
96
			'schema' => array( $this, 'get_public_item_schema' ),
97
		) );
98
	}
99
100
	/**
101
	 * Check if a given request has access to read the attributes.
102
	 *
103
	 * @param  WP_REST_Request $request Full details about the request.
104
	 * @return WP_Error|boolean
105
	 */
106 View Code Duplication
	public function get_items_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
108
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
109
		}
110
111
		return true;
112
	}
113
114
	/**
115
	 * Check if a given request has access to create a attribute.
116
	 *
117
	 * @param  WP_REST_Request $request Full details about the request.
118
	 * @return WP_Error|boolean
119
	 */
120 View Code Duplication
	public function create_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'create' ) ) {
122
			return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you cannot create new resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
123
		}
124
125
		return true;
126
	}
127
128
	/**
129
	 * Check if a given request has access to read a attribute.
130
	 *
131
	 * @param  WP_REST_Request $request Full details about the request.
132
	 * @return WP_Error|boolean
133
	 */
134 View Code Duplication
	public function get_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
		if ( ! $this->get_taxonomy( $request ) ) {
136
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
137
		}
138
139
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
140
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
141
		}
142
143
		return true;
144
	}
145
146
	/**
147
	 * Check if a given request has access to update a attribute.
148
	 *
149
	 * @param  WP_REST_Request $request Full details about the request.
150
	 * @return WP_Error|boolean
151
	 */
152 View Code Duplication
	public function update_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
		if ( ! $this->get_taxonomy( $request ) ) {
154
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
155
		}
156
157
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'edit' ) ) {
158
			return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
159
		}
160
161
		return true;
162
	}
163
164
	/**
165
	 * Check if a given request has access to delete a attribute.
166
	 *
167
	 * @param  WP_REST_Request $request Full details about the request.
168
	 * @return WP_Error|boolean
169
	 */
170 View Code Duplication
	public function delete_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
171
		if ( ! $this->get_taxonomy( $request ) ) {
172
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
173
		}
174
175
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'delete' ) ) {
176
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you cannot delete resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
177
		}
178
179
		return true;
180
	}
181
182
	/**
183
	 * Get all attributes.
184
	 *
185
	 * @param WP_REST_Request $request
186
	 * @return array
187
	 */
188
	public function get_items( $request ) {
189
		$attributes = wc_get_attribute_taxonomies();
190
		$data       = array();
191
		foreach ( $attributes as $attribute_obj ) {
192
			$attribute = $this->prepare_item_for_response( $attribute_obj, $request );
193
			$attribute = $this->prepare_response_for_collection( $attribute );
194
			$data[] = $attribute;
195
		}
196
197
		return rest_ensure_response( $data );
198
	}
199
200
	/**
201
	 * Create a single attribute.
202
	 *
203
	 * @param WP_REST_Request $request Full details about the request.
204
	 * @return WP_REST_Request|WP_Error
205
	 */
206
	public function create_item( $request ) {
207
		global $wpdb;
208
209
		$args = array(
210
			'attribute_label'   => $request['name'],
211
			'attribute_name'    => $request['slug'],
212
			'attribute_type'    => $request['type'],
213
			'attribute_orderby' => $request['order_by'],
214
			'attribute_public'  => $request['has_archives'],
215
		);
216
217
		// Set the attribute slug.
218
		if ( empty( $args['attribute_name'] ) ) {
219
			$args['attribute_name'] = wc_sanitize_taxonomy_name( stripslashes( $args['attribute_label'] ) );
220
		} else {
221
			$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
222
		}
223
224
		$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
225
		if ( is_wp_error( $valid_slug ) ) {
226
			return $valid_slug;
227
		}
228
229
		$insert = $wpdb->insert(
230
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
231
			$args,
232
			array( '%s', '%s', '%s', '%s', '%d' )
233
		);
234
235
		// Checks for errors.
236
		if ( is_wp_error( $insert ) ) {
237
			return new WP_Error( 'woocommerce_rest_cannot_create', $insert->get_error_message(), array( 'status' => 400 ) );
238
		}
239
240
		$attribute = $this->get_attribute( $wpdb->insert_id );
241
242
		if ( is_wp_error( $attribute ) ) {
243
			return $attribute;
244
		}
245
246
		$this->update_additional_fields_for_object( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($wpdb->insert_id) on line 240 can also be of type object<stdClass>; however, WP_REST_Controller::upda...nal_fields_for_object() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
247
248
		/**
249
		 * Fires after a single product attribute is created or updated via the REST API.
250
		 *
251
		 * @param stdObject       $attribute Inserted attribute object.
252
		 * @param WP_REST_Request $request   Request object.
253
		 * @param boolean         $creating  True when creating attribute, false when updating.
254
		 */
255
		do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, true );
256
257
		$request->set_param( 'context', 'edit' );
258
		$response = $this->prepare_item_for_response( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($wpdb->insert_id) on line 240 can also be of type object<stdClass>; however, WC_REST_Product_Attribut...are_item_for_response() does only seem to accept object<obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
259
		$response = rest_ensure_response( $response );
260
		$response->set_status( 201 );
261
		$response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $attribute->attribute_id ) );
262
263
		// Clear transients.
264
		flush_rewrite_rules();
265
		delete_transient( 'wc_attribute_taxonomies' );
266
267
		return $response;
268
	}
269
270
	/**
271
	 * Get a single attribute.
272
	 *
273
	 * @param WP_REST_Request $request Full details about the request.
274
	 * @return WP_REST_Request|WP_Error
275
	 */
276
	public function get_item( $request ) {
277
		global $wpdb;
278
279
		$attribute = $this->get_attribute( $request['id'] );
280
281
		if ( is_wp_error( $attribute ) ) {
282
			return $attribute;
283
		}
284
285
		$response = $this->prepare_item_for_response( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($request['id']) on line 279 can also be of type object<stdClass>; however, WC_REST_Product_Attribut...are_item_for_response() does only seem to accept object<obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
286
287
		return rest_ensure_response( $response );
288
	}
289
290
	/**
291
	 * Update a single term from a taxonomy.
292
	 *
293
	 * @param WP_REST_Request $request Full details about the request.
294
	 * @return WP_REST_Request|WP_Error
295
	 */
296
	public function update_item( $request ) {
297
		global $wpdb;
298
299
		$id     = (int) $request['id'];
300
		$format = array( '%s', '%s', '%s', '%s', '%d' );
301
		$args   = array(
302
			'attribute_label'   => $request['name'],
303
			'attribute_name'    => $request['slug'],
304
			'attribute_type'    => $request['type'],
305
			'attribute_orderby' => $request['order_by'],
306
			'attribute_public'  => $request['has_archives'],
307
		);
308
309
		$i = 0;
310
		foreach ( $args as $key => $value ) {
311
			if ( empty( $value ) && ! is_bool( $value ) ) {
312
				unset( $args[ $key ] );
313
				unset( $format[ $i ] );
314
			}
315
316
			$i++;
317
		}
318
319
		// Set the attribute slug.
320
		if ( ! empty( $args['attribute_name'] ) ) {
321
			$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
322
323
			$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
324
			if ( is_wp_error( $valid_slug ) ) {
325
				return $valid_slug;
326
			}
327
		}
328
329
		$update = $wpdb->update(
330
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
331
			$args,
332
			array( 'attribute_id' => $id ),
333
			$format,
334
			array( '%d' )
335
		);
336
337
		// Checks for errors.
338 View Code Duplication
		if ( false === $update ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
339
			return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Could not edit the attribute', 'woocommerce' ), array( 'status' => 400 ) );
340
		}
341
342
		$attribute = $this->get_attribute( $id );
343
344
		if ( is_wp_error( $attribute ) ) {
345
			return $attribute;
346
		}
347
348
		$this->update_additional_fields_for_object( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($id) on line 342 can also be of type object<stdClass>; however, WP_REST_Controller::upda...nal_fields_for_object() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
349
350
		/**
351
		 * Fires after a single product attribute is created or updated via the REST API.
352
		 *
353
		 * @param stdObject       $attribute Inserted attribute object.
354
		 * @param WP_REST_Request $request   Request object.
355
		 * @param boolean         $creating  True when creating attribute, false when updating.
356
		 */
357
		do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, false );
358
359
		$request->set_param( 'context', 'edit' );
360
		$response = $this->prepare_item_for_response( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($id) on line 342 can also be of type object<stdClass>; however, WC_REST_Product_Attribut...are_item_for_response() does only seem to accept object<obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
361
362
		// Clear transients.
363
		flush_rewrite_rules();
364
		delete_transient( 'wc_attribute_taxonomies' );
365
366
		return rest_ensure_response( $response );
367
	}
368
369
	/**
370
	 * Delete a single attribute.
371
	 *
372
	 * @param WP_REST_Request $request Full details about the request.
373
	 * @return WP_REST_Response|WP_Error
374
	 */
375
	public function delete_item( $request ) {
376
		global $wpdb;
377
378
		$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
379
380
		// We don't support trashing for this type, error out.
381
		if ( ! $force ) {
382
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
383
		}
384
385
		$attribute = $this->get_attribute( $request['id'] );
386
387
		if ( is_wp_error( $attribute ) ) {
388
			return $attribute;
389
		}
390
391
		$request->set_param( 'context', 'edit' );
392
		$response = $this->prepare_item_for_response( $attribute, $request );
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->get_attribute($request['id']) on line 385 can also be of type object<stdClass>; however, WC_REST_Product_Attribut...are_item_for_response() does only seem to accept object<obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
393
394
		$deleted = $wpdb->delete(
395
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
396
			array( 'attribute_id' => $attribute->attribute_id ),
397
			array( '%d' )
398
		);
399
400 View Code Duplication
		if ( false === $deleted ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
401
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
402
		}
403
404
		$taxonomy = wc_attribute_taxonomy_name( $attribute->attribute_name );
405
406
		if ( taxonomy_exists( $taxonomy ) ) {
407
			$terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
408
			foreach ( $terms as $term ) {
409
				wp_delete_term( $term->term_id, $taxonomy );
410
			}
411
		}
412
413
		/**
414
		 * Fires after a single attribute is deleted via the REST API.
415
		 *
416
		 * @param stdObject        $attribute     The deleted attribute.
417
		 * @param WP_REST_Response $response The response data.
418
		 * @param WP_REST_Request  $request  The request sent to the API.
419
		 */
420
		do_action( 'woocommerce_rest_delete_product_attribute', $attribute, $response, $request );
421
422
		// Fires woocommerce_attribute_deleted hook.
423
		do_action( 'woocommerce_attribute_deleted', $attribute->attribute_id, $attribute->attribute_name, $taxonomy );
424
425
		// Clear transients.
426
		flush_rewrite_rules();
427
		delete_transient( 'wc_attribute_taxonomies' );
428
429
		return $response;
430
	}
431
432
	/**
433
	 * Prepare a single product attribute output for response.
434
	 *
435
	 * @param obj $item Term object.
436
	 * @param WP_REST_Request $request
437
	 * @return WP_REST_Response $response
438
	 */
439
	public function prepare_item_for_response( $item, $request ) {
440
		$data = array(
441
			'id'           => (int) $item->attribute_id,
442
			'name'         => $item->attribute_label,
443
			'slug'         => wc_attribute_taxonomy_name( $item->attribute_name ),
444
			'type'         => $item->attribute_type,
445
			'order_by'     => $item->attribute_orderby,
446
			'has_archives' => (bool) $item->attribute_public,
447
		);
448
449
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
450
		$data    = $this->add_additional_fields_to_object( $data, $request );
451
		$data    = $this->filter_response_by_context( $data, $context );
452
453
		$response = rest_ensure_response( $data );
454
455
		$response->add_links( $this->prepare_links( $item ) );
456
457
		/**
458
		 * Filter a attribute item returned from the API.
459
		 *
460
		 * Allows modification of the product attribute data right before it is returned.
461
		 *
462
		 * @param WP_REST_Response  $response  The response object.
463
		 * @param object            $item      The original attribute object.
464
		 * @param WP_REST_Request   $request   Request used to generate the response.
465
		 */
466
		return apply_filters( 'woocommerce_rest_prepare_product_attribute', $response, $item, $request );
467
	}
468
469
	/**
470
	 * Prepare links for the request.
471
	 *
472
	 * @param object $attribute Attribute object.
473
	 * @param WP_REST_Request $request Full details about the request.
0 ignored issues
show
Bug introduced by
There is no parameter named $request. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
474
	 * @return array Links for the given attribute.
475
	 */
476
	protected function prepare_links( $attribute ) {
477
		$base  = '/' . $this->namespace . '/' . $this->rest_base;
478
		$links = array(
479
			'self' => array(
480
				'href' => rest_url( trailingslashit( $base ) . $attribute->attribute_id ),
481
			),
482
			'collection' => array(
483
				'href' => rest_url( $base ),
484
			),
485
		);
486
487
		return $links;
488
	}
489
490
	/**
491
	 * Get the Attribute's schema, conforming to JSON Schema.
492
	 *
493
	 * @return array
494
	 */
495
	public function get_item_schema() {
496
		$schema = array(
497
			'$schema'              => 'http://json-schema.org/draft-04/schema#',
498
			'title'                => 'product_attribute',
499
			'type'                 => 'object',
500
			'properties'           => array(
501
				'id' => array(
502
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
503
					'type'        => 'integer',
504
					'context'     => array( 'view', 'edit' ),
505
					'readonly'    => true,
506
				),
507
				'name' => array(
508
					'description' => __( 'Attribute name.', 'woocommerce' ),
509
					'type'        => 'string',
510
					'context'     => array( 'view', 'edit' ),
511
					'arg_options' => array(
512
						'sanitize_callback' => 'sanitize_text_field',
513
					),
514
				),
515
				'slug' => array(
516
					'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
517
					'type'        => 'string',
518
					'context'     => array( 'view', 'edit' ),
519
					'arg_options' => array(
520
						'sanitize_callback' => 'sanitize_title',
521
					),
522
				),
523
				'type' => array(
524
					'description' => __( 'Type of attribute.', 'woocommerce' ),
525
					'type'        => 'string',
526
					'default'     => 'select',
527
					'enum'        => array_keys( wc_get_attribute_types() ),
528
					'context'     => array( 'view', 'edit' ),
529
				),
530
				'order_by' => array(
531
					'description' => __( 'Default sort order.', 'woocommerce' ),
532
					'type'        => 'string',
533
					'default'     => 'menu_order',
534
					'enum'        => array( 'menu_order', 'name', 'name_num', 'id' ),
535
					'context'     => array( 'view', 'edit' ),
536
				),
537
				'has_archives' => array(
538
					'description' => __( 'Enable/Disable attribute archives.', 'woocommerce' ),
539
					'type'        => 'boolean',
540
					'default'     => false,
541
					'context'     => array( 'view', 'edit' ),
542
				),
543
			),
544
		);
545
546
		return $this->add_additional_fields_schema( $schema );
547
	}
548
549
	/**
550
	 * Get the query params for collections
551
	 *
552
	 * @return array
553
	 */
554
	public function get_collection_params() {
555
		$params = array();
556
		$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
557
558
		return $params;
559
	}
560
561
	/**
562
	 * Get attribute name.
563
	 *
564
	 * @param WP_REST_Request $request Full details about the request.
565
	 * @return int|WP_Error
566
	 */
567
	protected function get_taxonomy( $request ) {
568
		if ( '' !== $this->attribute ) {
569
			return $this->attribute;
570
		}
571
572
		if ( $request['id'] ) {
573
			$name = wc_attribute_taxonomy_name_by_id( (int) $request['id'] );
574
575
			$this->attribute = $name;
576
		}
577
578
		return $this->attribute;
579
	}
580
581
	/**
582
	 * Get attribute data.
583
	 *
584
	 * @param int $id Attribute ID.
585
	 * @return stdClass|WP_Error
586
	 */
587
	protected function get_attribute( $id ) {
588
		global $wpdb;
589
590
		$attribute = $wpdb->get_row( $wpdb->prepare( "
591
			SELECT *
592
			FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
593
			WHERE attribute_id = %d
594
		 ", $id ) );
595
596
		if ( is_wp_error( $attribute ) || is_null( $attribute ) ) {
597
			return new WP_Error( 'woocommerce_rest_attribute_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
598
		}
599
600
		return $attribute;
601
	}
602
603
	/**
604
	 * Validate attribute slug.
605
	 *
606
	 * @param string $slug
607
	 * @param bool $new_data
608
	 * @return bool|WP_Error
609
	 */
610
	protected function validate_attribute_slug( $slug, $new_data = true ) {
611
		if ( strlen( $slug ) >= 28 ) {
612
			return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_too_long', sprintf( __( 'Slug "%s" is too long (28 characters max).', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
613
		} else if ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
614
			return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_reserved_name', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
615
		} else if ( $new_data && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) {
616
			return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_already_exists', sprintf( __( 'Slug "%s" is already in use.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
617
		}
618
619
		return true;
620
	}
621
}
622