PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.16
Booktics – Appointment Booking Calendar for Service Businesses v1.0.16
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 / customer / controllers / customer-controller.php

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

614 lines 20.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\Customer\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Abstracts\Booktics_Database;
7 use Booktics\Abstracts\User_Model;
8 use Booktics\Customer\Handlers\Customer_Event_Handler;
9 use Booktics\Models\Appointment_Model;
10 use Booktics\Models\Customer_Model;
11 use Booktics\Models\Guest_Model;
12 use Booktics\Models\Order_Model;
13 use Booktics\Models\Payment_Model;
14 use WP_Error;
15 use WP_HTTP_Response;
16 use WP_REST_Response;
17 use WP_REST_Server;
18
19 /**
20 * Customer controller
21 *
22 * @package Booktics/Customer
23 */
24 class Customer_Controller extends Base_Rest_Controller {
25
26 /**
27 * Store namespace of api
28 *
29 * @var string
30 */
31 protected $namespace = 'booktics/v1';
32
33 /**
34 * Store base url
35 *
36 * @var string
37 */
38 protected $base = 'customers';
39
40 /**
41 *
42 *
43 * @return void
44 */
45 public function register_routes(): void {
46 register_rest_route(
47 $this->namespace, $this->base, array(
48 array(
49 'methods' => WP_REST_Server::CREATABLE,
50 'callback' => array( $this, 'create_item' ),
51 'permission_callback' => array(
52 $this,
53 'create_customer_permission',
54 ),
55 ),
56 array(
57 'methods' => WP_REST_Server::READABLE,
58 'callback' => array( $this, 'get_items' ),
59 'permission_callback' => array(
60 $this,
61 'get_customer_permission',
62 ),
63 ),
64 array(
65 'methods' => WP_REST_Server::DELETABLE,
66 'callback' => array( $this, 'delete_items' ),
67 'permission_callback' => array(
68 $this,
69 'delete_customer_permission',
70 ),
71 ),
72 )
73 );
74
75 register_rest_route(
76 $this->namespace, $this->base . '/profile', array(
77 array(
78 'methods' => WP_REST_Server::READABLE,
79 'callback' => array( $this, 'get_profile' ),
80 'permission_callback' => array(
81 $this,
82 'customer_profile_permission',
83 ),
84 ),
85 )
86 );
87
88 register_rest_route(
89 $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
90 array(
91 'methods' => WP_REST_Server::EDITABLE,
92 'callback' => array( $this, 'update_item' ),
93 'permission_callback' => array(
94 $this,
95 'update_customer_permission',
96 ),
97 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
98 ),
99 array(
100 'methods' => WP_REST_Server::DELETABLE,
101 'callback' => array( $this, 'delete_item' ),
102 'permission_callback' => array(
103 $this,
104 'delete_customer_permission',
105 ),
106 ),
107 array(
108 'method' => WP_REST_Server::READABLE,
109 'callback' => array( $this, 'get_item' ),
110 'permission_callback' => array(
111 $this,
112 'get_customer_permission',
113 ),
114 ),
115 )
116 );
117
118 register_rest_route(
119 $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)/connect', array(
120 array(
121 'methods' => WP_REST_Server::EDITABLE,
122 'callback' => array( $this, 'connect_customer' ),
123 'permission_callback' => array(
124 $this,
125 'update_customer_permission',
126 ),
127 ),
128 )
129 );
130
131 register_rest_route(
132 $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)/disconnect', array(
133 array(
134 'methods' => WP_REST_Server::EDITABLE,
135 'callback' => array( $this, 'disconnect_customer' ),
136 'permission_callback' => array(
137 $this,
138 'update_customer_permission',
139 ),
140 ),
141 )
142 );
143 }
144
145 /**
146 * Check if a given request has access to create items.
147 *
148 * @param $request Full data about the request.
149 */
150 public function create_customer_permission( $request ) {
151 return current_user_can( 'manage_options' );
152 }
153
154 /**
155 * Prepare and validate customer data
156 *
157 * @param $request
158 *
159 * @return array|bool|WP_Error
160 */
161 protected function prepare_customer_data( $request ) {
162 $input_data = json_decode( $request->get_body(), true );
163
164 $validate = booktics_validate(
165 $input_data, array(
166 'name' => array(
167 'required',
168 ),
169 'email' => array(
170 'required',
171 'unique:' . booktics_get_table_name( 'booktics_guests' ) . ',email',
172 ),
173 )
174 );
175
176 if ( is_wp_error( $validate ) ) {
177 return $validate;
178 }
179
180 return array(
181 'name' => $input_data['name'],
182 'email' => $input_data['email'],
183 'user_login' => User_Model::generate_username( $input_data['email'] ),
184 'phone' => $input_data['phone'],
185 );
186 }
187
188 /**
189 * Store new customer data
190 *
191 * @param $request
192 *
193 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
194 */
195 public function create_item( $request ) {
196 $prepared_data = $this->prepare_customer_data( $request );
197 if ( is_wp_error( $prepared_data ) ) {
198 return $this->error(
199 $prepared_data->get_error_message(),
200 422,
201 'Validation',
202 $prepared_data->errors
203 );
204 }
205
206 $db = new Booktics_Database();
207 $customer = ( new Guest_Model( $db ) )->create( $prepared_data );
208 $image_url = $request->get_param( 'image' );
209 if ( $image_url ) {
210 $customer->update( array( 'image' => esc_url_raw( $image_url ) ) );
211 }
212
213 if ( ! $customer ) {
214 return $this->error( __( 'Customer create error', 'booktics' ), 500 );
215 }
216
217 do_action( 'booktics_customer_created', $customer, $request );
218
219 return $this->response( $customer, __( 'Successfully created customer', 'booktics' ), 201 );
220 }
221
222 /**
223 * Check if a given request has access to get all customer
224 *
225 * @param $request
226 *
227 * @return bool
228 */
229 public function get_customer_permission( $request ) {
230 return true;
231 }
232
233 /**
234 * Get paginated list items for customer
235 *
236 * @param $request
237 *
238 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
239 */
240 public function get_items( $request ) {
241 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
242 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
243 $name = ! empty( $request['name'] ) ? sanitize_text_field( $request['name'] ) : '';
244 $email = ! empty( $request['email'] ) ? sanitize_email( $request['email'] ) : '';
245 $phone = ! empty( $request['phone'] ) ? sanitize_text_field( $request['phone'] ) : '';
246 $user_name = ! empty( $request['user_name'] ) ? sanitize_text_field( $request['user_name'] ) : '';
247 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
248
249 $db = new Booktics_Database();
250 $customers = ( new Guest_Model( $db ) )->query();
251
252 $args = array(
253 'name' => array( 'LIKE', $name ),
254 'email' => array( 'LIKE', $email ),
255 'phone' => array( 'LIKE', $phone ),
256 'user_login' => array( 'LIKE', $user_name ),
257 );
258
259 foreach ( $args as $key => $value ) {
260 if ( $value[1] ) {
261 $customers->where( $key, $value[0], $value[1] );
262 }
263 }
264
265 if ( $search ) {
266 $customers->where( 'name', 'LIKE', $search )
267 ->or_where( 'email', 'LIKE', $search )
268 ->or_where( 'phone', 'LIKE', $search );
269 }
270
271 $customers = $customers->paginate( $paged, $per_page );
272 foreach ( $customers['items'] as &$customer ) {
273 $customer = ( new Guest_Model( $db ) )->with_virtual_attributes( $customer );
274 }
275
276 return $this->response( $customers, __( 'Successfully fetched customer', 'booktics' ) );
277 }
278
279 /**
280 * Checks if a given request has access to delete a team member.
281 *
282 * @param $request
283 *
284 * @return bool
285 */
286 public function delete_customer_permission( $request ) {
287 return current_user_can( 'manage_options' );
288 }
289
290 /**
291 * Delete a customer
292 *
293 * @param $request
294 *
295 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
296 */
297 public function delete_item( $request ) {
298 $id = intval( $request['id'] );
299
300 $db = new Booktics_Database();
301 $order_model = new Order_Model( $db );
302 $orders = $order_model->get( array( 'customer_id' => $id ) );
303 foreach ( $orders['items'] as $order ) {
304 $appointment_model = new Appointment_Model();
305 $appointments = $appointment_model->where( 'order_id', $order->id );
306 foreach ( $appointments as $appointment ) {
307 $appointment->delete();
308 }
309 $payment_model = new Payment_Model( $db );
310 $payments = $payment_model->get( array( 'order_id' => $order->id ) );
311 foreach ( $payments['items'] as $payment ) {
312 ( new Payment_Model( $db ) )->delete( $payment->id );
313 }
314 $order_model->delete( $order->id );
315 }
316
317 $guest_model = ( new Guest_Model( $db ) );
318 $guest = $guest_model->find( array( 'id' => $id ) );
319
320 if ( $guest->wp_user_id ) {
321 $customer = new Customer_Model( $guest->wp_user_id );
322 $customer->delete();
323 }
324
325 $deleted = $guest_model->delete( $id );
326 if ( ! $deleted ) {
327 return $this->error( __( 'Customer delete error', 'booktics' ), 500 );
328 }
329
330 do_action( 'booktics_customers_deleted', $request );
331
332 return $this->response( array(), __( 'Successfully deleted customer', 'booktics' ) );
333 }
334
335 /**
336 * Delete multiple customer
337 *
338 * @param $request
339 *
340 * @return array|WP_Error|WP_HTTP_Response
341 */
342 public function delete_items( $request ) {
343 $ids = ! empty( $request['ids'] ) ? $request['ids'] : array();
344
345 if ( ! $ids ) {
346 return $this->error( __( 'Customer ids cannot be empty', 'booktics' ), 500 );
347 }
348 $count = 0;
349 $db = new Booktics_Database();
350 foreach ( $ids as $id ) {
351 // Delete all orders (and their related appointments and payments) for this customer
352 $order_model = new Order_Model( $db );
353 $orders = $order_model->get( array( 'customer_id' => $id ) );
354 foreach ( $orders['items'] as $order ) {
355 // Delete related appointments
356 $appointment_model = new Appointment_Model();
357 $appointments = $appointment_model->where( 'order_id', $order->id );
358 foreach ( $appointments as $appointment ) {
359 $appointment->delete();
360 }
361 // Delete related payments
362 $payment_model = new Payment_Model( $db );
363 $payments = $payment_model->get( array( 'order_id' => $order->id ) );
364 foreach ( $payments['items'] as $payment ) {
365 ( new Payment_Model( $db ) )->delete( $payment->id );
366 }
367 // Delete the order
368 $order_model->delete( $order->id );
369 }
370 $guest_model = ( new Guest_Model( $db ) );
371 $guest = $guest_model->find( array( 'id' => $id ) );
372 if ( $guest->wp_user_id ) {
373 $customer = new Customer_Model( $guest->wp_user_id );
374 $customer->delete();
375 }
376 if ( $guest_model->delete( $id ) ) {
377 ++$count;
378 }
379 }
380 if ( $count == 0 ) {
381 return $this->error( __( 'Customer delete error', 'booktics' ), 500 );
382 }
383
384 do_action( 'booktics_customers_deleted', $request );
385
386 return $this->response( array(), __( 'Successfully deleted customer', 'booktics' ) );
387 }
388
389 /**
390 * Get single customer
391 *
392 * @param $request
393 *
394 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
395 */
396 public function get_item( $request ) {
397 $id = intval( $request['id'] );
398 $db = new Booktics_Database();
399 $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
400 if ( ! $customer ) {
401 return $this->error( __( 'Customer not found', 'booktics' ), 404 );
402 }
403
404 $orders = ( new Order_Model( $db ) )->get( array( 'customer_id' => $id ) );
405 $customer->orders = array_map(
406 function ( $order ) {
407 $booking_count = ( new Appointment_Model() )
408 ->get_appointments_count_by_order_id( $order->id );
409
410 return array(
411 'date' => $order->created_at ? gmdate( 'j F, Y', strtotime( $order->created_at ) ) : '',
412 'total_booking' => $booking_count,
413 'total_amount' => $order->total,
414 );
415 }, $orders['items']
416 );
417
418 return $this->response( $customer, __( 'Customer found successfully', 'booktics' ) );
419 }
420
421 /**
422 * Checks if a given request has access to update a customer.
423 *
424 * @param $request
425 *
426 * @return bool True if the request has access to update the item, WP_Error object otherwise.
427 */
428 public function update_customer_permission( $request ) {
429 return current_user_can( 'manage_options' );
430 }
431
432 /**
433 * Prepare data for customer update
434 *
435 * @param $request
436 *
437 * @return array|bool|WP_Error
438 */
439 public function prepare_customer_update( $request ) {
440 $input_data = json_decode( $request->get_body(), true );
441
442 $validation_rules = array(
443 'first_name' => array(
444 'required',
445 ),
446 'email' => array(
447 'required',
448 'unique:' . booktics_get_table_name( 'booktics_guests' ) . ',email,' . $request['id'],
449 ),
450 );
451 $update_validation_rules = array();
452 $validated_data = array();
453
454 if ( ! is_array( $input_data ) ) {
455 return new WP_Error(
456 'invalid_input_data',
457 'Invalid data'
458 );
459 }
460
461 foreach ( $input_data as $key => $value ) {
462 $validated_data[ $key ] = $value;
463 }
464
465 $validate = booktics_validate( $input_data, $update_validation_rules );
466
467 if ( is_wp_error( $validate ) ) {
468 return $validate;
469 }
470
471 return $validated_data;
472 }
473
474 /**
475 * @param $request
476 *
477 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
478 */
479 public function update_item( $request ) {
480 if ( ! isset( $request['id'] ) ) {
481 return $this->error( 'Invalid customer id', 400 );
482 }
483 $prepared_data = $this->prepare_customer_update( $request );
484
485 if ( is_wp_error( $prepared_data ) ) {
486 return $this->error( __( 'Customer validation error', 'booktics' ), 422, 'Validation', $prepared_data->errors );
487 }
488
489 $db = new Booktics_Database();
490 $customer = ( new Guest_Model( $db ) )->update( $request['id'], $prepared_data );
491 $image_url = $request->get_param( 'image' );
492 if ( $image_url ) {
493 ( new Guest_Model( $db ) )->update( $request['id'], array( 'image' => esc_url_raw( $image_url ) ) );
494 ( new Guest_Model( $db ) )->update( absint( $request['id'] ), array( 'image' => esc_url_raw( $image_url ) ) );
495 }
496
497 if ( ! $customer ) {
498 return $this->error( __( 'Customer update error', 'booktics' ), 500 );
499 }
500
501 do_action( 'booktics_customer_updated', $customer, $request );
502 $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $request['id'] ) );
503
504 return $this->response( $customer, __( 'Successfully updated customer', 'booktics' ) );
505 }
506
507 /**
508 * Check if a given request has access to get customer profile.
509 *
510 * @param $request
511 *
512 * @return bool
513 */
514 public function customer_profile_permission( $request ) {
515 return true;
516 }
517
518 /**
519 * Get customer profile
520 *
521 * @param $request
522 *
523 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
524 */
525 public function get_profile( $request ) {
526 $user_id = get_current_user_id();
527 $db = new Booktics_Database();
528 $customer = ( new Guest_Model( $db ) )->find( array( 'wp_user_id' => $user_id ) );
529 $args = array(
530 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering customers by meta fields
531 'meta_query' => array(
532 array(
533 'key' => 'customer_id',
534 'value' => $customer->id,
535 'compare' => '=',
536 ),
537 ),
538 );
539
540 $appointments = ( new Appointment_Model() )->all( $args );
541 $customer->appointments = $appointments;
542
543 return $this->response( $customer, __( 'Successfully fetched customer profile', 'booktics' ) );
544 }
545
546 /**
547 * Connect a guest to a customer
548 *
549 * @param $request
550 *
551 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
552 */
553 public function connect_customer( $request ) {
554 $id = intval( $request['id'] );
555 $db = new Booktics_Database();
556 $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
557
558 if ( ! $guest ) {
559 return $this->error( __( 'Customer not found', 'booktics' ), 404 );
560 }
561
562 if ( $guest->wp_user_id ) {
563 return $this->error( __( 'Customer already connected', 'booktics' ), 400 );
564 }
565
566 $customer_model = ( new Customer_Model() );
567 $customer_id = $customer_model->save(
568 array(
569 'user_login' => $guest->user_login,
570 'user_email' => $guest->email,
571 'first_name' => $guest->first_name ?? '',
572 'last_name' => $guest->last_name ?? '',
573 'display_name' => $guest->display_name ?? '',
574 'phone' => $guest->phone ?? '',
575 )
576 );
577
578 if ( is_wp_error( $customer_id ) ) {
579 return $this->error( __( 'Customer create error', 'booktics' ), 500 );
580 }
581 ( new Customer_Event_Handler() )->on_customer_create( $customer_id );
582
583 ( new Guest_Model( $db ) )->update( $id, array( 'wp_user_id' => $customer_id ) );
584 $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
585 return $this->response( $guest, __( 'Successfully connected customer', 'booktics' ) );
586 }
587
588
589 /**
590 * Disconnect a guest from a customer
591 *
592 * @param $request
593 *
594 * @return array|WP_Error|WP_HTTP_Response|WP_REST_Response
595 */
596 public function disconnect_customer( $request ) {
597 $id = intval( $request['id'] );
598 $db = new Booktics_Database();
599 $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
600
601 if ( ! $guest ) {
602 return $this->error( __( 'Customer not found', 'booktics' ), 404 );
603 }
604 if ( $guest->wp_user_id ) {
605 $customer = new Customer_Model( $guest->wp_user_id );
606 $customer->delete();
607 }
608
609 ( new Guest_Model( $db ) )->update( $id, array( 'wp_user_id' => null ) );
610 $guest = ( new Guest_Model( $db ) )->find( array( 'id' => $id ) );
611 return $this->response( $guest, __( 'Successfully disconnected customer', 'booktics' ) );
612 }
613 }
614