PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.24
Booktics – Appointment Booking Calendar for Service Businesses v1.0.24
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 / cart / controllers / cart-controller.php

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

410 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Cart\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Cart\Cart_Manager;
7 use Booktics\Models\Service_Model;
8 use Booktics\Models\Team_Member_Model;
9 use WP_Error;
10 use WP_REST_Response;
11 use WP_REST_Server;
12
13 class Cart_Controller extends Base_Rest_Controller {
14
15 /**
16 * Endpoint namespace
17 *
18 * @var string
19 */
20 protected $namespace = 'booktics/v1';
21
22 /**
23 * Route name
24 *
25 * @var string
26 */
27 protected $base = 'carts';
28
29 /**
30 * @var Cart_Manager
31 */
32 private $cart_manager;
33
34 /**
35 * Initialize CartController with a new CartManager instance
36 */
37 public function __construct() {
38 $this->cart_manager = new Cart_Manager();
39 }
40
41 /**
42 * Register REST API routes for cart functionality
43 *
44 * @return void
45 */
46 public function register_routes(): void {
47 register_rest_route(
48 $this->namespace,
49 $this->base,
50 array(
51 'methods' => WP_REST_Server::CREATABLE,
52 'callback' => array( $this, 'add_to_cart' ),
53 'permission_callback' => '__return_true',
54 )
55 );
56
57 register_rest_route(
58 $this->namespace,
59 $this->base . '/(?P<uuid>[a-zA-Z0-9-]+)',
60 array(
61 'methods' => WP_REST_Server::DELETABLE,
62 'callback' => array( $this, 'delete_from_cart' ),
63 'permission_callback' => '__return_true',
64 )
65 );
66
67 register_rest_route(
68 $this->namespace,
69 $this->base,
70 array(
71 'methods' => WP_REST_Server::EDITABLE,
72 'callback' => array( $this, 'sync_cart' ),
73 'permission_callback' => array( $this, 'is_user_logged_in' ),
74 )
75 );
76
77 register_rest_route(
78 $this->namespace,
79 $this->base . '/(?P<uuid>[a-zA-Z0-9-]+)',
80 array(
81 'methods' => WP_REST_Server::EDITABLE,
82 'callback' => array( $this, 'edit_cart_item' ),
83 'permission_callback' => '__return_true',
84 )
85 );
86
87 register_rest_route(
88 $this->namespace,
89 $this->base,
90 array(
91 'methods' => WP_REST_Server::READABLE,
92 'callback' => array( $this, 'get_cart_items' ),
93 'permission_callback' => '__return_true',
94 )
95 );
96 }
97
98 /**
99 * Prepare and validate cart item data
100 *
101 * @param array $data
102 *
103 * @return []|array|bool|WP_Error
104 */
105 private function prepare_item_to_save( array $data ) {
106 $validated = booktics_validate(
107 $data, array(
108 'date' => array( 'required' ),
109 'start_time' => array( 'required' ),
110 'service_id' => array( 'required' ),
111 'team_member_id' => array( 'required' ),
112 )
113 );
114
115 if ( is_wp_error( $validated ) ) {
116 return $validated;
117 }
118
119 $item_validation = $this->validate_cart_item( $data );
120 if ( is_wp_error( $item_validation ) ) {
121 return $item_validation;
122 }
123
124 return $item_validation;
125 }
126
127 /**
128 * Add an item to the cart
129 *
130 * @param $request
131 *
132 * @return array|\WP_HTTP_Response
133 */
134 public function add_to_cart( $request ) {
135 $params = json_decode( $request->get_body(), true );
136 $validated = $this->prepare_item_to_save( $params );
137
138 if ( is_wp_error( $validated ) ) {
139 return $this->error(
140 __( 'Cart validation error', 'booktics' ),
141 422,
142 'Validation',
143 $validated->errors
144 );
145 }
146 // Calculate end time from start time and total duration
147 $total_duration_min = $this->calculate_total_duration( $validated );
148 $validated['end_time'] = $this->get_endtime_from_starttime($validated['start_time'], $total_duration_min);
149
150 $response = $this->cart_manager->add_to_cart( $validated );
151 $response['service'] = $this->get_service( $response['service_id'] );
152 $response['team'] = $this->get_team_member( $response['team_member_id'] );
153
154 // Allow booktics-pro to add location details
155 $response = apply_filters( 'booktics_cart_item_response', $response );
156
157 return $this->response( $response, __( 'Item added to cart', 'booktics' ), 201 );
158 }
159
160 /**
161 * Delete an item from the user's cart
162 *
163 * @param $request
164 *
165 * @return array|\WP_HTTP_Response
166 */
167 public function delete_from_cart( $request ) {
168 $uuid = sanitize_text_field( $request->get_param( 'uuid' ) );
169
170 if ( empty( $uuid ) ) {
171 return $this->error(
172 __( 'UUID is required', 'booktics' ),
173 422,
174 'Validation'
175 );
176 }
177
178 $this->cart_manager->remove_cart_item( $uuid );
179
180 return $this->response( array(), __( 'Successfully deleted item from cart.', 'booktics' ) );
181 }
182
183 /**
184 * Synchronize cart session data to database for logged-in user
185 *
186 * @param $request
187 *
188 * @return array|\WP_HTTP_Response
189 */
190 public function sync_cart( $request ) {
191 $user_id = get_current_user_id();
192 $this->cart_manager->sync_session_to_db( $user_id );
193
194 $response = $this->cart_manager->get_cart();
195
196 return $this->response( $response, __( 'Successfully synced cart.', 'booktics' ) );
197 }
198
199 /**
200 * Get all items from the cart
201 *
202 * @return array|\WP_HTTP_Response
203 */
204 public function get_cart_items() {
205 $response = $this->cart_manager->get_cart();
206 $response['items'] = isset( $response['items'] ) ? array_map(
207 function ( $item ) {
208 if ( ! is_array( $item ) ) {
209 $item = (array) $item;
210 }
211
212 $item['service'] = $this->get_service( $item['service_id'] );
213 $item['team'] = $this->get_team_member( $item['team_member_id'] );
214
215 // Allow booktics-pro to add location details
216 $item = apply_filters( 'booktics_cart_item_response', $item );
217
218 return $item;
219 }, $response['items']
220 ) : array();
221
222 return $this->response( $response, __( 'Cart items fetched successfully', 'booktics' ) );
223 }
224
225 /**
226 * Check if the current user is logged in
227 *
228 * @return bool True if user is logged in, false otherwise
229 */
230 public function is_user_logged_in() {
231 return is_user_logged_in();
232 }
233
234 /**
235 * Validate service, team member and price
236 *
237 * @param array $data Cart item data
238 *
239 * @return array|WP_Error Returns true if valid, WP_Error otherwise
240 */
241 private function validate_cart_item( array $data ) {
242 $error = new WP_Error();
243
244 $service = ( new Service_Model() )->find( $data['service_id'] );
245 if ( ! $service ) {
246 $error->add( 'invalid_service', __( 'Invalid service selected', 'booktics' ) );
247 }
248
249 $team_member = ( new Team_Member_Model() )->find( $data['team_member_id'] );
250 if ( empty( $team_member ) ) {
251 $error->add( 'invalid_team_member', __( 'Invalid team member selected', 'booktics' ) );
252 }
253
254 $service_price = 0;
255 $addition_durations = $service->additional_durations;
256 if ( ! empty( $addition_durations ) && isset( $data['additional_duration_id'] ) ) {
257 $addition_durations = array_filter(
258 $addition_durations, function ( $duration ) use ( $data ) {
259 return $duration['id'] === $data['additional_duration_id'];
260 }
261 );
262 $addition_duration = reset( $addition_durations );
263 $service_price = $addition_duration['charge_amount'] ?? 0;
264 }
265 $data['price'] = $service_price;
266
267 return $error->has_errors() ? $error : $data;
268 }
269
270 /**
271 * Edit a cart item
272 *
273 * @param $request
274 *
275 * @return array|\WP_HTTP_Response
276 */
277 public function edit_cart_item( $request ) {
278 $uuid = sanitize_text_field( (string) $request->get_param( 'uuid' ) );
279 $params = json_decode( $request->get_body(), true );
280
281 if ( empty( $uuid ) ) {
282 return $this->error(
283 __( 'UUID is required', 'booktics' ),
284 422,
285 'Validation'
286 );
287 }
288
289 $validated = $this->prepare_item_to_save( $params );
290 if ( is_wp_error( $validated ) ) {
291 return $this->error(
292 __( 'Cart validation error', 'booktics' ),
293 422,
294 'Validation',
295 $validated->errors
296 );
297 }
298
299 // Add UUID to validated data
300 $validated['uuid'] = $uuid;
301
302 // Calculate end time from start time and total duration
303 $total_duration_min = $this->calculate_total_duration( $validated );
304 $validated['end_time'] = $this->get_endtime_from_starttime( $validated['start_time'], $total_duration_min );
305
306 // Update the cart item
307 $updated_item = $this->cart_manager->edit_cart_item( $uuid, $validated );
308
309 if ( empty( $updated_item ) ) {
310 return $this->error(
311 __( 'Cart item not found', 'booktics' ),
312 404,
313 'NotFound'
314 );
315 }
316
317 // Allow booktics-pro to add location details
318 $updated_item = apply_filters( 'booktics_cart_item_response', $updated_item );
319
320 return $this->response(
321 $updated_item,
322 __( 'Successfully updated cart item', 'booktics' )
323 );
324 }
325
326 /**
327 * @param $service_id
328 *
329 * @return array
330 */
331 public function get_service( $service_id ): array {
332 $service = Service_Model::find( $service_id );
333 if ( empty( $service ) ) {
334 return array();
335 }
336
337 return array(
338 'id' => $service->id,
339 'title' => $service->title,
340 );
341 }
342
343 /**
344 * @param $team_member_id
345 *
346 * @return array
347 */
348 public function get_team_member( $team_member_id ): array {
349 $team_member = ( new Team_Member_Model() )->find( $team_member_id );
350 if ( empty( $team_member ) ) {
351 return array();
352 }
353 $team_member = $team_member->to_array();
354
355 return array(
356 'id' => $team_member['id'],
357 'display_name' => $team_member['display_name'] ?? '',
358 'image' => $team_member['image'] ?? '',
359 );
360 }
361
362 /**
363 * Calculate total duration for a cart item (appointment)
364 *
365 * @param array $item Cart item data
366 *
367 * @return int Total duration in minutes
368 */
369 private function calculate_total_duration( $item ) {
370 $service_model = Service_Model::find( $item['service_id'] );
371 if ( ! $service_model ) {
372 return 0;
373 }
374 $service_duration = array_filter(
375 $service_model->additional_durations, function ( $duration ) use ( $item ) {
376 return $item['additional_duration_id'] === $duration['id'];
377 }
378 );
379 if ( empty( $service_duration ) ) {
380 return 0;
381 }
382 $service_duration = reset( $service_duration );
383 $total_duration_min = $service_duration['duration_unit'] === 'min'
384 ? $service_duration['duration_time']
385 : ( $service_duration['duration_time'] * 60 );
386 if ( ! empty( $item['extra_services'] ) ) {
387 $total_duration_min = apply_filters( 'booktics_extra_service_duration', $total_duration_min, $item['extra_services'] );
388 }
389
390 $total_duration_min = apply_filters( 'booktics_calculate_total_duration', $total_duration_min, $item );
391
392 return $total_duration_min;
393 }
394
395 /**
396 * Get end time for appointments from start time and total duration
397 *
398 * @param $start_time start time of the appointment
399 * @param $total_duration_min total duration of the appointment in minutes including service time and extra service time
400 *
401 * @return string end time of the appointment
402 */
403 private function get_endtime_from_starttime( $start_time, $total_duration_min ) {
404 $start_time = strtotime( $start_time );
405 $end_time = strtotime( '+' . $total_duration_min . ' minutes', $start_time );
406
407 return gmdate( 'H:i', $end_time );
408 }
409 }
410