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