PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.6
Yatra – Travel Booking & Tour Operator Software v3.0.6
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / app / Controllers / CustomerController.php

CustomerController.php in Yatra – Travel Booking & Tour Operator Software 3.0.6, at app/Controllers/CustomerController.php

546 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Controllers;
6
7 use WP_REST_Request;
8 use WP_REST_Response;
9 use WP_Error;
10 use Yatra\Services\CustomerService;
11 use Yatra\Validators\CustomerValidator;
12 use Yatra\Exceptions\ValidationException;
13 use Yatra\Utils\Logger;
14
15 /**
16 * Customer REST API Controller
17 *
18 * Handles HTTP requests only - delegates business logic to CustomerService.
19 *
20 * NO DATABASE QUERIES OR BUSINESS LOGIC IN THIS FILE.
21 *
22 * @package Yatra\Controllers
23 */
24 class CustomerController extends BaseController
25 {
26 /**
27 * Customer service instance
28 */
29 private CustomerService $customerService;
30
31 /**
32 * Constructor
33 */
34 public function __construct()
35 {
36 $this->customerService = new CustomerService();
37 }
38
39 /**
40 * Register routes
41 */
42 public function register_routes(): void
43 {
44 $namespace = 'yatra/v1';
45 $base = 'customers';
46
47 // =====================
48 // CUSTOMER (FRONTEND) ROUTES
49 // =====================
50
51 // Get current customer profile
52 register_rest_route($namespace, '/' . $base . '/me', [
53 [
54 'methods' => \WP_REST_Server::READABLE,
55 'callback' => [$this, 'getMe'],
56 'permission_callback' => [$this, 'checkCustomerPermission'],
57 ],
58 [
59 'methods' => \WP_REST_Server::EDITABLE,
60 'callback' => [$this, 'updateMe'],
61 'permission_callback' => [$this, 'checkCustomerPermission'],
62 ],
63 ]);
64
65 // Current customer's bookings
66 register_rest_route($namespace, '/' . $base . '/my-bookings', [
67 [
68 'methods' => \WP_REST_Server::READABLE,
69 'callback' => [$this, 'getMyBookings'],
70 'permission_callback' => [$this, 'checkCustomerPermission'],
71 ],
72 ]);
73
74 register_rest_route($namespace, '/' . $base . '/my-bookings/(?P<id>\d+)', [
75 [
76 'methods' => \WP_REST_Server::READABLE,
77 'callback' => [$this, 'getMyBooking'],
78 'permission_callback' => [$this, 'checkCustomerPermission'],
79 'args' => [
80 'id' => [
81 'required' => true,
82 'type' => 'integer',
83 'sanitize_callback' => 'absint',
84 ],
85 ],
86 ],
87 ]);
88
89 // Current customer's payments
90 register_rest_route($namespace, '/' . $base . '/my-payments', [
91 [
92 'methods' => \WP_REST_Server::READABLE,
93 'callback' => [$this, 'getMyPayments'],
94 'permission_callback' => [$this, 'checkCustomerPermission'],
95 ],
96 ]);
97
98 // Current customer's documents
99 register_rest_route($namespace, '/' . $base . '/my-documents', [
100 [
101 'methods' => \WP_REST_Server::READABLE,
102 'callback' => [$this, 'getMyDocuments'],
103 'permission_callback' => [$this, 'checkCustomerPermission'],
104 ],
105 ]);
106
107 // Current customer's support tickets
108 register_rest_route($namespace, '/' . $base . '/my-support-tickets', [
109 [
110 'methods' => \WP_REST_Server::READABLE,
111 'callback' => [$this, 'getMySupportTickets'],
112 'permission_callback' => [$this, 'checkCustomerPermission'],
113 ],
114 ]);
115
116 // =====================
117 // ADMIN ROUTES
118 // =====================
119
120 // List + create customers. List gates on view, create gates
121 // on edit (creating a customer is a write).
122 register_rest_route($namespace, '/' . $base, [
123 [
124 'methods' => \WP_REST_Server::READABLE,
125 'callback' => [$this, 'getCustomers'],
126 'permission_callback' => [$this, 'checkCanView'],
127 ],
128 [
129 'methods' => \WP_REST_Server::CREATABLE,
130 'callback' => [$this, 'createCustomer'],
131 'permission_callback' => [$this, 'checkCanEdit'],
132 ],
133 ]);
134
135 // Single customer — read / update / delete with distinct caps.
136 register_rest_route($namespace, '/' . $base . '/(?P<id>\d+)', [
137 [
138 'methods' => \WP_REST_Server::READABLE,
139 'callback' => [$this, 'getCustomer'],
140 'permission_callback' => [$this, 'checkCanView'],
141 ],
142 [
143 'methods' => \WP_REST_Server::EDITABLE,
144 'callback' => [$this, 'updateCustomer'],
145 'permission_callback' => [$this, 'checkCanEdit'],
146 ],
147 [
148 'methods' => \WP_REST_Server::DELETABLE,
149 'callback' => [$this, 'deleteCustomer'],
150 'permission_callback' => [$this, 'checkCanEdit'],
151 ],
152 ]);
153
154 // Customer bookings list — view cap.
155 register_rest_route($namespace, '/' . $base . '/(?P<id>\d+)/bookings', [
156 [
157 'methods' => \WP_REST_Server::READABLE,
158 'callback' => [$this, 'getCustomerBookings'],
159 'permission_callback' => [$this, 'checkCanView'],
160 ],
161 ]);
162
163 // Merge customers — destructive write. Edit cap.
164 register_rest_route($namespace, '/' . $base . '/merge', [
165 [
166 'methods' => \WP_REST_Server::CREATABLE,
167 'callback' => [$this, 'mergeCustomers'],
168 'permission_callback' => [$this, 'checkCanEdit'],
169 ],
170 ]);
171
172 // Customer statistics — view cap.
173 register_rest_route($namespace, '/' . $base . '/stats', [
174 [
175 'methods' => \WP_REST_Server::READABLE,
176 'callback' => [$this, 'getCustomerStats'],
177 'permission_callback' => [$this, 'checkCanView'],
178 ],
179 ]);
180 }
181
182 /**
183 * Check if user is logged in (for /me, /my-bookings etc. — these
184 * are customer-facing endpoints that work for any logged-in WP
185 * user, not just team members).
186 */
187 public function checkCustomerPermission(): bool
188 {
189 return is_user_logged_in();
190 }
191
192 /**
193 * Granular admin-side permission checks. WP administrators pass
194 * every cap via the Team module's admin-fallback filter, so an
195 * explicit `manage_options` check isn't needed here.
196 */
197 public function checkCanView(): bool
198 {
199 return current_user_can('yatra_view_customers');
200 }
201
202 public function checkCanEdit(): bool
203 {
204 return current_user_can('yatra_edit_customers');
205 }
206
207 /**
208 * @deprecated Kept for any external code referencing the old
209 * method name. The previous implementation OR-ed
210 * `yatra_manage_customers` which was never registered anywhere
211 * — that arm has been removed because it was always false in
212 * practice. Routes to view-only — admin users still pass via
213 * the admin-fallback layer.
214 */
215 public function checkAdminPermission(): bool
216 {
217 return $this->checkCanView();
218 }
219
220 // =========================================================================
221 // FRONTEND ENDPOINTS (Current User)
222 // =========================================================================
223
224 /**
225 * GET /customers/me - Get current customer profile
226 */
227 public function getMe(WP_REST_Request $request): WP_REST_Response
228 {
229 $userId = get_current_user_id();
230
231 $profile = $this->customerService->getAccountProfileForUser($userId);
232
233 if ($profile === null) {
234 return new WP_REST_Response([
235 'success' => false,
236 'message' => __('Customer profile not found.', 'yatra'),
237 ], 404);
238 }
239
240 return new WP_REST_Response([
241 'success' => true,
242 'data' => $profile,
243 ]);
244 }
245
246 /**
247 * PUT /customers/me - Update current customer profile
248 */
249 public function updateMe(WP_REST_Request $request)
250 {
251 try {
252 $userId = get_current_user_id();
253 $data = $request->get_json_params();
254
255 Logger::apiRequest('/customers/me', 'PUT', $data);
256
257 $customer = $this->customerService->getCustomerByUserId($userId);
258
259 if (!$customer) {
260 Logger::warning("Customer profile not found for user", ['user_id' => $userId]);
261 return $this->not_found(__('Customer profile not found', 'yatra'));
262 }
263
264 $customerId = (int) $customer['id'];
265
266 // Validate and sanitize input data
267 CustomerValidator::validateUpdate($data, $customerId);
268 $data = CustomerValidator::sanitize($data);
269
270 $result = $this->customerService->updateCustomer($customerId, $data);
271
272 if (!$result['success']) {
273 Logger::warning("Customer profile update failed", ['customer_id' => $customerId, 'user_id' => $userId, 'result' => $result]);
274 return $this->error_response($result['message'] ?? 'Failed to update customer profile', 400);
275 }
276
277 Logger::info("Customer profile updated successfully", ['customer_id' => $customerId, 'user_id' => $userId]);
278 return $this->success_response($result['data']);
279
280 } catch (\Exception $e) {
281 Logger::error("Failed to update customer profile", ['user_id' => $userId ?? 0, 'data' => $data ?? [], 'error' => $e->getMessage()]);
282 return $this->handle_exception($e);
283 }
284 }
285
286 /**
287 * GET /customers/my-bookings - Get current customer's bookings
288 */
289 public function getMyBookings(WP_REST_Request $request): WP_REST_Response
290 {
291 $userId = get_current_user_id();
292
293 // Get bookings by user ID (checks both customer_id and user_id)
294 $bookings = $this->customerService->getBookingsByUserId($userId);
295
296 return new WP_REST_Response([
297 'success' => true,
298 'data' => $bookings,
299 ]);
300 }
301
302 public function getMyBooking(WP_REST_Request $request): WP_REST_Response
303 {
304 $userId = get_current_user_id();
305 $bookingId = (int) $request->get_param('id');
306
307 $booking = $this->customerService->getBookingDetailsForUser($userId, $bookingId);
308
309 if (!$booking) {
310 return new WP_REST_Response([
311 'success' => false,
312 'message' => __('Booking not found.', 'yatra'),
313 ], 404);
314 }
315
316 return new WP_REST_Response([
317 'success' => true,
318 'data' => $booking,
319 ]);
320 }
321
322 /**
323 * GET /customers/my-payments - Get current customer's payments
324 */
325 public function getMyPayments(WP_REST_Request $request): WP_REST_Response
326 {
327 $userId = get_current_user_id();
328
329 $customer = $this->customerService->getCustomerByUserId($userId);
330
331 if ($customer) {
332 $payments = $this->customerService->getCustomerPayments((int) $customer['id']);
333 } else {
334 $payments = $this->customerService->getPaymentsByUserId($userId);
335 }
336
337 return new WP_REST_Response([
338 'success' => true,
339 'data' => $payments,
340 ]);
341 }
342
343 /**
344 * GET /customers/my-documents - Get current customer's documents
345 */
346 public function getMyDocuments(WP_REST_Request $request): WP_REST_Response
347 {
348 $userId = get_current_user_id();
349
350 $customer = $this->customerService->getCustomerByUserId($userId);
351
352 if (!$customer) {
353 $bookings = $this->customerService->getBookingsByUserId($userId, 1000);
354 $documents = $this->customerService->getDocumentsForBookings($bookings, 0);
355
356 return new WP_REST_Response([
357 'success' => true,
358 'data' => is_array($documents) ? $documents : [],
359 ]);
360 }
361
362 $documents = $this->customerService->getCustomerDocuments((int) $customer['id']);
363
364 return new WP_REST_Response([
365 'success' => true,
366 'data' => $documents,
367 ]);
368 }
369
370 /**
371 * GET /customers/my-support-tickets - Get current customer's support tickets
372 */
373 public function getMySupportTickets(WP_REST_Request $request): WP_REST_Response
374 {
375 $userId = get_current_user_id();
376
377 $customer = $this->customerService->getCustomerByUserId($userId);
378
379 if (!$customer) {
380 return new WP_REST_Response([
381 'success' => true,
382 'data' => [],
383 ]);
384 }
385
386 $tickets = $this->customerService->getCustomerSupportTickets((int) $customer['id']);
387
388 return new WP_REST_Response([
389 'success' => true,
390 'data' => $tickets,
391 ]);
392 }
393
394 // =========================================================================
395 // ADMIN ENDPOINTS
396 // =========================================================================
397
398 /**
399 * GET /customers - List all customers
400 */
401 public function getCustomers(WP_REST_Request $request): WP_REST_Response
402 {
403 $filters = [
404 'page' => (int) ($request->get_param('page') ?: 1),
405 'per_page' => (int) ($request->get_param('per_page') ?: 20),
406 'status' => $request->get_param('status') ?: '',
407 'search' => $request->get_param('search') ?: '',
408 'orderby' => $request->get_param('orderby') ?: 'created_at',
409 'order' => $request->get_param('order') ?: 'desc',
410 ];
411
412 $result = $this->customerService->getCustomers($filters);
413
414 return new WP_REST_Response([
415 'success' => true,
416 'data' => $result['data'],
417 'total' => $result['total'],
418 'page' => $result['page'],
419 'per_page' => $result['per_page'],
420 'pages' => $result['pages'] ?? $result['total_pages'] ?? 1,
421 ]);
422 }
423
424 /**
425 * GET /customers/{id} - Get single customer
426 */
427 public function getCustomer(WP_REST_Request $request): WP_REST_Response
428 {
429 $id = (int) $request->get_param('id');
430
431 $customer = $this->customerService->getCustomer($id);
432
433 if (!$customer) {
434 return new WP_REST_Response([
435 'success' => false,
436 'message' => __('Customer not found.', 'yatra'),
437 ], 404);
438 }
439
440 // Return both legacy flat fields and wrapped data for UI compatibility
441 return new WP_REST_Response(array_merge([
442 'success' => true,
443 'data' => $customer,
444 ], is_array($customer) ? $customer : []));
445 }
446
447 /**
448 * POST /customers - Create customer
449 */
450 public function createCustomer(WP_REST_Request $request): WP_REST_Response
451 {
452 $data = $request->get_json_params();
453
454 $result = $this->customerService->createCustomer($data);
455
456 if (!$result['success']) {
457 return new WP_REST_Response($result, 400);
458 }
459
460 return new WP_REST_Response($result, 201);
461 }
462
463 /**
464 * PUT /customers/{id} - Update customer
465 */
466 public function updateCustomer(WP_REST_Request $request): WP_REST_Response
467 {
468 $id = (int) $request->get_param('id');
469 $data = $request->get_json_params();
470
471 $result = $this->customerService->updateCustomer($id, $data);
472
473 if (!$result['success']) {
474 return new WP_REST_Response($result, 400);
475 }
476
477 return new WP_REST_Response($result);
478 }
479
480 /**
481 * DELETE /customers/{id} - Delete customer
482 */
483 public function deleteCustomer(WP_REST_Request $request): WP_REST_Response
484 {
485 $id = (int) $request->get_param('id');
486
487 $result = $this->customerService->deleteCustomer($id);
488
489 if (!$result['success']) {
490 return new WP_REST_Response($result, 400);
491 }
492
493 return new WP_REST_Response($result);
494 }
495
496 /**
497 * GET /customers/{id}/bookings - Get customer's bookings (admin)
498 */
499 public function getCustomerBookings(WP_REST_Request $request): WP_REST_Response
500 {
501 $id = (int) $request->get_param('id');
502 $limit = (int) ($request->get_param('limit') ?: 10);
503
504 $bookings = $this->customerService->getCustomerBookings($id, $limit);
505
506 return new WP_REST_Response([
507 'success' => true,
508 'data' => $bookings,
509 ]);
510 }
511
512 /**
513 * POST /customers/merge - Merge two customers
514 */
515 public function mergeCustomers(WP_REST_Request $request): WP_REST_Response
516 {
517 $data = $request->get_json_params();
518 $sourceId = (int) ($data['source_id'] ?? 0);
519 $targetId = (int) ($data['target_id'] ?? 0);
520
521 if (!$sourceId || !$targetId) {
522 return new WP_REST_Response([
523 'success' => false,
524 'message' => __('Both source and target customer IDs are required.', 'yatra'),
525 ], 400);
526 }
527
528 $result = $this->customerService->mergeCustomers($sourceId, $targetId);
529
530 if (!$result['success']) {
531 return new WP_REST_Response($result, 400);
532 }
533
534 return new WP_REST_Response($result);
535 }
536
537 /**
538 * GET /customers/stats - Get customer statistics
539 */
540 public function getCustomerStats(WP_REST_Request $request): WP_REST_Response
541 {
542 $stats = $this->customerService->getStats();
543 return new WP_REST_Response($stats ?? []);
544 }
545 }
546