Completed
Push — master ( 2ddf19...6a32d5 )
by Claudio
09:34
created

WC_REST_Product_Attributes_Controller   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 609
Duplicated Lines 18.56 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 113
loc 609
rs 6.9509
wmc 54
lcom 2
cbo 1

18 Methods

Rating   Name   Duplication   Size   Complexity  
A register_routes() 60 60 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 View Code Duplication
	public function register_routes() {
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...
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
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
100
			array(
101
				'methods'             => WP_REST_Server::EDITABLE,
102
				'callback'            => array( $this, 'batch_items' ),
103
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
104
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
105
			),
106
			'schema' => array( $this, 'get_public_batch_schema' ),
107
		) );
108
	}
109
110
	/**
111
	 * Check if a given request has access to read the attributes.
112
	 *
113
	 * @param  WP_REST_Request $request Full details about the request.
114
	 * @return WP_Error|boolean
115
	 */
116 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...
117
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
118
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
119
		}
120
121
		return true;
122
	}
123
124
	/**
125
	 * Check if a given request has access to create a attribute.
126
	 *
127
	 * @param  WP_REST_Request $request Full details about the request.
128
	 * @return WP_Error|boolean
129
	 */
130 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...
131
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'create' ) ) {
132
			return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you cannot create new resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
133
		}
134
135
		return true;
136
	}
137
138
	/**
139
	 * Check if a given request has access to read a attribute.
140
	 *
141
	 * @param  WP_REST_Request $request Full details about the request.
142
	 * @return WP_Error|boolean
143
	 */
144 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...
145
		if ( ! $this->get_taxonomy( $request ) ) {
146
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
147
		}
148
149
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
150
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
151
		}
152
153
		return true;
154
	}
155
156
	/**
157
	 * Check if a given request has access to update a attribute.
158
	 *
159
	 * @param  WP_REST_Request $request Full details about the request.
160
	 * @return WP_Error|boolean
161
	 */
162 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...
163
		if ( ! $this->get_taxonomy( $request ) ) {
164
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
165
		}
166
167
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'edit' ) ) {
168
			return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
169
		}
170
171
		return true;
172
	}
173
174
	/**
175
	 * Check if a given request has access to delete a attribute.
176
	 *
177
	 * @param  WP_REST_Request $request Full details about the request.
178
	 * @return WP_Error|boolean
179
	 */
180 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...
181
		if ( ! $this->get_taxonomy( $request ) ) {
182
			return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
183
		}
184
185
		if ( ! wc_rest_check_manager_permissions( 'attributes', 'delete' ) ) {
186
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you cannot delete resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
187
		}
188
189
		return true;
190
	}
191
192
	/**
193
	 * Get all attributes.
194
	 *
195
	 * @param WP_REST_Request $request
196
	 * @return array
197
	 */
198
	public function get_items( $request ) {
199
		$attributes = wc_get_attribute_taxonomies();
200
		$data       = array();
201
		foreach ( $attributes as $attribute_obj ) {
202
			$attribute = $this->prepare_item_for_response( $attribute_obj, $request );
203
			$attribute = $this->prepare_response_for_collection( $attribute );
204
			$data[] = $attribute;
205
		}
206
207
		return rest_ensure_response( $data );
208
	}
209
210
	/**
211
	 * Create a single attribute.
212
	 *
213
	 * @param WP_REST_Request $request Full details about the request.
214
	 * @return WP_REST_Request|WP_Error
215
	 */
216
	public function create_item( $request ) {
217
		global $wpdb;
218
219
		$args = array(
220
			'attribute_label'   => $request['name'],
221
			'attribute_name'    => $request['slug'],
222
			'attribute_type'    => $request['type'],
223
			'attribute_orderby' => $request['order_by'],
224
			'attribute_public'  => $request['has_archives'],
225
		);
226
227
		// Set the attribute slug.
228
		if ( empty( $args['attribute_name'] ) ) {
229
			$args['attribute_name'] = wc_sanitize_taxonomy_name( stripslashes( $args['attribute_label'] ) );
230
		} else {
231
			$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
232
		}
233
234
		$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
235
		if ( is_wp_error( $valid_slug ) ) {
236
			return $valid_slug;
237
		}
238
239
		$insert = $wpdb->insert(
240
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
241
			$args,
242
			array( '%s', '%s', '%s', '%s', '%d' )
243
		);
244
245
		// Checks for errors.
246
		if ( is_wp_error( $insert ) ) {
247
			return new WP_Error( 'woocommerce_rest_cannot_create', $insert->get_error_message(), array( 'status' => 400 ) );
248
		}
249
250
		$attribute = $this->get_attribute( $wpdb->insert_id );
251
252
		if ( is_wp_error( $attribute ) ) {
253
			return $attribute;
254
		}
255
256
		$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 250 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...
257
258
		/**
259
		 * Fires after a single product attribute is created or updated via the REST API.
260
		 *
261
		 * @param stdObject       $attribute Inserted attribute object.
262
		 * @param WP_REST_Request $request   Request object.
263
		 * @param boolean         $creating  True when creating attribute, false when updating.
264
		 */
265
		do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, true );
266
267
		$request->set_param( 'context', 'edit' );
268
		$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 250 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...
269
		$response = rest_ensure_response( $response );
270
		$response->set_status( 201 );
271
		$response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $attribute->attribute_id ) );
272
273
		// Clear transients.
274
		flush_rewrite_rules();
275
		delete_transient( 'wc_attribute_taxonomies' );
276
277
		return $response;
278
	}
279
280
	/**
281
	 * Get a single attribute.
282
	 *
283
	 * @param WP_REST_Request $request Full details about the request.
284
	 * @return WP_REST_Request|WP_Error
285
	 */
286
	public function get_item( $request ) {
287
		global $wpdb;
288
289
		$attribute = $this->get_attribute( $request['id'] );
290
291
		if ( is_wp_error( $attribute ) ) {
292
			return $attribute;
293
		}
294
295
		$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 289 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...
296
297
		return rest_ensure_response( $response );
298
	}
299
300
	/**
301
	 * Update a single term from a taxonomy.
302
	 *
303
	 * @param WP_REST_Request $request Full details about the request.
304
	 * @return WP_REST_Request|WP_Error
305
	 */
306
	public function update_item( $request ) {
307
		global $wpdb;
308
309
		$id     = (int) $request['id'];
310
		$format = array( '%s', '%s', '%s', '%s', '%d' );
311
		$args   = array(
312
			'attribute_label'   => $request['name'],
313
			'attribute_name'    => $request['slug'],
314
			'attribute_type'    => $request['type'],
315
			'attribute_orderby' => $request['order_by'],
316
			'attribute_public'  => $request['has_archives'],
317
		);
318
319
		$i = 0;
320
		foreach ( $args as $key => $value ) {
321
			if ( empty( $value ) && ! is_bool( $value ) ) {
322
				unset( $args[ $key ] );
323
				unset( $format[ $i ] );
324
			}
325
326
			$i++;
327
		}
328
329
		// Set the attribute slug.
330
		if ( ! empty( $args['attribute_name'] ) ) {
331
			$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
332
333
			$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
334
			if ( is_wp_error( $valid_slug ) ) {
335
				return $valid_slug;
336
			}
337
		}
338
339
		$update = $wpdb->update(
340
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
341
			$args,
342
			array( 'attribute_id' => $id ),
343
			$format,
344
			array( '%d' )
345
		);
346
347
		// Checks for errors.
348 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...
349
			return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Could not edit the attribute', 'woocommerce' ), array( 'status' => 400 ) );
350
		}
351
352
		$attribute = $this->get_attribute( $id );
353
354
		if ( is_wp_error( $attribute ) ) {
355
			return $attribute;
356
		}
357
358
		$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 352 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...
359
360
		/**
361
		 * Fires after a single product attribute is created or updated via the REST API.
362
		 *
363
		 * @param stdObject       $attribute Inserted attribute object.
364
		 * @param WP_REST_Request $request   Request object.
365
		 * @param boolean         $creating  True when creating attribute, false when updating.
366
		 */
367
		do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, false );
368
369
		$request->set_param( 'context', 'edit' );
370
		$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 352 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...
371
372
		// Clear transients.
373
		flush_rewrite_rules();
374
		delete_transient( 'wc_attribute_taxonomies' );
375
376
		return rest_ensure_response( $response );
377
	}
378
379
	/**
380
	 * Delete a single attribute.
381
	 *
382
	 * @param WP_REST_Request $request Full details about the request.
383
	 * @return WP_REST_Response|WP_Error
384
	 */
385
	public function delete_item( $request ) {
386
		global $wpdb;
387
388
		$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
389
390
		// We don't support trashing for this type, error out.
391
		if ( ! $force ) {
392
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
393
		}
394
395
		$attribute = $this->get_attribute( $request['id'] );
396
397
		if ( is_wp_error( $attribute ) ) {
398
			return $attribute;
399
		}
400
401
		$request->set_param( 'context', 'edit' );
402
		$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 395 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...
403
404
		$deleted = $wpdb->delete(
405
			$wpdb->prefix . 'woocommerce_attribute_taxonomies',
406
			array( 'attribute_id' => $attribute->attribute_id ),
407
			array( '%d' )
408
		);
409
410 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...
411
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
412
		}
413
414
		$taxonomy = wc_attribute_taxonomy_name( $attribute->attribute_name );
415
416
		if ( taxonomy_exists( $taxonomy ) ) {
417
			$terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
418
			foreach ( $terms as $term ) {
419
				wp_delete_term( $term->term_id, $taxonomy );
420
			}
421
		}
422
423
		/**
424
		 * Fires after a single attribute is deleted via the REST API.
425
		 *
426
		 * @param stdObject        $attribute     The deleted attribute.
427
		 * @param WP_REST_Response $response The response data.
428
		 * @param WP_REST_Request  $request  The request sent to the API.
429
		 */
430
		do_action( 'woocommerce_rest_delete_product_attribute', $attribute, $response, $request );
431
432
		// Fires woocommerce_attribute_deleted hook.
433
		do_action( 'woocommerce_attribute_deleted', $attribute->attribute_id, $attribute->attribute_name, $taxonomy );
434
435
		// Clear transients.
436
		flush_rewrite_rules();
437
		delete_transient( 'wc_attribute_taxonomies' );
438
439
		return $response;
440
	}
441
442
	/**
443
	 * Prepare a single product attribute output for response.
444
	 *
445
	 * @param obj $item Term object.
446
	 * @param WP_REST_Request $request
447
	 * @return WP_REST_Response $response
448
	 */
449
	public function prepare_item_for_response( $item, $request ) {
450
		$data = array(
451
			'id'           => (int) $item->attribute_id,
452
			'name'         => $item->attribute_label,
453
			'slug'         => wc_attribute_taxonomy_name( $item->attribute_name ),
454
			'type'         => $item->attribute_type,
455
			'order_by'     => $item->attribute_orderby,
456
			'has_archives' => (bool) $item->attribute_public,
457
		);
458
459
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
460
		$data    = $this->add_additional_fields_to_object( $data, $request );
461
		$data    = $this->filter_response_by_context( $data, $context );
462
463
		$response = rest_ensure_response( $data );
464
465
		$response->add_links( $this->prepare_links( $item ) );
466
467
		/**
468
		 * Filter a attribute item returned from the API.
469
		 *
470
		 * Allows modification of the product attribute data right before it is returned.
471
		 *
472
		 * @param WP_REST_Response  $response  The response object.
473
		 * @param object            $item      The original attribute object.
474
		 * @param WP_REST_Request   $request   Request used to generate the response.
475
		 */
476
		return apply_filters( 'woocommerce_rest_prepare_product_attribute', $response, $item, $request );
477
	}
478
479
	/**
480
	 * Prepare links for the request.
481
	 *
482
	 * @param object $attribute Attribute object.
483
	 * @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...
484
	 * @return array Links for the given attribute.
485
	 */
486
	protected function prepare_links( $attribute ) {
487
		$base  = '/' . $this->namespace . '/' . $this->rest_base;
488
		$links = array(
489
			'self' => array(
490
				'href' => rest_url( trailingslashit( $base ) . $attribute->attribute_id ),
491
			),
492
			'collection' => array(
493
				'href' => rest_url( $base ),
494
			),
495
		);
496
497
		return $links;
498
	}
499
500
	/**
501
	 * Get the Attribute's schema, conforming to JSON Schema.
502
	 *
503
	 * @return array
504
	 */
505
	public function get_item_schema() {
506
		$schema = array(
507
			'$schema'              => 'http://json-schema.org/draft-04/schema#',
508
			'title'                => 'product_attribute',
509
			'type'                 => 'object',
510
			'properties'           => array(
511
				'id' => array(
512
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
513
					'type'        => 'integer',
514
					'context'     => array( 'view', 'edit' ),
515
					'readonly'    => true,
516
				),
517
				'name' => array(
518
					'description' => __( 'Attribute name.', 'woocommerce' ),
519
					'type'        => 'string',
520
					'context'     => array( 'view', 'edit' ),
521
					'arg_options' => array(
522
						'sanitize_callback' => 'sanitize_text_field',
523
					),
524
				),
525
				'slug' => array(
526
					'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
527
					'type'        => 'string',
528
					'context'     => array( 'view', 'edit' ),
529
					'arg_options' => array(
530
						'sanitize_callback' => 'sanitize_title',
531
					),
532
				),
533
				'type' => array(
534
					'description' => __( 'Type of attribute.', 'woocommerce' ),
535
					'type'        => 'string',
536
					'default'     => 'select',
537
					'enum'        => array_keys( wc_get_attribute_types() ),
538
					'context'     => array( 'view', 'edit' ),
539
				),
540
				'order_by' => array(
541
					'description' => __( 'Default sort order.', 'woocommerce' ),
542
					'type'        => 'string',
543
					'default'     => 'menu_order',
544
					'enum'        => array( 'menu_order', 'name', 'name_num', 'id' ),
545
					'context'     => array( 'view', 'edit' ),
546
				),
547
				'has_archives' => array(
548
					'description' => __( 'Enable/Disable attribute archives.', 'woocommerce' ),
549
					'type'        => 'boolean',
550
					'default'     => false,
551
					'context'     => array( 'view', 'edit' ),
552
				),
553
			),
554
		);
555
556
		return $this->add_additional_fields_schema( $schema );
557
	}
558
559
	/**
560
	 * Get the query params for collections
561
	 *
562
	 * @return array
563
	 */
564
	public function get_collection_params() {
565
		$params = array();
566
		$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
567
568
		return $params;
569
	}
570
571
	/**
572
	 * Get attribute name.
573
	 *
574
	 * @param WP_REST_Request $request Full details about the request.
575
	 * @return int|WP_Error
576
	 */
577
	protected function get_taxonomy( $request ) {
578
		if ( '' !== $this->attribute ) {
579
			return $this->attribute;
580
		}
581
582
		if ( $request['id'] ) {
583
			$name = wc_attribute_taxonomy_name_by_id( (int) $request['id'] );
584
585
			$this->attribute = $name;
586
		}
587
588
		return $this->attribute;
589
	}
590
591
	/**
592
	 * Get attribute data.
593
	 *
594
	 * @param int $id Attribute ID.
595
	 * @return stdClass|WP_Error
596
	 */
597
	protected function get_attribute( $id ) {
598
		global $wpdb;
599
600
		$attribute = $wpdb->get_row( $wpdb->prepare( "
601
			SELECT *
602
			FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
603
			WHERE attribute_id = %d
604
		 ", $id ) );
605
606
		if ( is_wp_error( $attribute ) || is_null( $attribute ) ) {
607
			return new WP_Error( 'woocommerce_rest_attribute_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
608
		}
609
610
		return $attribute;
611
	}
612
613
	/**
614
	 * Validate attribute slug.
615
	 *
616
	 * @param string $slug
617
	 * @param bool $new_data
618
	 * @return bool|WP_Error
619
	 */
620
	protected function validate_attribute_slug( $slug, $new_data = true ) {
621
		if ( strlen( $slug ) >= 28 ) {
622
			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 ) );
623
		} else if ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
624
			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 ) );
625
		} else if ( $new_data && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) {
626
			return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_already_exists', sprintf( __( 'Slug "%s" is already in use.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
627
		}
628
629
		return true;
630
	}
631
}
632