PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.15
Booktics – Appointment Booking Calendar for Service Businesses v1.0.15
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
booktics / core / service / controllers / service-controller.php

service-controller.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.15, at core/service/controllers/service-controller.php

703 lines 25.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Service\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Models\Service_Model;
7 use Booktics\Models\Team_Member_Model;
8 use Booktics\Services\Schedule_Manager;
9 use Booktics\Services\Schedule_Record;
10 use Booktics\Taxonomy\Category;
11 use Booktics\Service\Helpers\Service_Helper;
12 use Exception;
13 use WP_Error;
14 use WP_HTTP_Response;
15 use WP_REST_Server;
16
17 /**
18 * Service controller
19 *
20 * @package Booktics/Service
21 */
22 class Service_Controller extends Base_Rest_Controller {
23 /**
24 * Endpoint namespace
25 *
26 * @var string
27 */
28 protected $namespace = 'booktics/v1';
29
30 /**
31 * Route name
32 *
33 * @var string
34 */
35 protected $base = 'services';
36
37 /**
38 * Register routes
39 *
40 * @return void
41 */
42 public function register_routes(): void {
43 register_rest_route(
44 $this->namespace, $this->base, array(
45 array(
46 'methods' => WP_REST_Server::CREATABLE,
47 'callback' => array( $this, 'create_item' ),
48 'permission_callback' => array(
49 $this,
50 'create_item_permissions_check',
51 ),
52 ),
53 )
54 );
55
56 register_rest_route(
57 $this->namespace, $this->base, array(
58 array(
59 'methods' => WP_REST_Server::READABLE,
60 'callback' => array( $this, 'get_items' ),
61 'permission_callback' => array(
62 $this,
63 'get_items_permissions_check',
64 ),
65 ),
66 )
67 );
68
69 register_rest_route(
70 $this->namespace, $this->base, array(
71 array(
72 'methods' => WP_REST_Server::DELETABLE,
73 'callback' => array( $this, 'delete_items' ),
74 'permission_callback' => array(
75 $this,
76 'delete_items_permissions_check',
77 ),
78 ),
79 )
80 );
81
82 register_rest_route(
83 $this->namespace, $this->base . '/(?P<id>[\d]+)', array(
84 array(
85 'methods' => WP_REST_Server::READABLE,
86 'callback' => array( $this, 'get_item' ),
87 'permission_callback' => array(
88 $this,
89 'get_item_permissions_check',
90 ),
91 ),
92 )
93 );
94
95 register_rest_route(
96 $this->namespace, $this->base . '/(?P<id>[\d]+)', array(
97 array(
98 'methods' => WP_REST_Server::EDITABLE,
99 'callback' => array( $this, 'update_item' ),
100 'permission_callback' => array(
101 $this,
102 'update_item_permissions_check',
103 ),
104 ),
105 )
106 );
107
108 register_rest_route(
109 $this->namespace, $this->base . '/(?P<id>[\d]+)', array(
110 array(
111 'methods' => WP_REST_Server::DELETABLE,
112 'callback' => array( $this, 'delete_item' ),
113 'permission_callback' => array(
114 $this,
115 'delete_item_permissions_check',
116 ),
117 ),
118 )
119 );
120
121 register_rest_route(
122 $this->namespace, $this->base . '/(?P<id>[\d]+)/clone', array(
123 array(
124 'methods' => WP_REST_Server::CREATABLE,
125 'callback' => array( $this, 'clone_item' ),
126 'permission_callback' => array(
127 $this,
128 'create_item_permissions_check',
129 ),
130 ),
131 )
132 );
133 }
134
135 /**
136 * Get one item from the collection.
137 *
138 * @param $request
139 *
140 * @return WP_HTTP_Response
141 */
142 public function get_item( $request ) {
143 $id = intval( $request['id'] );
144
145 $service = Service_Model::find( $id );
146
147 if ( ! $service ) {
148 return $this->error( __( 'Service not found', 'booktics' ), 404 );
149 }
150 $service = $service->to_array();
151
152 $team_member_ids = is_array( $service['team_member'] )
153 ? array_unique( $service['team_member'] ) : array( $service['team_member'] );
154 $service['team_member_details'] = Service_Helper::team_member_details( $team_member_ids );
155 $service['team_member'] = $team_member_ids;
156
157 $category_ids = $this->get_service_category_ids( $service['id'] );
158 $service['category_details'] = Service_Helper::category_details( $category_ids );
159 $service['categories'] = $category_ids;
160
161 $schedule_record = ( new Schedule_Record() )
162 ->get_schedules( $id, 'service' );
163 $service['schedule'] = empty( $schedule_record['schedule'] ) ? (object) array()
164 : $schedule_record['schedule'];
165
166 $service['custom'] = $schedule_record['custom'];
167 $service['holiday'] = $schedule_record['holiday'];
168 $service['shortcode'] = '[booktics_service_view id="' . $service['id'] . '"]';
169 $service = apply_filters( 'booktics_service_format', $service );
170
171 return $this->response( $service );
172 }
173
174 /**
175 * Check if a given request has access to get a specific item.
176 *
177 * @param $request
178 *
179 * @return WP_Error
180 */
181 public function get_item_permissions_check( $request ): bool {
182 return true;
183 }
184
185 /**
186 * Get a collection of items.
187 *
188 * @param $request
189 *
190 * @return WP_HTTP_Response
191 */
192 public function get_items( $request ) {
193 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 10;
194 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
195
196 $args = array(
197 'per_page' => $per_page,
198 'paged' => $paged,
199 'meta_query' => array(),
200 );
201
202 if ( ! empty( $request['team_member_id'] ) ) {
203 $args['meta_query'][] = array(
204 'key' => 'team_member',
205 'value' => sprintf( 'i:%d;', intval( $request['team_member_id'] ) ),
206 'compare' => 'LIKE',
207 );
208 }
209
210 if ( ! empty( $request['search'] ) ) {
211 $args['s'] = sanitize_text_field( $request['search'] );
212 }
213
214 if ( ! empty( $args['meta_query'] ) ) {
215 $args['meta_query']['relation'] = 'AND';
216 }
217
218 $args = apply_filters( 'booktics_service_args', $args, $request );
219
220 $data = Service_Model::paginate( $per_page, $paged, $args );
221
222 if ( ! $data ) {
223 return $this->error( __( 'No service found', 'booktics' ), 404 );
224 }
225
226 if ( ! empty( $request['category_id'] ) ) {
227 $service_ids_for_category = get_term_meta( $request['category_id'], 'booktics_category_services', true);
228 }
229
230 if ( ! empty( $data['items'] ) ) {
231 $enhanced_items = array();
232 foreach ( $data['items'] as $service ) {
233 $service_array = $service->to_array();
234
235 if ( isset( $service_ids_for_category ) && ! in_array( $service_array['id'], $service_ids_for_category ) ) {
236 continue;
237 }
238
239 if ( ! empty( $service_array['team_member'] ) ) {
240 $team_member_ids = is_array( $service->team_member ) ? array_unique( $service->team_member ) : array( $service->team_member );
241 $service->team_member = $team_member_ids;
242 $service_array['team_member_details'] = Service_Helper::team_member_details( $team_member_ids );
243 }
244 $category_ids = $this->get_service_category_ids( $service_array['id'] );
245 if ( ! empty( $category_ids ) ) {
246 $service->categories = $category_ids;
247 $service_array['category_details'] = Service_Helper::category_details( $category_ids );
248 }
249 $service_array['shortcode'] = '[booktics_service_view id="' . $service_array['id'] . '"]';
250 $service_array = apply_filters( 'booktics_service_format', $service_array );
251 $enhanced_items[] = $service_array;
252 }
253 $data['items'] = $enhanced_items;
254 }
255
256 return $this->response( $data );
257 }
258
259 /**
260 * Get all category IDs linked to a given service
261 *
262 * @param int $service_id
263 * @return array
264 */
265 private function get_service_category_ids( int $service_id ): array {
266 $args = array(
267 'taxonomy' => 'booktics_service_category', // Category taxonomy
268 'hide_empty' => false,
269 'meta_query' => array(
270 array(
271 'key' => 'booktics_category_services',
272 'value' => sprintf( 'i:%d;', $service_id ),
273 'compare' => 'LIKE',
274 ),
275 ),
276 'fields' => 'ids', // returning only ids
277 );
278
279 $categories = get_terms( $args );
280
281 if ( is_wp_error( $categories ) || empty( $categories ) ) {
282 return [];
283 }
284
285 return $categories;
286 }
287
288
289 /**
290 * Check if a given request has access to get items.
291 *
292 * @param $request
293 *
294 * @return WP_Error
295 */
296 public function get_items_permissions_check( $request ): bool {
297 return true;
298 }
299
300 /**
301 * Create one item from the collection.
302 *
303 * @param $request
304 *
305 * @return WP_HTTP_Response
306 * @throws Exception
307 */
308 public function create_item( $request ) {
309 $data = $this->prepare_item_for_database( $request );
310 if ( is_wp_error( $data ) ) {
311 return $this->error( $data->get_error_message() );
312 }
313
314 $service = Service_Model::create( $data );
315
316 do_action( 'booktics_service_created', $service, $request );
317
318 if ( ! empty( $data['team_member'] ) && is_array( $data['team_member'] ) ) {
319 $this->update_team_members( $service->id, $data['team_member'] );
320 $this->remove_service_from_other_team_members( $service->id, $data['team_member'] );
321 }
322
323 if ( is_wp_error( $service ) ) {
324 return $this->error( $service->get_error_message() );
325 }
326 $service = $service->to_array();
327 $service['shortcode'] = '[booktics_service_view id="' . $service['id'] . '"]';
328 $service['category_details'] = Service_Helper::category_details( $service['categories'] );
329 $service['team_member_details'] = Service_Helper::team_member_details( $service['team_member'] );
330 $service = apply_filters( 'booktics_service_format', $service );
331
332 return $this->response( $service, __( 'Successfully created service', 'booktics' ) );
333 }
334
335 /**
336 * Check if a given request has access to create items.
337 *
338 * @param $request
339 *
340 * @return WP_Error
341 */
342 public function create_item_permissions_check( $request ): bool {
343 return current_user_can( 'manage_options' );
344 }
345
346 /**
347 * Update one item from the collection.
348 *
349 * @param $request
350 *
351 * @return WP_HTTP_Response
352 * @throws Exception
353 */
354 public function update_item( $request ) {
355 $id = (int) $request->get_param( 'id' );
356 $data = $this->prepare_item_for_database( $request );
357
358 if ( is_wp_error( $data ) ) {
359 return $this->error( $data->get_error_message() );
360 }
361
362 $service = Service_Model::find( $id );
363
364 if ( ! $service ) {
365 return $this->error( __( 'Service not found', 'booktics' ), 404 );
366 }
367
368 $result = $service->update( $data );
369
370 if ( is_wp_error( $result ) ) {
371 return $this->error( $result->get_error_message() );
372 }
373
374 if ( ! empty( $data['team_member'] ) && is_array( $data['team_member'] ) ) {
375 $this->update_team_members( $id, $data['team_member'] );
376 $this->remove_service_from_other_team_members( $id, $data['team_member'] );
377 }
378
379 $input_data = json_decode( $request->get_body(), true );
380 $this->update_service_schedule( $id, $input_data );
381
382 do_action( 'booktics_service_updated', $service, $request );
383
384 $response = $this->format_service_response( $id );
385 return $this->response( $response, __( 'Successfully updated service', 'booktics' ) );
386 }
387
388 /**
389 * Update team members for a service
390 */
391 private function update_team_members( $service_id, $team_members ) {
392 foreach ( $team_members as $team_member_id ) {
393 $team_member_model = ( new Team_Member_Model( $team_member_id ) );
394 $team_member = $team_member_model->find( $team_member_id );
395 $team_member_services = $team_member->services == '' ? array() : $team_member->services;
396
397 if ( ! in_array( (int) $service_id, $team_member_services ) ) {
398 $team_member_services[] = (int) $service_id;
399 if ( $team_member ) {
400 $team_member->update(
401 array(
402 'services' => $team_member_services,
403 )
404 );
405 }
406 }
407 }
408 }
409
410 /**
411 * Remove service from other team members not in the list
412 */
413 private function remove_service_from_other_team_members( $service_id, $team_members ) {
414 $other_team_members = ( new Team_Member_Model() )
415 ->where( 'services', $service_id, 'LIKE' )
416 ->all( array() );
417
418 if ( ! empty( $other_team_members['items'] ) ) {
419 foreach ( $other_team_members['items'] as $other_team_member ) {
420 if ( ! in_array( $other_team_member['id'], $team_members ) ) {
421 $other_services = $other_team_member['services'];
422 if ( ( $key = array_search( $service_id, $other_services ) ) !== false ) {
423 unset( $other_services[ $key ] );
424 ( new Team_Member_Model( $other_team_member['id'] ) )->update(
425 array(
426 'services' => array_values( $other_services ),
427 )
428 );
429 }
430 }
431 }
432 }
433 }
434
435 /**
436 * Update the service schedule if provided
437 */
438 private function update_service_schedule( $service_id, $input_data ) {
439 if ( isset( $input_data['schedule'] ) || isset( $input_data['custom'] ) || isset( $input_data['holiday'] ) ) {
440 $service_schedule = Schedule_Manager::make( $input_data );
441 $service_schedule->with_service_id( $service_id );
442 $service_schedule->save();
443 }
444 }
445
446 /**
447 * Format the service response after update
448 */
449 private function format_service_response( $service_id ) {
450 $response = Service_Model::find( $service_id )->to_array();
451 $response['shortcode'] = '[booktics_service_view id="' . $response['id'] . '"]';
452 $response['team_member_details'] = Service_Helper::team_member_details( $response['team_member'] );
453 if ( ! empty( $response['categories'] ) ) {
454 $category_ids = is_array( $response['categories'] ) ? $response['categories'] : array( $response['categories'] );
455 $response['category_details'] = Service_Helper::category_details( $category_ids );
456 }
457 return apply_filters( 'booktics_service_format', $response );
458 }
459
460 /**
461 * Check if a given request has access to update a specific item.
462 *
463 * @param $request
464 *
465 * @return WP_Error
466 */
467 public function update_item_permissions_check( $request ) {
468 return current_user_can( 'manage_options' );
469 }
470
471 /**
472 * Delete one item from the collection.
473 *
474 * @param $request
475 *
476 * @return WP_HTTP_Response
477 */
478 public function delete_item( $request ) {
479 $id = intval( $request['id'] );
480
481 $service = Service_Model::find( $id );
482
483 if ( ! $service ) {
484 return $this->error( __( 'Service not found', 'booktics' ), 404 );
485 }
486
487 $deleted = $service->delete();
488
489 if ( is_wp_error( $deleted ) ) {
490 return $this->error( $deleted->get_error_message() );
491 }
492
493 return $this->response( $service, __( 'Successfully deleted service', 'booktics' ) );
494 }
495
496 /**
497 * Check if a given request has access to delete a specific item.
498 *
499 * @param $request
500 *
501 * @return WP_Error
502 */
503 public function delete_item_permissions_check( $request ) {
504 return current_user_can( 'manage_options' );
505 }
506
507 /**
508 * Delete a collection of items
509 *
510 * @param $request
511 *
512 * @return WP_HTTP_Response
513 */
514 public function delete_items( $request ) {
515 $ids = $request['ids'];
516
517 if ( ! is_array( $ids ) ) {
518 return $this->error( __( 'Ids should be an array of items ids', 'booktics' ) );
519 }
520
521 $count = 0;
522 $total = count( $ids );
523
524 foreach ( $ids as $id ) {
525 $service = Service_Model::find( $id );
526
527 if ( $service ) {
528 $service->delete();
529 ++$count;
530 }
531 }
532
533 if ( $count === 0 ) {
534 return $this->error( __( 'No item deleted.', 'booktics' ) );
535 }
536
537 $message = sprintf( 'Deleted %d items of %d', $count, $total );
538
539 return $this->response( array(), $message );
540 }
541
542 /**
543 * Check if a given request has access to delete a collection of items.
544 *
545 * @param $request
546 *
547 * @return WP_Error
548 */
549 public function delete_items_permissions_check( $request ) {
550 return current_user_can( 'manage_options' );
551 }
552
553 /**
554 * Prepare the item for create or update operation.
555 *
556 * @param $request
557 *
558 * @return WP_Error
559 */
560 public function prepare_item_for_database( $request ) {
561 $data = json_decode( $request->get_body(), true );
562 $validate = booktics_validate(
563 $data, array(
564 'title' => array(
565 'required',
566 ),
567 )
568 );
569
570 if ( is_wp_error( $validate ) ) {
571 return $validate;
572 }
573 if ( empty( $data['team_member'] ) ) {
574 $admin_query = new \WP_User_Query(
575 array(
576 'role' => array( 'administrator', 'booktics_team_member' ),
577 )
578 );
579 $admins = $admin_query->get_results();
580 $admin = reset( $admins );
581 $team_member_id = $admin->id;
582 $data['team_member'] = array( $team_member_id );
583 }
584
585 $data['title'] = ! empty( $data['title'] ) ? sanitize_text_field( $data['title'] ) : '';
586 $data['description'] = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
587 $data['content'] = ! empty( $data['content'] ) ? sanitize_text_field( $data['content'] ) : '';
588 $data['link'] = ! empty( $data['link'] ) ? sanitize_text_field( $data['link'] ) : '';
589 $data['image'] = ! empty( $data['image'] ) ? esc_url_raw( $data['image'] ) : '';
590 $data['image_id'] = ! empty( $data['image_id'] ) ? esc_url_raw( $data['image_id'] ) : '';
591 $data['interval_time'] = ! empty( $data['interval_time'] ) ? sanitize_text_field( $data['interval_time'] ) : 0;
592 $data['before_buffer_time'] = ! empty( $data['before_buffer_time'] ) ? sanitize_text_field( $data['before_buffer_time'] ) : 0;
593 $data['after_buffer_time'] = ! empty( $data['after_buffer_time'] ) ? sanitize_text_field( $data['after_buffer_time'] ) : 0;
594 $data['show_end_time'] = ! empty( $data['show_end_time'] ) ? sanitize_text_field( $data['show_end_time'] ) : false;
595 $data['status'] = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : 'deactive';
596 $data['team_member'] = empty( $data['team_member'] ) ? array() : array_unique( $data['team_member'] );
597 $data['categories'] = empty( $data['categories'] ) ? array() : array_unique( $data['categories'] );
598 $sanitized_durations = array();
599 if ( ! empty( $data['additional_durations'] ) ) {
600 foreach ( $data['additional_durations'] as $duration ) {
601 if ( is_array( $duration ) ) {
602 $sanitized_durations[] = array(
603 'id' => ! empty( $duration['id'] ) ? sanitize_text_field( $duration['id'] ) : '',
604 'name' => ! empty( $duration['name'] ) ? sanitize_text_field( $duration['name'] ) : '',
605 'duration_time' => ! empty( $duration['duration_time'] ) ? sanitize_text_field( $duration['duration_time'] ) : 0,
606 'duration_unit' => ! empty( $duration['duration_unit'] ) ? sanitize_text_field( $duration['duration_unit'] ) : '',
607 'charge_amount' => ! empty( $duration['charge_amount'] ) ? (float) number_format( (float) $duration['charge_amount'], 4, '.', '' ) : '0.0000',
608 'deposit_amount' => ! empty( $duration['deposit_amount'] ) ? (float) number_format( (float) $duration['deposit_amount'], 4, '.', '' ) : '0.0000',
609 );
610 }
611 }
612 } else {
613 $sanitized_durations = array(
614 array(
615 'id' => 'default',
616 'name' => 'default',
617 'duration_time' => 30,
618 'duration_unit' => 'min',
619 'charge_amount' => 0,
620 'deposit_amount' => 0,
621 ),
622 );
623 }
624 $data['additional_durations'] = $sanitized_durations;
625 $data = apply_filters( 'booktics_prepare_service_custom_fields', $data );
626 $image_url = $request->get_param( 'image' );
627 if ( $image_url ) {
628 $data['image'] = esc_url_raw( $image_url );
629 }
630
631 return $data;
632 }
633
634 /**
635 * Clone service from the record.
636 *
637 * @param $request
638 *
639 * @return WP_HTTP_Response
640 * @throws Exception
641 */
642 public function clone_item( $request ) {
643 $id = intval( $request['id'] );
644
645 $service = Service_Model::find( $id );
646
647 if ( ! $service ) {
648 return $this->error( __( 'Service not found', 'booktics' ), 404 );
649 }
650 $clone_data = [
651 'title' => $service->title . __( ' Copy', 'booktics' ),
652 'description' => $service->description,
653 'content' => $service->content,
654 'image' => $service->image,
655 'image_id' => $service->image_id,
656 'categories' => $service->categories,
657 'interval_time' => $service->interval_time,
658 'before_buffer_time' => $service->before_buffer_time,
659 'after_buffer_time' => $service->after_buffer_time,
660 'team_member' => $service->team_member,
661 'show_end_time' => $service->show_end_time,
662 'additional_durations' => $service->additional_durations,
663 'enable_custom_holiday' => $service->enable_custom_holiday,
664 'enable_custom_schedule' => $service->enable_custom_schedule,
665 'enable_additional_service_duration' => $service->enable_additional_service_duration,
666 ];
667
668 $clone_data = apply_filters( 'booktics_clone_service_data', $clone_data, $service );
669
670 $cloned = Service_Model::create( $clone_data );
671
672 if ( is_wp_error( $cloned ) ) {
673 return $this->error( $cloned->get_error_message() );
674 }
675
676 $schedule_record = ( new Schedule_Record() )
677 ->get_schedules( $id, 'service' );
678 $schedule_data['schedule'] = $schedule_record['schedule'];
679 $schedule_data['custom'] = $schedule_record['custom'];
680 $schedule_data['holiday'] = $schedule_record['holiday'];
681
682 $service_schedule = Schedule_Manager::make( $schedule_data );
683 $service_schedule->with_service_id( $cloned->id );
684 $service_schedule->save();
685
686 $team_member_details = Service_Helper::team_member_details( $service->team_member );
687 $cloned = $cloned->to_array();
688 $cloned['team_member_details'] = $team_member_details;
689 $cloned['category_details'] = Service_Helper::category_details( $cloned['categories'] );
690
691 $clone_schedule_record = ( new Schedule_Record() )
692 ->get_schedules( $cloned['id'], 'service' );
693 $cloned['schedule'] = empty( $clone_schedule_record['schedule'] ) ? (object) array()
694 : $clone_schedule_record['schedule'];
695 $cloned['custom'] = $clone_schedule_record['custom'];
696 $cloned['holiday'] = $clone_schedule_record['holiday'];
697 $cloned['shortcode'] = '[booktics_service_view id="' . $cloned['id'] . '"]';
698 $cloned = apply_filters( 'booktics_service_format', $cloned );
699
700 return $this->response( $cloned, __( 'Successfully cloned service', 'booktics' ) );
701 }
702 }
703