Passed
Push — master ( 6176aa...f7c939 )
by Mike
03:08
created

BatchTrait::get_default_params()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Batch trait.
4
 *
5
 * @package WooCommerce/RestApi
6
 */
7
8
namespace WooCommerce\RestApi\Controllers\Version4\Utilities;
9
10
/**
11
 * BatchTrait.
12
 */
13
trait BatchTrait {
14
15
	/**
16
	 * Bulk create, update and delete items.
17
	 *
18
	 * @param \WP_REST_Request $request Full details about the request.
19
	 * @return array Of \WP_Error or \WP_REST_Response.
20
	 */
21
	public function batch_items( $request ) {
22
		$items = $request->get_params();
23
		$limit = $this->check_batch_limit( $items );
24
		if ( is_wp_error( $limit ) ) {
25
			return $limit;
26
		}
27
28
		$batches  = [ 'create', 'update', 'delete' ];
29
		$response = [];
30
31
		foreach ( $batches as $batch ) {
32
			$response[ $batch ] = $this->{"batch_$batch"}( $this->get_batch_of_items_from_request( $request, $batch ) );
33
		}
34
35
		return array_filter( $response );
36
	}
37
38
	/**
39
	 * Get batch of items from requst.
40
	 *
41
	 * @param \WP_REST_Request $request Full details about the request.
42
	 * @param string           $batch_type Batch type; one of create, update, delete.
43
	 * @return array
44
	 */
45
	protected function get_batch_of_items_from_request( $request, $batch_type ) {
46
		$params = $request->get_params();
47
48
		if ( ! isset( $params[ $batch_type ] ) ) {
49
			return array();
50
		}
51
52
		return array_filter( $params[ $batch_type ] );
53
	}
54
55
	/**
56
	 * Check if a given request has access batch create, update and delete items.
57
	 *
58
	 * @param  \WP_REST_Request $request Full details about the request.
59
	 * @return boolean|\WP_Error
60
	 */
61
	public function batch_items_permissions_check( $request ) {
62
		return update_item_permissions_check( $request );
63
	}
64
65
	/**
66
	 * Register route for batch requests.
67
	 */
68
	protected function register_batch_route() {
69
		register_rest_route(
70
			$this->namespace,
71
			'/' . $this->rest_base . '/batch',
72
			array(
73
				array(
74
					'methods'             => \WP_REST_Server::EDITABLE,
75
					'callback'            => array( $this, 'batch_items' ),
76
					'permission_callback' => array( $this, 'batch_items_permissions_check' ),
77
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
0 ignored issues
show
Bug introduced by
It seems like get_endpoint_args_for_item_schema() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

77
					'args'                => $this->/** @scrutinizer ignore-call */ get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
Loading history...
78
				),
79
				'schema' => array( $this, 'get_public_batch_schema' ),
80
			),
81
			true
82
		);
83
	}
84
85
	/**
86
	 * Get the batch schema, conforming to JSON Schema.
87
	 *
88
	 * @return array
89
	 */
90
	protected function get_public_batch_schema() {
91
		$schema = array(
92
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
93
			'title'      => 'batch',
94
			'type'       => 'object',
95
			'properties' => array(
96
				'create' => array(
97
					'description' => __( 'List of created resources.', 'woocommerce' ),
98
					'type'        => 'array',
99
					'context'     => array( 'view', 'edit' ),
100
					'items'       => array(
101
						'type' => 'object',
102
					),
103
				),
104
				'update' => array(
105
					'description' => __( 'List of updated resources.', 'woocommerce' ),
106
					'type'        => 'array',
107
					'context'     => array( 'view', 'edit' ),
108
					'items'       => array(
109
						'type' => 'object',
110
					),
111
				),
112
				'delete' => array(
113
					'description' => __( 'List of delete resources.', 'woocommerce' ),
114
					'type'        => 'array',
115
					'context'     => array( 'view', 'edit' ),
116
					'items'       => array(
117
						'type' => 'integer',
118
					),
119
				),
120
			),
121
		);
122
123
		return $schema;
124
	}
125
126
	/**
127
	 * Get normalized rest base.
128
	 *
129
	 * @return string
130
	 */
131
	protected function get_normalized_rest_base() {
132
		return preg_replace( '/\(.*\)\//i', '', $this->rest_base );
133
	}
134
135
	/**
136
	 * Check batch limit.
137
	 *
138
	 * @param array $items Request items.
139
	 * @return bool|\WP_Error
140
	 */
141
	protected function check_batch_limit( $items ) {
142
		$limit   = apply_filters( 'woocommerce_rest_batch_items_limit', 100, $this->get_normalized_rest_base() );
143
		$total   = 0;
144
		$batches = [ 'create', 'update', 'delete' ];
145
146
		foreach ( $batches as $batch ) {
147
			if ( ! isset( $items[ $batch ] ) ) {
148
				continue;
149
			}
150
			$total = $total + count( $items[ $batch ] );
151
		}
152
153
		if ( $total > $limit ) {
154
			/* translators: %s: items limit */
155
			return new \WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) );
156
		}
157
158
		return true;
159
	}
160
161
	/**
162
	 * Get default params from schema.
163
	 *
164
	 * @return array
165
	 */
166
	protected function get_default_params() {
167
		$defaults = [];
168
		$schema   = $this->get_public_item_schema();
0 ignored issues
show
Bug introduced by
The method get_public_item_schema() does not exist on WooCommerce\RestApi\Cont...n4\Utilities\BatchTrait. Did you maybe mean get_public_batch_schema()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

168
		/** @scrutinizer ignore-call */ 
169
  $schema   = $this->get_public_item_schema();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
169
		foreach ( $schema['properties'] as $arg => $options ) {
170
			if ( isset( $options['default'] ) ) {
171
				$defaults[ $arg ] = $options['default'];
172
			}
173
		}
174
		return $defaults;
175
	}
176
177
	/**
178
	 * Batch create items.
179
	 *
180
	 * @param array $items Array of items.
181
	 * @return array Response data.
182
	 */
183
	protected function batch_create( $items ) {
184
		$batch_response = [];
185
186
		foreach ( $items as $item ) {
187
			$request = new \WP_REST_Request( 'POST' );
188
			$request->set_default_params( $this->get_default_params() );
189
			$request->set_body_params( $item );
190
191
			$response         = $this->create_item( $request );
0 ignored issues
show
Bug introduced by
It seems like create_item() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
			/** @scrutinizer ignore-call */ 
192
   $response         = $this->create_item( $request );
Loading history...
192
			$batch_response[] = $this->format_response( 0, $response );
193
		}
194
195
		return $batch_response;
196
	}
197
198
	/**
199
	 * Batch update items.
200
	 *
201
	 * @param array $items Array of items.
202
	 * @return array Response data.
203
	 */
204
	protected function batch_update( $items ) {
205
		$batch_response = [];
206
207
		foreach ( $items as $item ) {
208
			$request = new \WP_REST_Request( 'PUT' );
209
			$request->set_body_params( $item );
210
211
			$response         = $this->update_item( $request );
0 ignored issues
show
Bug introduced by
It seems like update_item() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

211
			/** @scrutinizer ignore-call */ 
212
   $response         = $this->update_item( $request );
Loading history...
212
			$batch_response[] = $this->format_response( $item['id'], $response );
213
		}
214
215
		return $batch_response;
216
	}
217
218
	/**
219
	 * Batch delete items.
220
	 *
221
	 * @param array $items Array of item ids.
222
	 * @return array Response data.
223
	 */
224
	protected function batch_delete( $items ) {
225
		$batch_response = [];
226
		$items          = wp_parse_id_list( $items );
227
228
		foreach ( $items as $id ) {
229
			$request = new \WP_REST_Request( 'DELETE' );
230
			$request->set_query_params(
231
				[
232
					'id'    => $id,
233
					'force' => true,
234
				]
235
			);
236
			$response         = $this->delete_item( $request );
0 ignored issues
show
Bug introduced by
It seems like delete_item() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
			/** @scrutinizer ignore-call */ 
237
   $response         = $this->delete_item( $request );
Loading history...
237
			$batch_response[] = $this->format_response( $id, $response );
238
		}
239
240
		return $batch_response;
241
	}
242
243
	/**
244
	 * Format response data.
245
	 *
246
	 * @param int                         $id ID of item being updated.
247
	 * @param \WP_REST_Response|\WP_Error $response Response object.
248
	 * @return array
249
	 */
250
	protected function format_response( $id, $response ) {
251
		/**
252
		 * REST Server
253
		 *
254
		 * @var \WP_REST_Server $wp_rest_server
255
		 */
256
		global $wp_rest_server;
257
258
		if ( ! is_wp_error( $response ) ) {
259
			return $wp_rest_server->response_to_data( $response, '' );
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type boolean expected by parameter $embed of WP_REST_Server::response_to_data(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
			return $wp_rest_server->response_to_data( $response, /** @scrutinizer ignore-type */ '' );
Loading history...
Bug introduced by
It seems like $response can also be of type WP_Error; however, parameter $response of WP_REST_Server::response_to_data() does only seem to accept WP_REST_Response, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
			return $wp_rest_server->response_to_data( /** @scrutinizer ignore-type */ $response, '' );
Loading history...
260
		} else {
261
			return array(
262
				'id'    => $id,
263
				'error' => $this->format_error_response( $response ),
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type WP_REST_Response; however, parameter $error of WooCommerce\RestApi\Cont...format_error_response() does only seem to accept WP_Error, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

263
				'error' => $this->format_error_response( /** @scrutinizer ignore-type */ $response ),
Loading history...
264
			);
265
		}
266
	}
267
268
	/**
269
	 * Format WP Error to response data.
270
	 *
271
	 * @param \WP_Error $error Error object.
272
	 * @return array
273
	 */
274
	protected function format_error_response( $error ) {
275
		return array(
276
			'code'    => $error->get_error_code(),
277
			'message' => $error->get_error_message(),
278
			'data'    => $error->get_error_data(),
279
		);
280
	}
281
}
282