# booktics/1.0.3/trunk/core/service/controllers/service-controller.php

Booktics – Appointment Booking Calendar for Service Businesses, version 1.0.3. 677 lines.

- Page: https://pluginprobe.com/plugins/booktics/1.0.3/code/trunk/core/service/controllers/service-controller.php
- Raw: https://pluginprobe.com/plugins/booktics/1.0.3/raw/trunk/core/service/controllers/service-controller.php
- Modified: 2025-07-10T04:07:52+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booktics/1.0.3/code/trunk/core/service/controllers/service-controller.php#L10-L20`.

```php
<?php

namespace Booktics\Service\Controllers;

use Booktics\Abstracts\Base_Rest_Controller;
use Booktics\Models\Service_Model;
use Booktics\Models\Team_Member_Model;
use Booktics\Services\Schedule_Manager;
use Booktics\Services\Schedule_Record;
use Booktics\Taxonomy\Category;
use Exception;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Server;

/**
 * Service controller
 *
 * @package Booktics/Service
 */
class Service_Controller extends Base_Rest_Controller {
    /**
     * Endpoint namespace
     *
     * @var string
     */
    protected $namespace = 'booktics/v1';

    /**
     * Route name
     *
     * @var string
     */
    protected $base = 'services';

    /**
     * Register routes
     *
     * @return  void
     */
    public function register_routes(): void {
        register_rest_route(
            $this->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<id>[\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<id>[\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<id>[\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<id>[\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'] = $this->team_member_details( $team_member_ids );
        $service['team_member']         = $team_member_ids;

        $category_ids                = is_array( $service['categories'] )
            ? array_unique( $service['categories'] ) : array( $service['categories'] );
        $service['category_details'] = $this->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,
            '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['category_id'] ) ) {
            $args['meta_query'][] = array(
                'key'     => 'categories',
                'value'   => sprintf( 'i:%d;', intval( $request['category_id'] ) ),
                'compare' => 'LIKE',
            );
        }

        if ( ! empty( $request['search'] ) ) {
            $args['s'] = sanitize_text_field( $request['search'] );
        }

        if ( ! empty( $args['meta_query'] ) ) {
            $args['meta_query']['relation'] = 'AND';
        }

        $data = Service_Model::paginate( $per_page, $paged, $args );

        if ( ! $data ) {
            return $this->error( __( 'No service found', 'booktics' ), 404 );
        }

        if ( ! empty( $data['items'] ) ) {
            $enhanced_items = array();
            foreach ( $data['items'] as $service ) {
                $service_array = $service->to_array();
                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'] = $this->team_member_details( $team_member_ids );
                }

                if ( ! empty( $service_array['categories'] ) ) {
                    $category_ids                      = is_array( $service->categories ) ? array_unique( $service->categories ) : array( $service->categories );
                    $service->categories               = $category_ids;
                    $service_array['category_details'] = $this->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 );
    }

    /**
     * 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 ( 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']    = array();
        $service['team_member_details'] = array();
        $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 true;
    }

    /**
     * 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'] ) ) {
            foreach ( $data['team_member'] 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) $id, $team_member_services ) ) {
                    $team_member_services[] = (int) $id;
                    if ( $team_member ) {
                        $team_member->update(
                            array(
                                'services' => $team_member_services,
                            )
                        );
                    }
                }
            }

            // Remove service from other team members

            $other_team_members = ( new Team_Member_Model() )
                ->where( 'services', $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'], $data['team_member'] ) ) {
                        $other_services = $other_team_member['services'];
                        if ( ( $key = array_search( $id, $other_services ) ) !== false ) {
                            unset( $other_services[ $key ] );
                            ( new Team_Member_Model( $other_team_member['id'] ) )->update(
                                array(
                                    'services' => array_values( $other_services ),
                                )
                            );
                        }
                    }
                }
            }
        }

        $input_data = json_decode( $request->get_body(), true );
        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( $request['id'] );
            $service_schedule->save();
        }

        do_action( 'booktics_service_updated', $service, $request );

        $response                        = Service_Model::find( $id )->to_array();
        $response['shortcode']           = '[booktics-service-view id="' . $response['id'] . '"]';
        $response['team_member_details'] = $this->team_member_details( $response['team_member'] );
        if ( ! empty( $response['categories'] ) ) {
            $category_ids                 = is_array( $response['categories'] ) ? $response['categories'] : array( $response['categories'] );
            $response['category_details'] = $this->category_details( $category_ids );
        }
        $response = apply_filters( 'booktics_service_format', $response );

        return $this->response( $response, __( 'Successfully updated service', 'booktics' ) );
    }

    /**
     * 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 true;
    }

    /**
     * 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( (array) $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 = $request['ids'];

        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' ) );
        }

        $message = sprintf( 'Deleted %d items of %d', $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;
        }

        $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'] ) : '';
        $data['before_buffer_time'] = ! empty( $data['before_buffer_time'] ) ? sanitize_text_field( $data['before_buffer_time'] ) : '';
        $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'] ) : '',
                        '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.0000',
                    'deposit_amount' => '0.0000',
                ),
            );
        }
        $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 );
        }

        $cloned = Service_Model::create(
            array(
                '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,
                'custom_field'                       => $service->custom_field,
                'extra_services'                     => $service->extra_services,
            )
        );

        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           = $this->team_member_details( $service->team_member );
        $cloned                        = $cloned->to_array();
        $cloned['team_member_details'] = $team_member_details;

        $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                = apply_filters( 'booktics_service_format', $cloned );

        return $this->response( $cloned, __( 'Successfully cloned service', 'booktics' ) );
    }

    /**
     * Map team member details
     *
     * @param $team_member_ids
     *
     * @return array
     */
    private function team_member_details( $team_member_ids ): array {
        $team_member_details = array();
        foreach ( $team_member_ids as $team_member_id ) {
            $team_member = ( new Team_Member_Model() )->find( $team_member_id );
            if ( $team_member ) {
                $team_member           = $team_member->to_array();
                $team_member_details[] = array(
                    'id'           => $team_member['id'],
                    'display_name' => $team_member['display_name'],
                    'email'        => $team_member['email'],
                    'image'        => $team_member['image'],
                );
            }
        }

        return $team_member_details;
    }

    /**
     * Get category details for given category IDs.
     *
     * @param array|int[] $category_ids
     *
     * @return array
     */
    private function category_details( $category_ids ): array {
        $category_details = array();
        foreach ( $category_ids as $category_id ) {
            $category = get_term( $category_id, ( new Category() )->get_name() );

            if ( $category ) {
                $category           = $category->to_array();
                $category_details[] = array(
                    'id'   => $category['term_id'],
                    'name' => $category['name'],
                );
            }
        }

        return $category_details;
    }
}

```
