Completed
Push — master ( f47a1d...37f03f )
by Claudio
28:06
created

WC_REST_Order_Notes_Controller   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 390
Duplicated Lines 28.72 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 4
Bugs 2 Features 0
Metric Value
wmc 40
lcom 1
cbo 2
dl 112
loc 390
rs 8.2608
c 4
b 2
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
B register_routes() 44 44 1
A get_items_permissions_check() 7 7 2
A create_item_permissions_check() 7 7 2
A get_item_permissions_check() 9 9 3
A delete_item_permissions_check() 9 9 3
B get_items() 0 29 4
B create_item() 0 40 5
B get_item() 19 19 6
D delete_item() 0 41 9
B prepare_item_for_response() 0 26 2
A prepare_links() 17 17 1
B get_item_schema() 0 34 1
A get_collection_params() 0 5 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_Order_Notes_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_Order_Notes_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 Order Notes controller
4
 *
5
 * Handles requests to the /orders/<order_id>/notes 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 Order Notes controller class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WP_REST_Controller
22
 */
23
class WC_REST_Order_Notes_Controller extends WP_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 = 'orders/(?P<order_id>[\d]+)/notes';
38
39
	/**
40
	 * Register the routes for order notes.
41
	 */
42 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...
43
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
44
			array(
45
				'methods'             => WP_REST_Server::READABLE,
46
				'callback'            => array( $this, 'get_items' ),
47
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
48
				'args'                => $this->get_collection_params(),
49
			),
50
			array(
51
				'methods'             => WP_REST_Server::CREATABLE,
52
				'callback'            => array( $this, 'create_item' ),
53
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
54
				'args'                => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
55
					'note' => array(
56
						'required' => true,
57
					),
58
				) ),
59
			),
60
			'schema' => array( $this, 'get_public_item_schema' ),
61
		) );
62
63
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
64
			array(
65
				'methods'             => WP_REST_Server::READABLE,
66
				'callback'            => array( $this, 'get_item' ),
67
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
68
				'args'                => array(
69
					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
70
				),
71
			),
72
			array(
73
				'methods'             => WP_REST_Server::DELETABLE,
74
				'callback'            => array( $this, 'delete_item' ),
75
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
76
				'args'                => array(
77
					'force' => array(
78
						'default'     => false,
79
						'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
80
					),
81
				),
82
			),
83
			'schema' => array( $this, 'get_public_item_schema' ),
84
		) );
85
	}
86
87
	/**
88
	 * Check whether a given request has permission to read order notes.
89
	 *
90
	 * @param  WP_REST_Request $request Full details about the request.
91
	 * @return WP_Error|boolean
92
	 */
93 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...
94
		if ( ! wc_rest_check_post_permissions( 'shop_order', 'read' ) ) {
95
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
96
		}
97
98
		return true;
99
	}
100
101
	/**
102
	 * Check if a given request has access create order notes.
103
	 *
104
	 * @param  WP_REST_Request $request Full details about the request.
105
	 * @return boolean
106
	 */
107 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...
108
		if ( ! wc_rest_check_post_permissions( 'shop_order', 'create' ) ) {
109
			return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
110
		}
111
112
		return true;
113
	}
114
115
	/**
116
	 * Check if a given request has access to read a order note.
117
	 *
118
	 * @param  WP_REST_Request $request Full details about the request.
119
	 * @return WP_Error|boolean
120
	 */
121 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...
122
		$post = get_post( (int) $request['order_id'] );
123
124
		if ( $post && ! wc_rest_check_post_permissions( 'shop_order', 'read', $post->ID ) ) {
125
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
126
		}
127
128
		return true;
129
	}
130
131
	/**
132
	 * Check if a given request has access delete a order note.
133
	 *
134
	 * @param  WP_REST_Request $request Full details about the request.
135
	 * @return boolean
136
	 */
137 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...
138
		$post = get_post( (int) $request['order_id'] );
139
140
		if ( $post && ! wc_rest_check_post_permissions( 'shop_order', 'delete', $post->ID ) ) {
141
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
142
		}
143
144
		return true;
145
	}
146
147
	/**
148
	 * Get order notes from an order.
149
	 *
150
	 * @param WP_REST_Request $request
151
	 * @return array
152
	 */
153
	public function get_items( $request ) {
154
		$id    = (int) $request['id'];
0 ignored issues
show
Unused Code introduced by
$id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
155
		$order = get_post( (int) $request['order_id'] );
156
157
		if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
158
			return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
159
		}
160
161
		$args = array(
162
			'post_id' => $order->ID,
163
			'approve' => 'approve',
164
			'type'    => 'order_note',
165
		);
166
167
		remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
168
169
		$notes = get_comments( $args );
170
171
		add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
172
173
		$data = array();
174
		foreach ( $notes as $note ) {
175
			$order_note = $this->prepare_item_for_response( $note, $request );
176
			$order_note = $this->prepare_response_for_collection( $order_note );
177
			$data[]     = $order_note;
178
		}
179
180
		return rest_ensure_response( $data );
181
	}
182
183
	/**
184
	 * Create a single order note.
185
	 *
186
	 * @param WP_REST_Request $request Full details about the request.
187
	 * @return WP_Error|WP_REST_Response
188
	 */
189
	public function create_item( $request ) {
190
		if ( ! empty( $request['id'] ) ) {
191
			return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
192
		}
193
194
		$order = get_post( (int) $request['order_id'] );
195
196
		if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
197
			return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
198
		}
199
200
		$order = wc_get_order( $order );
201
202
		// Create the note.
203
		$note_id = $order->add_order_note( $request['note'], $request['customer_note'] );
204
205
		if ( ! $note_id ) {
206
			return new WP_Error( 'woocommerce_api_cannot_create_order_note', __( 'Cannot create order note, please try again.', 'woocommerce' ), array( 'status' => 500 ) );
207
		}
208
209
		$note = get_comment( $note_id );
210
		$this->update_additional_fields_for_object( $note, $request );
211
212
		/**
213
		 * Fires after a order note is created or updated via the REST API.
214
		 *
215
		 * @param WP_Comment      $note      New order note object.
216
		 * @param WP_REST_Request $request   Request object.
217
		 * @param boolean         $creating  True when creating item, false when updating.
218
		 */
219
		do_action( 'woocommerce_rest_insert_order_note', $note, $request, true );
220
221
		$request->set_param( 'context', 'edit' );
222
		$response = $this->prepare_item_for_response( $note, $request );
223
		$response = rest_ensure_response( $response );
224
		$response->set_status( 201 );
225
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, str_replace( '(?P<order_id>[\d]+)', $order->id, $this->rest_base ), $note_id ) ) );
226
227
		return $response;
228
	}
229
230
	/**
231
	 * Get a single order note.
232
	 *
233
	 * @param WP_REST_Request $request Full details about the request.
234
	 * @return WP_Error|WP_REST_Response
235
	 */
236 View Code Duplication
	public function get_item( $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...
237
		$id    = (int) $request['id'];
238
		$order = get_post( (int) $request['order_id'] );
239
240
		if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
241
			return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
242
		}
243
244
		$note = get_comment( $id );
245
246
		if ( empty( $id ) || empty( $note ) || intval( $note->comment_post_ID ) !== intval( $order->ID ) ) {
247
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
248
		}
249
250
		$order_note = $this->prepare_item_for_response( $note, $request );
251
		$response   = rest_ensure_response( $order_note );
252
253
		return $response;
254
	}
255
256
	/**
257
	 * Delete a single order note.
258
	 *
259
	 * @param WP_REST_Request $request Full details about the request.
260
	 * @return WP_REST_Response|WP_Error
261
	 */
262
	public function delete_item( $request ) {
263
		$id    = (int) $request['id'];
264
		$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
265
266
		// We don't support trashing for this type, error out.
267
		if ( ! $force ) {
268
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Webhooks do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
269
		}
270
271
		$order = get_post( (int) $request['order_id'] );
272
273
		if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
274
			return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
275
		}
276
277
		$note = get_comment( $id );
278
279
		if ( empty( $id ) || empty( $note ) || intval( $note->comment_post_ID ) !== intval( $order->ID ) ) {
280
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
281
		}
282
283
		$request->set_param( 'context', 'edit' );
284
		$response = $this->prepare_item_for_response( $note, $request );
285
286
		$result = wp_delete_comment( $note->comment_ID, true );;
287
288
		if ( ! $result ) {
289
			return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), 'order_note' ), array( 'status' => 500 ) );
290
		}
291
292
		/**
293
		 * Fires after a order note is deleted or trashed via the REST API.
294
		 *
295
		 * @param WP_Comment       $note     The deleted or trashed order note.
296
		 * @param WP_REST_Response $response The response data.
297
		 * @param WP_REST_Request  $request  The request sent to the API.
298
		 */
299
		do_action( 'woocommerce_rest_delete_order_note', $note, $response, $request );
300
301
		return $response;
302
	}
303
304
	/**
305
	 * Prepare a single order note output for response.
306
	 *
307
	 * @param WP_Comment $note Order note object.
308
	 * @param WP_REST_Request $request Request object.
309
	 * @return WP_REST_Response $response Response data.
310
	 */
311
	public function prepare_item_for_response( $note, $request ) {
312
		$data = array(
313
			'id'            => $note->comment_ID,
314
			'date_created'  => wc_rest_prepare_date_response( $note->comment_date_gmt ),
315
			'note'          => $note->comment_content,
316
			'customer_note' => (bool) get_comment_meta( $note->comment_ID, 'is_customer_note', true ),
317
		);
318
319
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
320
		$data    = $this->add_additional_fields_to_object( $data, $request );
321
		$data    = $this->filter_response_by_context( $data, $context );
322
323
		// Wrap the data in a response object.
324
		$response = rest_ensure_response( $data );
325
326
		$response->add_links( $this->prepare_links( $note ) );
327
328
		/**
329
		 * Filter order note object returned from the REST API.
330
		 *
331
		 * @param WP_REST_Response $response The response object.
332
		 * @param WP_Comment       $note     Order note object used to create response.
333
		 * @param WP_REST_Request  $request  Request object.
334
		 */
335
		return apply_filters( 'woocommerce_rest_prepare_order_note', $response, $note, $request );
336
	}
337
338
	/**
339
	 * Prepare links for the request.
340
	 *
341
	 * @param WP_Comment $note Delivery order_note object.
342
	 * @return array Links for the given order note.
343
	 */
344 View Code Duplication
	protected function prepare_links( $note ) {
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...
345
		$order_id = (int) $note->comment_post_ID;
346
		$base     = str_replace( '(?P<order_id>[\d]+)', $order_id, $this->rest_base );
347
		$links    = array(
348
			'self' => array(
349
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $note->comment_ID ) ),
350
			),
351
			'collection' => array(
352
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
353
			),
354
			'up' => array(
355
				'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order_id ) ),
356
			),
357
		);
358
359
		return $links;
360
	}
361
362
	/**
363
	 * Get the Order Notes schema, conforming to JSON Schema.
364
	 *
365
	 * @return array
366
	 */
367
	public function get_item_schema() {
368
		$schema = array(
369
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
370
			'title'      => 'tax',
371
			'type'       => 'order_note',
372
			'properties' => array(
373
				'id' => array(
374
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
375
					'type'        => 'integer',
376
					'context'     => array( 'view', 'edit' ),
377
					'readonly'    => true,
378
				),
379
				'date_created' => array(
380
					'description' => __( "The date the order note was created, in the site's timezone.", 'woocommerce' ),
381
					'type'        => 'date-time',
382
					'context'     => array( 'view', 'edit' ),
383
					'readonly'    => true,
384
				),
385
				'note' => array(
386
					'description' => __( 'Order note.', 'woocommerce' ),
387
					'type'        => 'string',
388
					'context'     => array( 'view', 'edit' ),
389
				),
390
				'customer_note' => array(
391
					'description' => __( 'Shows/define if the note is only for reference or for the customer (the user will be notified).', 'woocommerce' ),
392
					'type'        => 'boolean',
393
					'default'     => false,
394
					'context'     => array( 'view', 'edit' ),
395
				),
396
			),
397
		);
398
399
		return $this->add_additional_fields_schema( $schema );
400
	}
401
402
	/**
403
	 * Get the query params for collections.
404
	 *
405
	 * @return array
406
	 */
407
	public function get_collection_params() {
408
		return array(
409
			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
410
		);
411
	}
412
}
413