Completed
Pull Request — master (#11781)
by
unknown
12:13
created

WC_REST_Shipping_Zone_Methods_Controller   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 413
Duplicated Lines 20.58 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 2
Bugs 1 Features 2
Metric Value
c 2
b 1
f 2
dl 85
loc 413
rs 8.3396
wmc 44
lcom 1
cbo 1

11 Methods

Rating   Name   Duplication   Size   Complexity  
B register_routes() 0 42 1
B get_item() 3 26 5
A get_items() 17 17 3
B create_item() 3 28 5
B delete_item() 3 47 6
B update_item() 3 28 5
C update_fields() 0 31 8
B prepare_item_for_response() 0 25 2
B get_settings() 0 21 7
A prepare_links() 0 16 1
A get_item_schema() 56 56 1

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_Shipping_Zone_Methods_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_Shipping_Zone_Methods_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * REST API Shipping Zone Methods controller
4
 *
5
 * Handles requests to the /shipping/zones/<id>/methods endpoint.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.7.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Shipping Zone Methods class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WC_REST_Shipping_Zones_Controller_Base
22
 */
23
class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zones_Controller_Base {
24
25
	/**
26
	 * Register the routes for Shipping Zone Methods.
27
	 */
28
	public function register_routes() {
29
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<zone_id>[\d-]+)/methods', array(
30
			array(
31
				'methods'             => WP_REST_Server::READABLE,
32
				'callback'            => array( $this, 'get_items' ),
33
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
34
			),
35
			array(
36
				'methods'             => WP_REST_Server::CREATABLE,
37
				'callback'            => array( $this, 'create_item' ),
38
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
39
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
40
			),
41
			'schema' => array( $this, 'get_public_item_schema' ),
42
		) );
43
44
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<zone_id>[\d-]+)/methods/(?P<instance_id>[\d-]+)', array(
45
			array(
46
				'methods'             => WP_REST_Server::READABLE,
47
				'callback'            => array( $this, 'get_item' ),
48
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
49
			),
50
			array(
51
				'methods'             => WP_REST_Server::EDITABLE,
52
				'callback'            => array( $this, 'update_item' ),
53
				'permission_callback' => array( $this, 'update_items_permissions_check' ),
54
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
55
			),
56
			array(
57
				'methods'             => WP_REST_Server::DELETABLE,
58
				'callback'            => array( $this, 'delete_item' ),
59
				'permission_callback' => array( $this, 'update_items_permissions_check' ),
60
				'args'                => array(
61
					'force' => array(
62
						'default'     => false,
63
						'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
64
					),
65
				),
66
			),
67
			'schema' => array( $this, 'get_public_item_schema' ),
68
		) );
69
	}
70
71
	/**
72
	 * Get a single Shipping Zone Method.
73
	 *
74
	 * @param WP_REST_Request $request
75
	 * @return WP_REST_Response|WP_Error
76
	 */
77
	public function get_item( $request ) {
78
		$zone = $this->get_zone( $request['zone_id'] );
79
80
		if ( is_wp_error( $zone ) ) {
81
			return $zone;
82
		}
83
84
		$instance_id = (int) $request['instance_id'];
85
		$methods     = $zone->get_shipping_methods();
86
		$method      = false;
87
88
		foreach ( $methods as $method_obj ) {
89
			if ( $instance_id === $method_obj->instance_id ) {
90
				$method = $method_obj;
91
				break;
92
			}
93
		}
94
95 View Code Duplication
		if ( false === $method ) {
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...
96
			return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
97
		}
98
99
		$data = $this->prepare_item_for_response( $method, $request );
100
101
		return rest_ensure_response( $data );
102
	}
103
104
	/**
105
	 * Get all Shipping Zone Methods.
106
	 *
107
	 * @param WP_REST_Request $request
108
	 * @return WP_REST_Response|WP_Error
109
	 */
110 View Code Duplication
	public function get_items( $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...
111
		$zone = $this->get_zone( $request['zone_id'] );
112
113
		if ( is_wp_error( $zone ) ) {
114
			return $zone;
115
		}
116
117
		$methods = $zone->get_shipping_methods();
118
		$data    = array();
119
120
		foreach ( $methods as $method_obj ) {
121
			$method = $this->prepare_item_for_response( $method_obj, $request );
122
			$data[] = $method;
123
		}
124
125
		return rest_ensure_response( $data );
126
	}
127
128
	/**
129
	 * Create a new shipping zone method instance.
130
	 *
131
	 * @param WP_REST_Request $request Full details about the request.
132
	 * @return WP_REST_Request|WP_Error
133
	 */
134
	public function create_item( $request ) {
135
		global $wpdb;
136
137
		$method_id = $request['method_id'];
138
		$zone      = $this->get_zone( $request['zone_id'] );
139
		if ( is_wp_error( $zone ) ) {
140
			return $zone;
141
		}
142
143
		$instance_id = $zone->add_shipping_method( $method_id ) ;
144
		$methods     = $zone->get_shipping_methods();
145
		$method      = false;
146
		foreach ( $methods as $method_obj ) {
147
			if ( $instance_id === $method_obj->instance_id ) {
148
				$method = $method_obj;
149
				break;
150
			}
151
		}
152
153 View Code Duplication
		if ( false === $method ) {
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...
154
			return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( 'Resource cannot be created.', 'woocommerce' ), array( 'status' => 500 ) );
155
		}
156
157
		$method = $this->update_fields( $instance_id, $method, $request );
158
159
		$data = $this->prepare_item_for_response( $method, $request );
160
		return rest_ensure_response( $data );
161
	}
162
163
	/**
164
	 * Delete a shipping method instance.
165
	 *
166
	 * @param WP_REST_Request $request Full details about the request
167
	 * @return WP_Error|boolean
168
	 */
169
	public function delete_item( $request ) {
170
		global $wpdb;
171
172
		$zone = $this->get_zone( $request['zone_id'] );
173
		if ( is_wp_error( $zone ) ) {
174
			return $zone;
175
		}
176
177
		$instance_id = (int) $request['instance_id'];
178
		$force       = $request['force'];
179
180
		$methods     = $zone->get_shipping_methods();
181
		$method      = false;
182
183
		foreach ( $methods as $method_obj ) {
184
			if ( $instance_id === $method_obj->instance_id ) {
185
				$method = $method_obj;
186
				break;
187
			}
188
		}
189
190 View Code Duplication
		if ( false === $method ) {
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...
191
			return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
192
		}
193
194
		$method = $this->update_fields( $instance_id, $method, $request );
195
		$request->set_param( 'context', 'view' );
196
		$response = $this->prepare_item_for_response( $method, $request );
197
198
		// Actually delete
199
		if ( $force ) {
200
			$zone->delete_shipping_method( $instance_id ) ;
201
		} else {
202
			return new WP_Error( 'rest_trash_not_supported', __( 'Shipping methods do not support trashing.' ), array( 'status' => 501 ) );
203
		}
204
205
		/**
206
		 * Fires after a product review is deleted via the REST API.
207
		 *
208
		 * @param object           $method
209
		 * @param WP_REST_Response $response        The response data.
210
		 * @param WP_REST_Request  $request         The request sent to the API.
211
		 */
212
		do_action( 'rest_delete_product_review', $method, $response, $request );
213
214
		return $response;
215
	}
216
217
	/**
218
	 * Update A Single Shipping Zone Method.
219
	 *
220
	 * @param WP_REST_Request $request
221
	 * @return WP_REST_Response|WP_Error
222
	 */
223
	public function update_item( $request ) {
224
		global $wpdb;
225
226
		$zone = $this->get_zone( $request['zone_id'] );
227
		if ( is_wp_error( $zone ) ) {
228
			return $zone;
229
		}
230
231
		$instance_id = (int) $request['instance_id'];
232
		$methods     = $zone->get_shipping_methods();
233
		$method      = false;
234
235
		foreach ( $methods as $method_obj ) {
236
			if ( $instance_id === $method_obj->instance_id ) {
237
				$method = $method_obj;
238
				break;
239
			}
240
		}
241
242 View Code Duplication
		if ( false === $method ) {
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...
243
			return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
244
		}
245
246
		$method = $this->update_fields( $instance_id, $method, $request );
247
248
		$data = $this->prepare_item_for_response( $method, $request );
249
		return rest_ensure_response( $data );
250
	}
251
252
	/**
253
	 * Updates settings, order, and enabled status on create.
254
	 *
255
	 * @param $instance_id integer
256
	 * @param $method
257
	 * @param WP_REST_Request $request
258
	 * @return $method
0 ignored issues
show
Documentation introduced by
The doc-type $method could not be parsed: Unknown type name "$method" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
259
	 */
260
	public function update_fields( $instance_id, $method, $request ) {
261
		global $wpdb;
262
263
		// Update settings if present
264
		if ( isset( $request['settings'] ) ) {
265
			$method->init_instance_settings();
266
			$instance_settings = $method->instance_settings;
267
			foreach ( $method->get_instance_form_fields() as $key => $field ) {
268
				if ( isset( $request['settings'][ $key ] ) ) {
269
					$instance_settings[ $key ] = $request['settings'][ $key ];
270
				}
271
			}
272
			update_option( $method->get_instance_option_key(), apply_filters( 'woocommerce_shipping_' . $method->id . '_instance_settings_values', $instance_settings, $method ) );
273
		}
274
275
		// Update order
276
		if ( isset( $request['order'] ) ) {
277
			$wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'method_order' => absint( $request['order'] ) ), array( 'instance_id' => absint( $instance_id ) ) );
278
			$method->method_order = absint( $request['order'] );
279
		}
280
281
		// Update if this method is enabled or not.
282
		if ( isset( $request['enabled'] ) ) {
283
			if ( $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'is_enabled' => $request['enabled'] ), array( 'instance_id' => absint( $instance_id ) ) ) ) {
284
				do_action( 'woocommerce_shipping_zone_method_status_toggled', $instance_id, $method->id, $request['zone_id'], $request['enabled'] );
285
				$method->enabled = ( true === $request['enabled'] ? 'yes' : 'no' );
286
			}
287
		}
288
289
		return $method;
290
	}
291
292
	/**
293
	 * Prepare the Shipping Zone Method for the REST response.
294
	 *
295
	 * @param array $item Shipping Zone Method.
296
	 * @param WP_REST_Request $request Request object.
297
	 * @return WP_REST_Response $response
298
	 */
299
	public function prepare_item_for_response( $item, $request ) {
300
		$method = array(
301
			'instance_id'        => $item->instance_id,
302
			'title'              => $item->instance_settings['title'],
303
			'order'              => $item->method_order,
304
			'enabled'            => ( 'yes' === $item->enabled ),
305
			'method_id'          => $item->id,
306
			'method_title'       => $item->method_title,
307
			'method_description' => $item->method_description,
308
			'settings'           => $this->get_settings( $item ),
309
		);
310
311
		$context = empty( $request['context'] ) ? 'view' : $request['context'];
312
		$data    = $this->add_additional_fields_to_object( $method, $request );
313
		$data    = $this->filter_response_by_context( $data, $context );
314
315
		// Wrap the data in a response object.
316
		$response = rest_ensure_response( $data );
317
318
		$response->add_links( $this->prepare_links( $request['zone_id'], $item->instance_id ) );
319
320
		$response = $this->prepare_response_for_collection( $response );
321
322
		return $response;
323
	}
324
325
	/**
326
	 * Return settings associated with this shipping zone method instance.
327
	 */
328
	public function get_settings( $item ) {
329
		$item->init_instance_settings();
330
		$settings = array();
331
		foreach ( $item->get_instance_form_fields() as $id => $field ) {
332
			$data = array(
333
				'id'          => $id,
334
				'label'       => $field['title'],
335
				'description' => empty( $field['description'] ) ? '' : $field['description'],
336
				'type'        => $field['type'],
337
				'value'       => $item->instance_settings[ $id ],
338
				'default'     => empty( $field['default'] ) ? '' : $field['default'],
339
				'tip'         => empty( $field['description'] ) ? '' : $field['description'],
340
				'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'],
341
			);
342
			if ( ! empty( $field['options'] ) ) {
343
				$data['options'] = $field['options'];
344
			}
345
			$settings[ $id ] = $data;
346
		}
347
		return $settings;
348
	}
349
350
	/**
351
	 * Prepare links for the request.
352
	 *
353
	 * @param int $zone_id Given Shipping Zone ID.
354
	 * @param int $instance_id Given Shipping Zone Method Instance ID.
355
	 * @return array Links for the given Shipping Zone Method.
356
	 */
357
	protected function prepare_links( $zone_id, $instance_id ) {
358
		$base  = '/' . $this->namespace . '/' . $this->rest_base . '/' . $zone_id;
359
		$links = array(
360
			'self' => array(
361
				'href' => rest_url( $base . '/methods/' . $instance_id ),
362
			),
363
			'collection' => array(
364
				'href' => rest_url( $base . '/methods' ),
365
			),
366
			'describes'  => array(
367
				'href' => rest_url( $base ),
368
			),
369
		);
370
371
		return $links;
372
	}
373
374
	/**
375
	 * Get the Shipping Zone Methods schema, conforming to JSON Schema
376
	 *
377
	 * @return array
378
	 */
379 View Code Duplication
	public function get_item_schema() {
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...
380
		$schema = array(
381
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
382
			'title'      => 'shipping_zone_method',
383
			'type'       => 'object',
384
			'properties' => array(
385
				'instance_id' => array(
386
					'description' => __( 'Shipping method instance ID.', 'woocommerce' ),
387
					'type'        => 'integer',
388
					'context'     => array( 'view' ),
389
				),
390
				'title' => array(
391
					'description' => __( 'Shipping method customer facing title.', 'woocommerce' ),
392
					'type'        => 'string',
393
					'context'     => array( 'view' ),
394
				),
395
				'order' => array(
396
					'description' => __( 'Shipping method sort order.', 'woocommerce' ),
397
					'type'        => 'integer',
398
					'context'     => array( 'view', 'edit' ),
399
					'required'    => false,
400
					'arg_options' => array(
401
						'sanitize_callback' => 'absint',
402
					),
403
				),
404
				'enabled' => array(
405
					'description' => __( 'Shipping method enabled status.', 'woocommerce' ),
406
					'type'        => 'boolean',
407
					'context'     => array( 'view', 'edit' ),
408
					'required'    => false,
409
				),
410
				'method_id' => array(
411
					'description' => __( 'Shipping method ID. Write on create only.', 'woocommerce' ),
412
					'type'        => 'string',
413
					'context'     => array( 'view', 'edit.' ),
414
				),
415
				'method_title' => array(
416
					'description' => __( 'Shipping method title.', 'woocommerce' ),
417
					'type'        => 'string',
418
					'context'     => array( 'view' ),
419
				),
420
				'method_description' => array(
421
					'description' => __( 'Shipping method description.', 'woocommerce' ),
422
					'type'        => 'string',
423
					'context'     => array( 'view' ),
424
				),
425
				'settings' => array(
426
					'description' => __( 'Shipping method settings.', 'woocommerce' ),
427
					'type'        => 'array',
428
					'context'     => array( 'view', 'edit' ),
429
				),
430
			),
431
		);
432
433
		return $this->add_additional_fields_schema( $schema );
434
	}
435
}
436