Total Complexity | 52 |
Total Lines | 678 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Webhooks 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.
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 Webhooks, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class Webhooks extends AbstractController { |
||
18 | |||
19 | /** |
||
20 | * Route base. |
||
21 | * |
||
22 | * @var string |
||
23 | */ |
||
24 | protected $rest_base = 'webhooks'; |
||
25 | |||
26 | /** |
||
27 | * Permission to check. |
||
28 | * |
||
29 | * @var string |
||
30 | */ |
||
31 | protected $resource_type = 'webhooks'; |
||
32 | |||
33 | /** |
||
34 | * Post type. |
||
35 | * |
||
36 | * @var string |
||
37 | */ |
||
38 | protected $post_type = 'shop_webhook'; |
||
39 | |||
40 | /** |
||
41 | * Register the routes for webhooks. |
||
42 | */ |
||
43 | public function register_routes() { |
||
44 | register_rest_route( |
||
45 | $this->namespace, |
||
46 | '/' . $this->rest_base, |
||
47 | array( |
||
48 | array( |
||
49 | 'methods' => \WP_REST_Server::READABLE, |
||
50 | 'callback' => array( $this, 'get_items' ), |
||
51 | 'permission_callback' => array( $this, 'get_items_permissions_check' ), |
||
52 | 'args' => $this->get_collection_params(), |
||
53 | ), |
||
54 | array( |
||
55 | 'methods' => \WP_REST_Server::CREATABLE, |
||
56 | 'callback' => array( $this, 'create_item' ), |
||
57 | 'permission_callback' => array( $this, 'create_item_permissions_check' ), |
||
58 | 'args' => array_merge( |
||
59 | $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), |
||
60 | array( |
||
61 | 'topic' => array( |
||
62 | 'required' => true, |
||
63 | 'type' => 'string', |
||
64 | 'description' => __( 'Webhook topic.', 'woocommerce' ), |
||
65 | ), |
||
66 | 'delivery_url' => array( |
||
67 | 'required' => true, |
||
68 | 'type' => 'string', |
||
69 | 'description' => __( 'Webhook delivery URL.', 'woocommerce' ), |
||
70 | ), |
||
71 | ) |
||
72 | ), |
||
73 | ), |
||
74 | 'schema' => array( $this, 'get_public_item_schema' ), |
||
75 | ), |
||
76 | true |
||
77 | ); |
||
78 | |||
79 | register_rest_route( |
||
80 | $this->namespace, |
||
81 | '/' . $this->rest_base . '/(?P<id>[\d]+)', |
||
82 | array( |
||
83 | 'args' => array( |
||
84 | 'id' => array( |
||
85 | 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), |
||
86 | 'type' => 'integer', |
||
87 | ), |
||
88 | ), |
||
89 | array( |
||
90 | 'methods' => \WP_REST_Server::READABLE, |
||
91 | 'callback' => array( $this, 'get_item' ), |
||
92 | 'permission_callback' => array( $this, 'get_item_permissions_check' ), |
||
93 | 'args' => array( |
||
94 | 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
||
95 | ), |
||
96 | ), |
||
97 | array( |
||
98 | 'methods' => \WP_REST_Server::EDITABLE, |
||
99 | 'callback' => array( $this, 'update_item' ), |
||
100 | 'permission_callback' => array( $this, 'update_item_permissions_check' ), |
||
101 | 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), |
||
102 | ), |
||
103 | array( |
||
104 | 'methods' => \WP_REST_Server::DELETABLE, |
||
105 | 'callback' => array( $this, 'delete_item' ), |
||
106 | 'permission_callback' => array( $this, 'delete_item_permissions_check' ), |
||
107 | 'args' => array( |
||
108 | 'force' => array( |
||
109 | 'default' => false, |
||
110 | 'type' => 'boolean', |
||
111 | 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ), |
||
112 | ), |
||
113 | ), |
||
114 | ), |
||
115 | 'schema' => array( $this, 'get_public_item_schema' ), |
||
116 | ), |
||
117 | true |
||
118 | ); |
||
119 | |||
120 | $this->register_batch_route(); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Get the default REST API version. |
||
125 | * |
||
126 | * @since 3.0.0 |
||
127 | * @return string |
||
128 | */ |
||
129 | protected function get_default_api_version() { |
||
130 | return 'wp_api_v4'; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Get all webhooks. |
||
135 | * |
||
136 | * @param \WP_REST_Request $request Full details about the request. |
||
137 | * @return \WP_Error|\WP_REST_Response |
||
138 | */ |
||
139 | public function get_items( $request ) { |
||
140 | $args = array(); |
||
141 | $args['order'] = $request['order']; |
||
142 | $args['orderby'] = $request['orderby']; |
||
143 | $args['status'] = 'all' === $request['status'] ? '' : $request['status']; |
||
144 | $args['include'] = implode( ',', $request['include'] ); |
||
|
|||
145 | $args['exclude'] = implode( ',', $request['exclude'] ); |
||
146 | $args['limit'] = $request['per_page']; |
||
147 | $args['search'] = $request['search']; |
||
148 | $args['before'] = $request['before']; |
||
149 | $args['after'] = $request['after']; |
||
150 | |||
151 | if ( empty( $request['offset'] ) ) { |
||
152 | $args['offset'] = 1 < $request['page'] ? ( $request['page'] - 1 ) * $args['limit'] : 0; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Filter arguments, before passing to WC_Webhook_Data_Store->search_webhooks, when querying webhooks via the REST API. |
||
157 | * |
||
158 | * @param array $args Array of arguments for $wpdb->get_results(). |
||
159 | * @param \WP_REST_Request $request The current request. |
||
160 | */ |
||
161 | $prepared_args = apply_filters( 'woocommerce_rest_webhook_query', $args, $request ); |
||
162 | unset( $prepared_args['page'] ); |
||
163 | $prepared_args['paginate'] = true; |
||
164 | |||
165 | // Get the webhooks. |
||
166 | $webhooks = array(); |
||
167 | $data_store = \WC_Data_Store::load( 'webhook' ); |
||
168 | $results = $data_store->search_webhooks( $prepared_args ); |
||
169 | $webhook_ids = $results->webhooks; |
||
170 | |||
171 | foreach ( $webhook_ids as $webhook_id ) { |
||
172 | $data = $this->prepare_item_for_response( $webhook_id, $request ); |
||
173 | $webhooks[] = $this->prepare_response_for_collection( $data ); |
||
174 | } |
||
175 | |||
176 | $response = rest_ensure_response( $webhooks ); |
||
177 | $per_page = (int) $prepared_args['limit']; |
||
178 | $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); |
||
179 | $total_webhooks = $results->total; |
||
180 | $max_pages = $results->max_num_pages; |
||
181 | $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); |
||
182 | |||
183 | $response->header( 'X-WP-Total', $total_webhooks ); |
||
184 | $response->header( 'X-WP-TotalPages', $max_pages ); |
||
185 | |||
186 | if ( $page > 1 ) { |
||
187 | $prev_page = $page - 1; |
||
188 | if ( $prev_page > $max_pages ) { |
||
189 | $prev_page = $max_pages; |
||
190 | } |
||
191 | $prev_link = add_query_arg( 'page', $prev_page, $base ); |
||
192 | $response->link_header( 'prev', $prev_link ); |
||
193 | } |
||
194 | if ( $max_pages > $page ) { |
||
195 | $next_page = $page + 1; |
||
196 | $next_link = add_query_arg( 'page', $next_page, $base ); |
||
197 | $response->link_header( 'next', $next_link ); |
||
198 | } |
||
199 | |||
200 | return $response; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * Get a single item. |
||
205 | * |
||
206 | * @param \WP_REST_Request $request Full details about the request. |
||
207 | * @return \WP_Error|\WP_REST_Response |
||
208 | */ |
||
209 | public function get_item( $request ) { |
||
210 | $id = (int) $request['id']; |
||
211 | |||
212 | if ( empty( $id ) ) { |
||
213 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); |
||
214 | } |
||
215 | |||
216 | $data = $this->prepare_item_for_response( $id, $request ); |
||
217 | $response = rest_ensure_response( $data ); |
||
218 | |||
219 | return $response; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Create a single webhook. |
||
224 | * |
||
225 | * @param \WP_REST_Request $request Full details about the request. |
||
226 | * @return \WP_Error|\WP_REST_Response |
||
227 | */ |
||
228 | public function create_item( $request ) { |
||
229 | if ( ! empty( $request['id'] ) ) { |
||
230 | /* translators: %s: post type */ |
||
231 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); |
||
232 | } |
||
233 | |||
234 | // Validate topic. |
||
235 | if ( empty( $request['topic'] ) || ! wc_is_webhook_valid_topic( strtolower( $request['topic'] ) ) ) { |
||
236 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_topic", __( 'Webhook topic is required and must be valid.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
237 | } |
||
238 | |||
239 | // Validate delivery URL. |
||
240 | if ( empty( $request['delivery_url'] ) || ! wc_is_valid_url( $request['delivery_url'] ) ) { |
||
241 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_delivery_url", __( 'Webhook delivery URL must be a valid URL starting with http:// or https://.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
242 | } |
||
243 | |||
244 | $post = $this->prepare_item_for_database( $request ); |
||
245 | if ( is_wp_error( $post ) ) { |
||
246 | return $post; |
||
247 | } |
||
248 | |||
249 | $webhook = new \WC_Webhook(); |
||
250 | $webhook->set_name( $post->post_title ); |
||
251 | $webhook->set_user_id( $post->post_author ); |
||
252 | $webhook->set_status( 'publish' === $post->post_status ? 'active' : 'disabled' ); |
||
253 | $webhook->set_topic( $request['topic'] ); |
||
254 | $webhook->set_delivery_url( $request['delivery_url'] ); |
||
255 | $webhook->set_secret( ! empty( $request['secret'] ) ? $request['secret'] : wp_generate_password( 50, true, true ) ); |
||
256 | $webhook->set_api_version( $this->get_default_api_version() ); |
||
257 | $webhook->save(); |
||
258 | |||
259 | $this->update_additional_fields_for_object( $webhook, $request ); |
||
260 | |||
261 | /** |
||
262 | * Fires after a single item is created or updated via the REST API. |
||
263 | * |
||
264 | * @param WC_Webhook $webhook Webhook data. |
||
265 | * @param \WP_REST_Request $request Request object. |
||
266 | * @param bool $creating True when creating item, false when updating. |
||
267 | */ |
||
268 | do_action( 'woocommerce_rest_insert_webhook_object', $webhook, $request, true ); |
||
269 | |||
270 | $request->set_param( 'context', 'edit' ); |
||
271 | $response = $this->prepare_item_for_response( $webhook->get_id(), $request ); |
||
272 | $response = rest_ensure_response( $response ); |
||
273 | $response->set_status( 201 ); |
||
274 | $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $webhook->get_id() ) ) ); |
||
275 | |||
276 | // Send ping. |
||
277 | $webhook->deliver_ping(); |
||
278 | |||
279 | return $response; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Update a single webhook. |
||
284 | * |
||
285 | * @param \WP_REST_Request $request Full details about the request. |
||
286 | * @return \WP_Error|\WP_REST_Response |
||
287 | */ |
||
288 | public function update_item( $request ) { |
||
289 | $id = (int) $request['id']; |
||
290 | $webhook = wc_get_webhook( $id ); |
||
291 | |||
292 | if ( empty( $webhook ) || is_null( $webhook ) ) { |
||
293 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
294 | } |
||
295 | |||
296 | // Update topic. |
||
297 | if ( ! empty( $request['topic'] ) ) { |
||
298 | if ( wc_is_webhook_valid_topic( strtolower( $request['topic'] ) ) ) { |
||
299 | $webhook->set_topic( $request['topic'] ); |
||
300 | } else { |
||
301 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_topic", __( 'Webhook topic must be valid.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
302 | } |
||
303 | } |
||
304 | |||
305 | // Update delivery URL. |
||
306 | if ( ! empty( $request['delivery_url'] ) ) { |
||
307 | if ( wc_is_valid_url( $request['delivery_url'] ) ) { |
||
308 | $webhook->set_delivery_url( $request['delivery_url'] ); |
||
309 | } else { |
||
310 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_delivery_url", __( 'Webhook delivery URL must be a valid URL starting with http:// or https://.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
311 | } |
||
312 | } |
||
313 | |||
314 | // Update secret. |
||
315 | if ( ! empty( $request['secret'] ) ) { |
||
316 | $webhook->set_secret( $request['secret'] ); |
||
317 | } |
||
318 | |||
319 | // Update status. |
||
320 | if ( ! empty( $request['status'] ) ) { |
||
321 | if ( wc_is_webhook_valid_status( strtolower( $request['status'] ) ) ) { |
||
322 | $webhook->set_status( $request['status'] ); |
||
323 | } else { |
||
324 | return new \WP_Error( "woocommerce_rest_{$this->post_type}_invalid_status", __( 'Webhook status must be valid.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
325 | } |
||
326 | } |
||
327 | |||
328 | $post = $this->prepare_item_for_database( $request ); |
||
329 | if ( is_wp_error( $post ) ) { |
||
330 | return $post; |
||
331 | } |
||
332 | |||
333 | if ( isset( $post->post_title ) ) { |
||
334 | $webhook->set_name( $post->post_title ); |
||
335 | } |
||
336 | |||
337 | $webhook->save(); |
||
338 | |||
339 | $this->update_additional_fields_for_object( $webhook, $request ); |
||
340 | |||
341 | /** |
||
342 | * Fires after a single item is created or updated via the REST API. |
||
343 | * |
||
344 | * @param WC_Webhook $webhook Webhook data. |
||
345 | * @param \WP_REST_Request $request Request object. |
||
346 | * @param bool $creating True when creating item, false when updating. |
||
347 | */ |
||
348 | do_action( 'woocommerce_rest_insert_webhook_object', $webhook, $request, false ); |
||
349 | |||
350 | $request->set_param( 'context', 'edit' ); |
||
351 | $response = $this->prepare_item_for_response( $webhook->get_id(), $request ); |
||
352 | |||
353 | return rest_ensure_response( $response ); |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Delete a single webhook. |
||
358 | * |
||
359 | * @param \WP_REST_Request $request Full details about the request. |
||
360 | * @return \WP_REST_Response|\WP_Error |
||
361 | */ |
||
362 | public function delete_item( $request ) { |
||
402 | } |
||
403 | |||
404 | /** |
||
405 | * Prepare a single webhook for create or update. |
||
406 | * |
||
407 | * @param \WP_REST_Request $request Request object. |
||
408 | * @return \WP_Error|stdClass $data Post object. |
||
409 | */ |
||
410 | protected function prepare_item_for_database( $request ) { |
||
411 | $data = new \stdClass(); |
||
412 | |||
413 | // Post ID. |
||
414 | if ( isset( $request['id'] ) ) { |
||
415 | $data->ID = absint( $request['id'] ); |
||
416 | } |
||
417 | |||
418 | // Validate required POST fields. |
||
419 | if ( 'POST' === $request->get_method() && empty( $data->ID ) ) { |
||
420 | $data->post_title = ! empty( $request['name'] ) ? $request['name'] : sprintf( __( 'Webhook created on %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Webhook created on date parsed by strftime', 'woocommerce' ) ) ); // @codingStandardsIgnoreLine |
||
421 | |||
422 | // Post author. |
||
423 | $data->post_author = get_current_user_id(); |
||
424 | |||
425 | // Post password. |
||
426 | $data->post_password = 'webhook_' . wp_generate_password(); |
||
427 | |||
428 | // Post status. |
||
429 | $data->post_status = 'publish'; |
||
430 | } else { |
||
431 | |||
432 | // Allow edit post title. |
||
433 | if ( ! empty( $request['name'] ) ) { |
||
434 | $data->post_title = $request['name']; |
||
435 | } |
||
436 | } |
||
437 | |||
438 | // Comment status. |
||
439 | $data->comment_status = 'closed'; |
||
440 | |||
441 | // Ping status. |
||
442 | $data->ping_status = 'closed'; |
||
443 | |||
444 | /** |
||
445 | * Filter the query_vars used in `get_items` for the constructed query. |
||
446 | * |
||
447 | * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being |
||
448 | * prepared for insertion. |
||
449 | * |
||
450 | * @param \stdClass $data An object representing a single item prepared |
||
451 | * for inserting or updating the database. |
||
452 | * @param \WP_REST_Request $request Request object. |
||
453 | */ |
||
454 | return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $data, $request ); |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * Prepare a single webhook output for response. |
||
459 | * |
||
460 | * @param int $id Webhook ID. |
||
461 | * @param \WP_REST_Request $request Request object. |
||
462 | * @return \WP_REST_Response $response |
||
463 | */ |
||
464 | public function prepare_item_for_response( $id, $request ) { |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * Prepare links for the request. |
||
507 | * |
||
508 | * @param int $id Webhook ID. |
||
509 | * @return array |
||
510 | */ |
||
511 | protected function prepare_links( $id ) { |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * Get the Webhook's schema, conforming to JSON Schema. |
||
526 | * |
||
527 | * @return array |
||
528 | */ |
||
529 | public function get_item_schema() { |
||
619 | } |
||
620 | |||
621 | /** |
||
622 | * Get the query params for collections of attachments. |
||
623 | * |
||
624 | * @return array |
||
625 | */ |
||
626 | public function get_collection_params() { |
||
695 | } |
||
696 | } |
||
697 |