Completed
Push — master ( a60b03...41517c )
by Claudio
27:03
created

WC_API_Orders   D

Complexity

Total Complexity 288

Size/Duplication

Total Lines 383
Duplicated Lines 20.1 %

Coupling/Cohesion

Components 1
Dependencies 8
Metric Value
wmc 288
lcom 1
cbo 8
dl 77
loc 383
rs 4.8717

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

1
<?php
2
/**
3
 * WooCommerce API Orders Class
4
 *
5
 * Handles requests to the /orders endpoint
6
 *
7
 * @author      WooThemes
8
 * @category    API
9
 * @package     WooCommerce/API
10
 * @since       2.1
11
 * @version     2.1
12
 */
13
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit; // Exit if accessed directly
16
}
17
18
class WC_API_Orders extends WC_API_Resource {
19
20
	/** @var string $base the route base */
21
	protected $base = '/orders';
22
23
	/**
24
	 * Register the routes for this class
25
	 *
26
	 * GET /orders
27
	 * GET /orders/count
28
	 * GET|PUT /orders/<id>
29
	 * GET /orders/<id>/notes
30
	 *
31
	 * @since 2.1
32
	 * @param array $routes
33
	 * @return array
34
	 */
35
	public function register_routes( $routes ) {
36
37
		# GET /orders
38
		$routes[ $this->base ] = array(
39
			array( array( $this, 'get_orders' ),     WC_API_Server::READABLE ),
40
		);
41
42
		# GET /orders/count
43
		$routes[ $this->base . '/count'] = array(
44
			array( array( $this, 'get_orders_count' ), WC_API_Server::READABLE ),
45
		);
46
47
		# GET|PUT /orders/<id>
48
		$routes[ $this->base . '/(?P<id>\d+)' ] = array(
49
			array( array( $this, 'get_order' ),  WC_API_Server::READABLE ),
50
			array( array( $this, 'edit_order' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
51
		);
52
53
		# GET /orders/<id>/notes
54
		$routes[ $this->base . '/(?P<id>\d+)/notes' ] = array(
55
			array( array( $this, 'get_order_notes' ), WC_API_Server::READABLE ),
56
		);
57
58
		return $routes;
59
	}
60
61
	/**
62
	 * Get all orders
63
	 *
64
	 * @since 2.1
65
	 * @param string $fields
66
	 * @param array $filter
67
	 * @param string $status
68
	 * @param int $page
69
	 * @return array
70
	 */
71
	public function get_orders( $fields = null, $filter = array(), $status = null, $page = 1 ) {
72
73
		if ( ! empty( $status ) )
74
			$filter['status'] = $status;
75
76
		$filter['page'] = $page;
77
78
		$query = $this->query_orders( $filter );
79
80
		$orders = array();
81
82
		foreach( $query->posts as $order_id ) {
83
84
			if ( ! $this->is_readable( $order_id ) )
85
				continue;
86
87
			$orders[] = current( $this->get_order( $order_id, $fields ) );
88
		}
89
90
		$this->server->add_pagination_headers( $query );
91
92
		return array( 'orders' => $orders );
93
	}
94
95
96
	/**
97
	 * Get the order for the given ID
98
	 *
99
	 * @since 2.1
100
	 * @param int $id the order ID
101
	 * @param array $fields
102
	 * @return array
103
	 */
104
	public function get_order( $id, $fields = null ) {
105
106
		// ensure order ID is valid & user has permission to read
107
		$id = $this->validate_request( $id, 'shop_order', 'read' );
108
109
		if ( is_wp_error( $id ) )
110
			return $id;
111
112
		$order = wc_get_order( $id );
113
114
		$order_post = get_post( $id );
115
116
		$order_data = array(
117
			'id'                        => $order->id,
118
			'order_number'              => $order->get_order_number(),
119
			'created_at'                => $this->server->format_datetime( $order_post->post_date_gmt ),
120
			'updated_at'                => $this->server->format_datetime( $order_post->post_modified_gmt ),
121
			'completed_at'              => $this->server->format_datetime( $order->completed_date, true ),
122
			'status'                    => $order->get_status(),
123
			'currency'                  => $order->order_currency,
124
			'total'                     => wc_format_decimal( $order->get_total(), 2 ),
125
			'subtotal'                  => wc_format_decimal( $this->get_order_subtotal( $order ), 2 ),
126
			'total_line_items_quantity' => $order->get_item_count(),
127
			'total_tax'                 => wc_format_decimal( $order->get_total_tax(), 2 ),
128
			'total_shipping'            => wc_format_decimal( $order->get_total_shipping(), 2 ),
129
			'cart_tax'                  => wc_format_decimal( $order->get_cart_tax(), 2 ),
130
			'shipping_tax'              => wc_format_decimal( $order->get_shipping_tax(), 2 ),
131
			'total_discount'            => wc_format_decimal( $order->get_total_discount(), 2 ),
132
			'cart_discount'             => wc_format_decimal( $order->get_cart_discount(), 2 ),
133
			'order_discount'            => wc_format_decimal( $order->get_order_discount(), 2 ),
134
			'shipping_methods'          => $order->get_shipping_method(),
135
			'payment_details' => array(
136
				'method_id'    => $order->payment_method,
137
				'method_title' => $order->payment_method_title,
138
				'paid'         => isset( $order->paid_date ),
139
			),
140
			'billing_address' => array(
141
				'first_name' => $order->billing_first_name,
142
				'last_name'  => $order->billing_last_name,
143
				'company'    => $order->billing_company,
144
				'address_1'  => $order->billing_address_1,
145
				'address_2'  => $order->billing_address_2,
146
				'city'       => $order->billing_city,
147
				'state'      => $order->billing_state,
148
				'postcode'   => $order->billing_postcode,
149
				'country'    => $order->billing_country,
150
				'email'      => $order->billing_email,
151
				'phone'      => $order->billing_phone,
152
			),
153
			'shipping_address' => array(
154
				'first_name' => $order->shipping_first_name,
155
				'last_name'  => $order->shipping_last_name,
156
				'company'    => $order->shipping_company,
157
				'address_1'  => $order->shipping_address_1,
158
				'address_2'  => $order->shipping_address_2,
159
				'city'       => $order->shipping_city,
160
				'state'      => $order->shipping_state,
161
				'postcode'   => $order->shipping_postcode,
162
				'country'    => $order->shipping_country,
163
			),
164
			'note'                      => $order->customer_note,
165
			'customer_ip'               => $order->customer_ip_address,
166
			'customer_user_agent'       => $order->customer_user_agent,
167
			'customer_id'               => $order->customer_user,
168
			'view_order_url'            => $order->get_view_order_url(),
169
			'line_items'                => array(),
170
			'shipping_lines'            => array(),
171
			'tax_lines'                 => array(),
172
			'fee_lines'                 => array(),
173
			'coupon_lines'              => array(),
174
		);
175
176
		// add line items
177
		foreach( $order->get_items() as $item_id => $item ) {
178
179
			$product = $order->get_product_from_item( $item );
180
181
			$order_data['line_items'][] = array(
182
				'id'         => $item_id,
183
				'subtotal'   => wc_format_decimal( $order->get_line_subtotal( $item ), 2 ),
184
				'total'      => wc_format_decimal( $order->get_line_total( $item ), 2 ),
185
				'total_tax'  => wc_format_decimal( $order->get_line_tax( $item ), 2 ),
186
				'price'      => wc_format_decimal( $order->get_item_total( $item ), 2 ),
187
				'quantity'   => (int) $item['qty'],
188
				'tax_class'  => ( ! empty( $item['tax_class'] ) ) ? $item['tax_class'] : null,
189
				'name'       => $item['name'],
190
				'product_id' => ( isset( $product->variation_id ) ) ? $product->variation_id : $product->id,
191
				'sku'        => is_object( $product ) ? $product->get_sku() : null,
192
			);
193
		}
194
195
		// add shipping
196
		foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) {
197
198
			$order_data['shipping_lines'][] = array(
199
				'id'           => $shipping_item_id,
200
				'method_id'    => $shipping_item['method_id'],
201
				'method_title' => $shipping_item['name'],
202
				'total'        => wc_format_decimal( $shipping_item['cost'], 2 ),
203
			);
204
		}
205
206
		// add taxes
207
		foreach ( $order->get_tax_totals() as $tax_code => $tax ) {
208
209
			$order_data['tax_lines'][] = array(
210
				'code'     => $tax_code,
211
				'title'    => $tax->label,
212
				'total'    => wc_format_decimal( $tax->amount, 2 ),
213
				'compound' => (bool) $tax->is_compound,
214
			);
215
		}
216
217
		// add fees
218
		foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
219
220
			$order_data['fee_lines'][] = array(
221
				'id'        => $fee_item_id,
222
				'title'     => $fee_item['name'],
223
				'tax_class' => ( ! empty( $fee_item['tax_class'] ) ) ? $fee_item['tax_class'] : null,
224
				'total'     => wc_format_decimal( $order->get_line_total( $fee_item ), 2 ),
225
				'total_tax' => wc_format_decimal( $order->get_line_tax( $fee_item ), 2 ),
226
			);
227
		}
228
229
		// add coupons
230
		foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
231
232
			$order_data['coupon_lines'][] = array(
233
				'id'     => $coupon_item_id,
234
				'code'   => $coupon_item['name'],
235
				'amount' => wc_format_decimal( $coupon_item['discount_amount'], 2 ),
236
			);
237
		}
238
239
		return array( 'order' => apply_filters( 'woocommerce_api_order_response', $order_data, $order, $fields, $this->server ) );
240
	}
241
242
	/**
243
	 * Get the total number of orders
244
	 *
245
	 * @since 2.1
246
	 * @param string $status
247
	 * @param array $filter
248
	 * @return array
249
	 */
250
	public function get_orders_count( $status = null, $filter = array() ) {
251
252
		if ( ! empty( $status ) )
253
			$filter['status'] = $status;
254
255
		$query = $this->query_orders( $filter );
256
257
		if ( ! current_user_can( 'read_private_shop_orders' ) )
258
			return new WP_Error( 'woocommerce_api_user_cannot_read_orders_count', __( 'You do not have permission to read the orders count', 'woocommerce' ), array( 'status' => 401 ) );
259
260
		return array( 'count' => (int) $query->found_posts );
261
	}
262
263
	/**
264
	 * Edit an order
265
	 *
266
	 * API v1 only allows updating the status of an order
267
	 *
268
	 * @since 2.1
269
	 * @param int $id the order ID
270
	 * @param array $data
271
	 * @return array
272
	 */
273
	public function edit_order( $id, $data ) {
274
275
		$id = $this->validate_request( $id, 'shop_order', 'edit' );
276
277
		if ( is_wp_error( $id ) )
278
			return $id;
279
280
		$order = wc_get_order( $id );
281
282
		if ( ! empty( $data['status'] ) ) {
283
284
			$order->update_status( $data['status'], isset( $data['note'] ) ? $data['note'] : '' );
285
		}
286
287
		return $this->get_order( $id );
288
	}
289
290
	/**
291
	 * Delete an order
292
	 *
293
	 * @TODO enable along with POST in 2.2
294
	 * @param int $id the order ID
295
	 * @param bool $force true to permanently delete order, false to move to trash
296
	 * @return array
297
	 */
298
	public function delete_order( $id, $force = false ) {
299
300
		$id = $this->validate_request( $id, 'shop_order', 'delete' );
301
302
		return $this->delete( $id, 'order',  ( 'true' === $force ) );
303
	}
304
305
	/**
306
	 * Get the admin order notes for an order
307
	 *
308
	 * @since 2.1
309
	 * @param int $id the order ID
310
	 * @param string $fields fields to include in response
311
	 * @return array
312
	 */
313
	public function get_order_notes( $id, $fields = null ) {
314
315
		// ensure ID is valid order ID
316
		$id = $this->validate_request( $id, 'shop_order', 'read' );
317
318
		if ( is_wp_error( $id ) )
319
			return $id;
320
321
		$args = array(
322
			'post_id' => $id,
323
			'approve' => 'approve',
324
			'type'    => 'order_note'
325
		);
326
327
		remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
328
329
		$notes = get_comments( $args );
330
331
		add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
332
333
		$order_notes = array();
334
335
		foreach ( $notes as $note ) {
336
337
			$order_notes[] = array(
338
				'id'            => $note->comment_ID,
339
				'created_at'    => $this->server->format_datetime( $note->comment_date_gmt ),
340
				'note'          => $note->comment_content,
341
				'customer_note' => get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ? true : false,
342
			);
343
		}
344
345
		return array( 'order_notes' => apply_filters( 'woocommerce_api_order_notes_response', $order_notes, $id, $fields, $notes, $this->server ) );
346
	}
347
348
	/**
349
	 * Helper method to get order post objects
350
	 *
351
	 * @since 2.1
352
	 * @param array $args request arguments for filtering query
353
	 * @return WP_Query
354
	 */
355
	private function query_orders( $args ) {
356
357
		// set base query arguments
358
		$query_args = array(
359
			'fields'      => 'ids',
360
			'post_type'   => 'shop_order',
361
			'post_status' => array_keys( wc_get_order_statuses() )
362
		);
363
364
		// add status argument
365
		if ( ! empty( $args['status'] ) ) {
366
367
			$statuses                  = 'wc-' . str_replace( ',', ',wc-', $args['status'] );
368
			$statuses                  = explode( ',', $statuses );
369
			$query_args['post_status'] = $statuses;
370
371
			unset( $args['status'] );
372
373
		}
374
375
		$query_args = $this->merge_query_args( $query_args, $args );
376
377
		return new WP_Query( $query_args );
378
	}
379
380
	/**
381
	 * Helper method to get the order subtotal
382
	 *
383
	 * @since 2.1
384
	 * @param WC_Order $order
385
	 * @return float
386
	 */
387
	private function get_order_subtotal( $order ) {
388
389
		$subtotal = 0;
390
391
		// subtotal
392
		foreach ( $order->get_items() as $item ) {
393
394
			$subtotal += ( isset( $item['line_subtotal'] ) ) ? $item['line_subtotal'] : 0;
395
		}
396
397
		return $subtotal;
398
	}
399
400
}
401