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