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

WC_API_Orders::query_orders()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 18

Duplication

Lines 24
Ratio 100 %
Metric Value
dl 24
loc 24
rs 8.9713
nc 4
cc 3
eloc 18
nop 1
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 18 and the first side effect is on line 15.

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
 * 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 View Code Duplication
	public function get_orders( $fields = null, $filter = array(), $status = null, $page = 1 ) {
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...
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 ) );
0 ignored issues
show
Bug introduced by
It seems like $fields defined by parameter $fields on line 71 can also be of type string; however, WC_API_Orders::get_order() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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 ),
0 ignored issues
show
Deprecated Code introduced by
The method WC_Abstract_Order::get_cart_discount() has been deprecated with message: in favour of get_total_discount() since we now only have one discount type.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
133
			'order_discount'            => wc_format_decimal( $order->get_order_discount(), 2 ),
0 ignored issues
show
Deprecated Code introduced by
The method WC_Abstract_Order::get_order_discount() has been deprecated with message: order (after tax) discounts removed in 2.3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
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 View Code Duplication
		foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
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...
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 View Code Duplication
		foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
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...
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 View Code Duplication
	public function get_orders_count( $status = null, $filter = array() ) {
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...
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 View Code Duplication
	private function query_orders( $args ) {
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...
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