namespace, $this->base, array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'create_item_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base, array( array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_items' ), 'permission_callback' => array( $this, 'delete_items_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base . '/(?P[\d]+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base . '/(?P[\d]+)', array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_item_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base . '/(?P[\d]+)', array( array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_item' ), 'permission_callback' => array( $this, 'delete_item_permissions_check', ), ), ) ); register_rest_route( $this->namespace, $this->base . '/(?P[\d]+)/clone', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'clone_item' ), 'permission_callback' => array( $this, 'create_item_permissions_check', ), ), ) ); } /** * Get one item from the collection. * * @param $request * * @return WP_HTTP_Response */ public function get_item( $request ) { $id = intval( $request['id'] ); $service = Service_Model::find( $id ); if ( ! $service ) { return $this->error( __( 'Service not found', 'booktics' ), 404 ); } $service = $service->to_array(); $team_member_ids = is_array( $service['team_member'] ) ? array_unique( $service['team_member'] ) : array( $service['team_member'] ); $service['team_member_details'] = Service_Helper::team_member_details( $team_member_ids ); $service['team_member'] = $team_member_ids; $category_ids = $this->get_service_category_ids( $service['id'] ); $service['category_details'] = Service_Helper::category_details( $category_ids ); $service['categories'] = $category_ids; $schedule_record = ( new Schedule_Record() ) ->get_schedules( $id, 'service' ); $service['schedule'] = empty( $schedule_record['schedule'] ) ? (object) array() : $schedule_record['schedule']; $service['custom'] = $schedule_record['custom']; $service['holiday'] = $schedule_record['holiday']; $service['shortcode'] = '[booktics_service_view id="' . $service['id'] . '"]'; $service = apply_filters( 'booktics_service_format', $service ); return $this->response( $service ); } /** * Check if a given request has access to get a specific item. * * @param $request * * @return WP_Error */ public function get_item_permissions_check( $request ): bool { return true; } /** * Get a collection of items. * * @param $request * * @return WP_HTTP_Response */ public function get_items( $request ) { $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 10; $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1; $args = array( 'per_page' => $per_page, 'paged' => $paged, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering services by meta fields 'meta_query' => array(), ); if ( ! empty( $request['team_member_id'] ) ) { $args['meta_query'][] = array( 'key' => 'team_member', 'value' => sprintf( 'i:%d;', intval( $request['team_member_id'] ) ), 'compare' => 'LIKE', ); } if ( ! empty( $request['search'] ) ) { $args['s'] = sanitize_text_field( $request['search'] ); } if ( ! empty( $args['meta_query'] ) ) { $args['meta_query']['relation'] = 'AND'; } $args = apply_filters( 'booktics_service_args', $args, $request ); $data = Service_Model::paginate( $per_page, $paged, $args ); if ( ! $data ) { return $this->error( __( 'No service found', 'booktics' ), 404 ); } if ( ! empty( $request['category_id'] ) ) { $service_ids_for_category = get_term_meta( absint( $request['category_id'] ), 'booktics_category_services', true ); } if ( ! empty( $data['items'] ) ) { $enhanced_items = array(); foreach ( $data['items'] as $service ) { $service_array = $service->to_array(); if ( isset( $service_ids_for_category ) && ! in_array( $service_array['id'], $service_ids_for_category ) ) { continue; } if ( ! empty( $service_array['team_member'] ) ) { $team_member_ids = is_array( $service->team_member ) ? array_unique( $service->team_member ) : array( $service->team_member ); $service->team_member = $team_member_ids; $service_array['team_member_details'] = Service_Helper::team_member_details( $team_member_ids ); } $category_ids = $this->get_service_category_ids( $service_array['id'] ); if ( ! empty( $category_ids ) ) { $service->categories = $category_ids; $service_array['category_details'] = Service_Helper::category_details( $category_ids ); } $service_array['shortcode'] = '[booktics_service_view id="' . $service_array['id'] . '"]'; $service_array = apply_filters( 'booktics_service_format', $service_array ); $enhanced_items[] = $service_array; } $data['items'] = $enhanced_items; } return $this->response( $data ); } /** * Get all category IDs linked to a given service * * @param int $service_id * @return array */ private function get_service_category_ids( int $service_id ): array { $args = array( 'taxonomy' => 'booktics_service_category', // Category taxonomy 'hide_empty' => false, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for services by category ID 'meta_query' => array( array( 'key' => 'booktics_category_services', 'value' => sprintf( 'i:%d;', $service_id ), 'compare' => 'LIKE', ), ), 'fields' => 'ids', // returning only ids ); $categories = get_terms( $args ); if ( is_wp_error( $categories ) || empty( $categories ) ) { return []; } return $categories; } /** * Check if a given request has access to get items. * * @param $request * * @return WP_Error */ public function get_items_permissions_check( $request ): bool { return true; } /** * Create one item from the collection. * * @param $request * * @return WP_HTTP_Response * @throws Exception */ public function create_item( $request ) { $data = $this->prepare_item_for_database( $request ); if ( is_wp_error( $data ) ) { return $this->error( $data->get_error_message() ); } $service = Service_Model::create( $data ); do_action( 'booktics_service_created', $service, $request ); if ( ! empty( $data['team_member'] ) && is_array( $data['team_member'] ) ) { $this->update_team_members( $service->id, $data['team_member'] ); $this->remove_service_from_other_team_members( $service->id, $data['team_member'] ); } if ( is_wp_error( $service ) ) { return $this->error( $service->get_error_message() ); } $service = $service->to_array(); $service['shortcode'] = '[booktics_service_view id="' . $service['id'] . '"]'; $service['category_details'] = Service_Helper::category_details( $service['categories'] ); $service['team_member_details'] = Service_Helper::team_member_details( $service['team_member'] ); $service = apply_filters( 'booktics_service_format', $service ); return $this->response( $service, __( 'Successfully created service', 'booktics' ) ); } /** * Check if a given request has access to create items. * * @param $request * * @return WP_Error */ public function create_item_permissions_check( $request ): bool { return current_user_can( 'manage_options' ); } /** * Update one item from the collection. * * @param $request * * @return WP_HTTP_Response * @throws Exception */ public function update_item( $request ) { $id = (int) $request->get_param( 'id' ); $data = $this->prepare_item_for_database( $request ); if ( is_wp_error( $data ) ) { return $this->error( $data->get_error_message() ); } $service = Service_Model::find( $id ); if ( ! $service ) { return $this->error( __( 'Service not found', 'booktics' ), 404 ); } $result = $service->update( $data ); if ( is_wp_error( $result ) ) { return $this->error( $result->get_error_message() ); } if ( ! empty( $data['team_member'] ) && is_array( $data['team_member'] ) ) { $this->update_team_members( $id, $data['team_member'] ); $this->remove_service_from_other_team_members( $id, $data['team_member'] ); } $input_data = json_decode( $request->get_body(), true ); $this->update_service_schedule( $id, $input_data ); do_action( 'booktics_service_updated', $service, $request ); $response = $this->format_service_response( $id ); return $this->response( $response, __( 'Successfully updated service', 'booktics' ) ); } /** * Update team members for a service */ private function update_team_members( $service_id, $team_members ) { foreach ( $team_members as $team_member_id ) { $team_member_model = ( new Team_Member_Model( $team_member_id ) ); $team_member = $team_member_model->find( $team_member_id ); $team_member_services = $team_member->services == '' ? array() : $team_member->services; if ( ! in_array( (int) $service_id, $team_member_services ) ) { $team_member_services[] = (int) $service_id; if ( $team_member ) { $team_member->update( array( 'services' => $team_member_services, ) ); } } } } /** * Remove service from other team members not in the list */ private function remove_service_from_other_team_members( $service_id, $team_members ) { $other_team_members = ( new Team_Member_Model() ) ->where( 'services', $service_id, 'LIKE' ) ->all( array() ); if ( ! empty( $other_team_members['items'] ) ) { foreach ( $other_team_members['items'] as $other_team_member ) { if ( ! in_array( $other_team_member['id'], $team_members ) ) { $other_services = $other_team_member['services']; if ( ( $key = array_search( $service_id, $other_services ) ) !== false ) { unset( $other_services[ $key ] ); ( new Team_Member_Model( $other_team_member['id'] ) )->update( array( 'services' => array_values( $other_services ), ) ); } } } } } /** * Update the service schedule if provided */ private function update_service_schedule( $service_id, $input_data ) { if ( isset( $input_data['schedule'] ) || isset( $input_data['custom'] ) || isset( $input_data['holiday'] ) ) { $service_schedule = Schedule_Manager::make( $input_data ); $service_schedule->with_service_id( $service_id ); $service_schedule->save(); } } /** * Format the service response after update */ private function format_service_response( $service_id ) { $response = Service_Model::find( $service_id )->to_array(); $response['shortcode'] = '[booktics_service_view id="' . $response['id'] . '"]'; $response['team_member_details'] = Service_Helper::team_member_details( $response['team_member'] ); if ( ! empty( $response['categories'] ) ) { $category_ids = is_array( $response['categories'] ) ? $response['categories'] : array( $response['categories'] ); $response['category_details'] = Service_Helper::category_details( $category_ids ); } return apply_filters( 'booktics_service_format', $response ); } /** * Check if a given request has access to update a specific item. * * @param $request * * @return WP_Error */ public function update_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } /** * Delete one item from the collection. * * @param $request * * @return WP_HTTP_Response */ public function delete_item( $request ) { $id = intval( $request['id'] ); $service = Service_Model::find( $id ); if ( ! $service ) { return $this->error( __( 'Service not found', 'booktics' ), 404 ); } $deleted = $service->delete(); if ( is_wp_error( $deleted ) ) { return $this->error( $deleted->get_error_message() ); } return $this->response( $service, __( 'Successfully deleted service', 'booktics' ) ); } /** * Check if a given request has access to delete a specific item. * * @param $request * * @return WP_Error */ public function delete_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } /** * Delete a collection of items * * @param $request * * @return WP_HTTP_Response */ public function delete_items( $request ) { $ids = isset( $request['ids'] ) ? $request['ids'] : array(); if ( ! is_array( $ids ) ) { return $this->error( __( 'Ids should be an array of items ids', 'booktics' ) ); } $count = 0; $total = count( $ids ); foreach ( $ids as $id ) { $service = Service_Model::find( $id ); if ( $service ) { $service->delete(); ++$count; } } if ( $count === 0 ) { return $this->error( __( 'No item deleted.', 'booktics' ) ); } /* translators: 1: deleted count, 2: total count */ $message = sprintf( __( 'Deleted %1$d items of %2$d', 'booktics' ), $count, $total ); return $this->response( array(), $message ); } /** * Check if a given request has access to delete a collection of items. * * @param $request * * @return WP_Error */ public function delete_items_permissions_check( $request ) { return current_user_can( 'manage_options' ); } /** * Prepare the item for create or update operation. * * @param $request * * @return WP_Error */ public function prepare_item_for_database( $request ) { $data = json_decode( $request->get_body(), true ); $validate = booktics_validate( $data, array( 'title' => array( 'required', ), ) ); if ( is_wp_error( $validate ) ) { return $validate; } if ( empty( $data['team_member'] ) ) { $admin_query = new \WP_User_Query( array( 'role' => array( 'administrator', 'booktics_team_member' ), ) ); $admins = $admin_query->get_results(); $admin = reset( $admins ); $team_member_id = $admin->id; $data['team_member'] = array( $team_member_id ); } $data['title'] = ! empty( $data['title'] ) ? sanitize_text_field( $data['title'] ) : ''; $data['description'] = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : ''; $data['content'] = ! empty( $data['content'] ) ? sanitize_text_field( $data['content'] ) : ''; $data['link'] = ! empty( $data['link'] ) ? sanitize_text_field( $data['link'] ) : ''; $data['image'] = ! empty( $data['image'] ) ? esc_url_raw( $data['image'] ) : ''; $data['image_id'] = ! empty( $data['image_id'] ) ? esc_url_raw( $data['image_id'] ) : ''; $data['interval_time'] = ! empty( $data['interval_time'] ) ? sanitize_text_field( $data['interval_time'] ) : 0; $data['before_buffer_time'] = ! empty( $data['before_buffer_time'] ) ? sanitize_text_field( $data['before_buffer_time'] ) : 0; $data['after_buffer_time'] = ! empty( $data['after_buffer_time'] ) ? sanitize_text_field( $data['after_buffer_time'] ) : 0; $data['show_end_time'] = ! empty( $data['show_end_time'] ) ? sanitize_text_field( $data['show_end_time'] ) : false; $data['status'] = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : 'deactive'; $data['team_member'] = empty( $data['team_member'] ) ? array() : array_unique( $data['team_member'] ); $data['categories'] = empty( $data['categories'] ) ? array() : array_unique( $data['categories'] ); $sanitized_durations = array(); if ( ! empty( $data['additional_durations'] ) ) { foreach ( $data['additional_durations'] as $duration ) { if ( is_array( $duration ) ) { $sanitized_durations[] = array( 'id' => ! empty( $duration['id'] ) ? sanitize_text_field( $duration['id'] ) : '', 'name' => ! empty( $duration['name'] ) ? sanitize_text_field( $duration['name'] ) : '', 'duration_time' => ! empty( $duration['duration_time'] ) ? sanitize_text_field( $duration['duration_time'] ) : 0, 'duration_unit' => ! empty( $duration['duration_unit'] ) ? sanitize_text_field( $duration['duration_unit'] ) : '', 'charge_amount' => ! empty( $duration['charge_amount'] ) ? (float) number_format( (float) $duration['charge_amount'], 4, '.', '' ) : '0.0000', 'deposit_amount' => ! empty( $duration['deposit_amount'] ) ? (float) number_format( (float) $duration['deposit_amount'], 4, '.', '' ) : '0.0000', ); } } } else { $sanitized_durations = array( array( 'id' => 'default', 'name' => 'default', 'duration_time' => 30, 'duration_unit' => 'min', 'charge_amount' => 0, 'deposit_amount' => 0, ), ); } $data['additional_durations'] = $sanitized_durations; $data = apply_filters( 'booktics_prepare_service_custom_fields', $data ); $image_url = $request->get_param( 'image' ); if ( $image_url ) { $data['image'] = esc_url_raw( $image_url ); } return $data; } /** * Clone service from the record. * * @param $request * * @return WP_HTTP_Response * @throws Exception */ public function clone_item( $request ) { $id = intval( $request['id'] ); $service = Service_Model::find( $id ); if ( ! $service ) { return $this->error( __( 'Service not found', 'booktics' ), 404 ); } $clone_data = [ 'title' => $service->title . __( ' Copy', 'booktics' ), 'description' => $service->description, 'content' => $service->content, 'image' => $service->image, 'image_id' => $service->image_id, 'categories' => $service->categories, 'interval_time' => $service->interval_time, 'before_buffer_time' => $service->before_buffer_time, 'after_buffer_time' => $service->after_buffer_time, 'team_member' => $service->team_member, 'show_end_time' => $service->show_end_time, 'additional_durations' => $service->additional_durations, 'enable_custom_holiday' => $service->enable_custom_holiday, 'enable_custom_schedule' => $service->enable_custom_schedule, 'enable_additional_service_duration' => $service->enable_additional_service_duration, ]; $clone_data = apply_filters( 'booktics_clone_service_data', $clone_data, $service ); $cloned = Service_Model::create( $clone_data ); if ( is_wp_error( $cloned ) ) { return $this->error( $cloned->get_error_message() ); } $schedule_record = ( new Schedule_Record() ) ->get_schedules( $id, 'service' ); $schedule_data['schedule'] = $schedule_record['schedule']; $schedule_data['custom'] = $schedule_record['custom']; $schedule_data['holiday'] = $schedule_record['holiday']; $service_schedule = Schedule_Manager::make( $schedule_data ); $service_schedule->with_service_id( $cloned->id ); $service_schedule->save(); $team_member_details = Service_Helper::team_member_details( $service->team_member ); $cloned = $cloned->to_array(); $cloned['team_member_details'] = $team_member_details; $cloned['category_details'] = Service_Helper::category_details( $cloned['categories'] ); $clone_schedule_record = ( new Schedule_Record() ) ->get_schedules( $cloned['id'], 'service' ); $cloned['schedule'] = empty( $clone_schedule_record['schedule'] ) ? (object) array() : $clone_schedule_record['schedule']; $cloned['custom'] = $clone_schedule_record['custom']; $cloned['holiday'] = $clone_schedule_record['holiday']; $cloned['shortcode'] = '[booktics_service_view id="' . $cloned['id'] . '"]'; $cloned = apply_filters( 'booktics_service_format', $cloned ); return $this->response( $cloned, __( 'Successfully cloned service', 'booktics' ) ); } }