PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / api / Resource / CustomerResource.php

CustomerResource.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at api/Resource/CustomerResource.php

531 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Api\Resource;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Helpers\AddressHelper;
7 use FluentCart\App\Helpers\Status;
8 use FluentCart\App\Models\Customer;
9 use FluentCart\App\Services\Renderer\CheckoutFieldsSchema;
10 use FluentCart\Framework\Database\Orm\Builder;
11 use FluentCart\Framework\Database\Orm\Collection;
12 use FluentCart\Framework\Support\Arr;
13
14 class CustomerResource extends BaseResourceApi
15 {
16 /**
17 * Per-request memo for getCurrentCustomer(). In production every HTTP
18 * request runs in a fresh PHP process, so this lives exactly one
19 * request. Long-running processes that simulate multiple requests
20 * (test suites, CLI) must clear it between simulated requests via
21 * resetCurrentCustomerRuntimeCache() — as a function-static it was
22 * unreachable and leaked the first request's customer into every
23 * subsequent one.
24 *
25 * @var object|null
26 */
27 private static $currentCustomerRuntimeCache = null;
28
29 public static function resetCurrentCustomerRuntimeCache(): void
30 {
31 static::$currentCustomerRuntimeCache = null;
32 }
33
34 public static function getQuery(): Builder
35 {
36 return Customer::query();
37 }
38
39 /**
40 * Get customers based on specified parameters.
41 *
42 * @param array $params Array containing the necessary parameters.
43 * [
44 * "params" => (array) Required.
45 * [
46 * 'search' => (string) Optional.Search Customer.
47 * [
48 * "column name(e.g., first_name|last_name|email|id)" => [
49 * column => "column name(e.g., first_name|last_name|email|id)",
50 * operator => "operator (e.g., like_all|rlike|or_rlike|or_like_all)",
51 * value => "value" ]
52 * ],
53 * 'filters' => (string) Optional.Filters customer.
54 * [
55 * "column name(e.g., first_name|last_name|email)" => [
56 * column => "column name(e.g., first_name|last_name|email)",
57 * operator => "operator (e.g., between|or_between|like_all|in)",
58 * value => "value" ]
59 * ],
60 * 'order_by' => (string) Optional. Column to order by,
61 * 'order_type' => (string) Optional. Order type for sorting (ASC or DESC),
62 * 'per_page' => (int) Optional. Number of items for per page,
63 * 'page' => (int) Optional. Page number for pagination
64 * ]
65 * ]
66 *
67 */
68 public static function get(array $params = [])
69 {
70 $sortBy = Arr::get($params, 'sort_by', 'id');
71 $sortType = Arr::get($params, 'sort_type', 'DESC');
72 $search = Arr::get($params, 'search', '');
73
74 return static::getQuery()->when($search, function ($query) use ($search) {
75 return $query->searchBy($search);
76 })
77 ->applyCustomFilters(Arr::get($params, 'filters', []))
78 ->orderBy(
79 sanitize_sql_orderby($sortBy),
80 sanitize_sql_orderby($sortType))
81 ->paginate(Arr::get($params, 'per_page', 15), ['*'], 'page', Arr::get($params, 'page'));
82 }
83
84 /**
85 * Find customer by ID.
86 *
87 * @param int $id Required. The ID of the customer.
88 * @param array $params Optional. Additional parameters for finding a customer.
89 * [
90 * 'with' => (array) Optional. Relationships name to be eager loaded,
91 * ]
92 *
93 */
94 public static function find($id, $params = [])
95 {
96 $with = Arr::get($params, 'with', []);
97 $customer = Customer::with($with)->find($id);
98 if (!empty($customer) && isset($customer['labels'])) {
99 $customer['selected_labels'] = Collection::make($customer['labels'])->pluck('label_id');
100 }
101
102 return [
103 'customer' => (!empty($customer) ? $customer : null)
104 ];
105 }
106
107 public static function findOrder($id, $params = [])
108 {
109 $customer = Customer::with('orders.filteredOrderItems')->find($id);
110
111 return [
112 'data' => (!empty($customer) ? $customer->orders : null)
113 ];
114 }
115
116 /**
117 * Create a new customer with the given data
118 *
119 * @param array $data Required. Array containing the necessary parameters
120 * [
121 * 'first_name' => (string) Required. The first name of the customer,
122 * 'last_name' => (string) Optional. The last name of the customer,
123 * 'email' => (string) Required. The email of the customer,
124 * 'city' => (string) Optional. The city of the customer,
125 * 'state' => (string) Optional. The state of the customer,
126 * 'postcode' => (string) Optional. The postal code of the customer,
127 * 'country' => (string) Optional. The country of the customer,
128 * 'wp_user' => (string) Optional. Create customer as WP user,
129 * ]
130 * @param array $params Optional. Additional parameters for creating a customer.
131 *
132 */
133 public static function create($data, $params = [])
134 {
135 $email = Arr::get($data, 'email');
136 $data = static::resolveCustomerName($data);
137
138 $data['purchase_value'] = [];
139 $customer = static::getQuery()->firstOrCreate(
140 ['email' => $email],
141 $data
142 );
143
144 if (empty($customer)) {
145 return static::makeErrorResponse([
146 ['code' => 400, 'message' => __('Customer creation failed.', 'fluent-cart')]
147 ]);
148 }
149
150 $isUserAttached = false;
151 $user = get_user_by('email', $email);
152 if ($user) {
153 $customer->update(['user_id' => $user->ID]);
154 $isUserAttached = true;
155 }
156
157 if (Arr::get($data, 'wp_user') === 'yes' && !$isUserAttached) {
158 $isUserCreated = \FluentCart\App\Services\AuthService::createUserFromCustomer($customer);
159 if (is_wp_error($isUserCreated)) {
160 return static::makeErrorResponse([
161 ['code' => 423, 'message' => __('Failed to create user.', 'fluent-cart')]
162 ]);
163 }
164 }
165
166 if ($customer->wasRecentlyCreated) {
167 return static::makeSuccessResponse(
168 $customer,
169 __('Customer created successfully!', 'fluent-cart')
170 );
171 }
172
173 return static::makeErrorResponse([
174 ['code' => 400, 'message' => __('Customer already exists.', 'fluent-cart')]
175 ]);
176
177
178 }
179
180 /**
181 * Update customer with the given data
182 *
183 * @param array $data Required. Array containing the necessary parameters
184 * [
185 * 'first_name' => (string) Required. The first name of the customer,
186 * 'last_name' => (string) Optional. The last name of the customer,
187 * 'email' => (string) Required. The email of the customer,
188 * 'city' => (string) Optional. The city of the customer,
189 * 'state' => (string) Optional. The state of the customer,
190 * 'postcode' => (string) Optional. The postal code of the customer,
191 * 'country' => (string) Optional. The country of the customer,
192 * ]
193 * @param int $id Required. The ID of the customer.
194 * @param array $params Optional. Additional parameters for creating a customer.
195 *
196 */
197 public static function update($data, $id, $params = [])
198 {
199 $customer = static::getQuery()->find($id);
200
201 if ($customer) {
202 $data = static::resolveCustomerName($data);
203
204 if ($customer->user_id != 0) {
205 $data['email'] = $customer->email;
206 $isUserUpdated = static::updateUser($data, $customer->user_id);
207
208 if (is_wp_error($isUserUpdated)) {
209 return static::makeErrorResponse([
210 ['code' => 423, 'message' => __('Failed to update user.', 'fluent-cart')]
211 ]);
212 }
213 }
214 $customer->update($data);
215 $customer->refresh();
216
217 if ($customer) {
218 return static::makeSuccessResponse(
219 $customer,
220 __('Customer updated successfully!', 'fluent-cart')
221 );
222 }
223
224 return static::makeErrorResponse([
225 ['code' => 400, 'message' => __('Customer update failed.', 'fluent-cart')]
226 ]);
227 }
228
229 return static::makeErrorResponse([
230 ['code' => 400, 'message' => __('Customer not found, please reload the page and try again!', 'fluent-cart')]
231 ]);
232 }
233
234 /**
235 * Delete a customer based on the given ID and parameters.
236 *
237 * @param int $id Optional. The ID of the customer.
238 * @param array $params Optional. Additional parameters for deleting multiple customers.
239 * [
240 * 'ids' => (array) Required. The array of customer IDs to be deleted.
241 * ]
242 *
243 */
244 public static function delete($id, $params = [])
245 {
246 $ids = Arr::get($params, 'ids');
247
248 $customers = static::getQuery()->with(['orders'])->whereIn('id', $ids)->get();
249
250 foreach ($customers as $customer) {
251 $customer->orders()->delete();
252 $customer->delete();
253 }
254
255 if ($customer) {
256 return static::makeSuccessResponse(
257 '',
258 __('Selected Customers has been deleted permanently', 'fluent-cart')
259 );
260 }
261
262 return static::makeErrorResponse([
263 ['code' => 400, 'message' => __('Customer update failed.', 'fluent-cart')]
264 ]);
265 }
266
267 /**
268 * Update customer additional information with the given data
269 *
270 * @param array $data Required. Array containing the necessary parameters
271 * [
272 * 'labels' => (array) Required. The id of the labels,
273 * ]
274 * @param int $id Required. The ID of the customer.
275 * @param array $params Optional. Additional parameters for updating a customer info.
276 *
277 */
278 public static function updateAdditionalInfo($data, $id, $params = [])
279 {
280 $customer = static::find($id, ['with' => ['labels']]);
281 $customer = $customer['customer'];
282
283 if ($customer) {
284 $newLabelIds = Arr::get($data, 'labels', []);
285 // Pluck and convert $existingLabelIds to a collection of strings
286 $existingLabelIds = Collection::make($customer['labels'])->pluck('label_id')->map(function ($value) {
287 return (string)$value;
288 });
289
290 if (count($newLabelIds) > 0 || count($existingLabelIds) > 0) {
291 $isUpdated = LabelResource::addLabelToLabelRelationships($customer, [
292 'labelable_id' => $id,
293 'labelable_type' => Customer::class,
294 'new_label_ids' => $newLabelIds,
295 'existing_label_ids' => $existingLabelIds
296 ]);
297
298 if ($isUpdated) {
299 return static::makeSuccessResponse(
300 $isUpdated,
301 __('Customer updated successfully!', 'fluent-cart')
302 );
303 }
304
305 return static::makeErrorResponse([
306 ['code' => 400, 'message' => __('Customer update failed.', 'fluent-cart')]
307 ], 400);
308 }
309
310 return static::makeErrorResponse([
311 ['code' => 400, 'message' => __('Customer does not have any changes to update.', 'fluent-cart')]
312 ], 400);
313 }
314
315 return static::makeErrorResponse([
316 ['code' => 404, 'message' => __('Customer not found, please reload the page and try again!', 'fluent-cart')]
317 ], 404);
318 }
319
320 /**
321 * Update the status of multiple customers with the given parameters.
322 *
323 * @param array $params Optional. Array containing the necessary parameters
324 * [
325 * 'new_status' => (string) Required. The new status to be set for the customers.
326 * 'customer_ids' => (array) Required. Customer IDs whose status will be updated.
327 * ]
328 *
329 */
330 public static function updateStatus($params = [])
331 {
332 $newStatus = Arr::get($params, 'new_status', '');
333
334 if (!$newStatus) {
335 return static::makeErrorResponse([
336 ['code' => 403, 'message' => __('Please select status', 'fluent-cart')]
337 ]);
338 }
339
340 $validStatuses = Status::getEditableCustomerStatuses();
341 if (!isset($validStatuses[$newStatus])) {
342 return static::makeErrorResponse([
343 ['code' => 403, 'message' => __('Provided customer status is not valid', 'fluent-cart')]
344 ]);
345 }
346
347 $customers = static::getQuery()->with(['orders'])->whereIn('id', Arr::get($params, 'customer_ids'))->get();
348
349 foreach ($customers as $customer) {
350 $customer->updateCustomerStatus($newStatus);
351 }
352
353 return static::makeSuccessResponse(
354 '',
355 __('Customer Status has been changed', 'fluent-cart')
356 );
357 }
358
359 /**
360 * Manage customers based on the provided action and customer IDs.
361 *
362 * @param array $params Optional. Array containing the necessary parameters
363 * [
364 * 'action' => (string) Required. The action to be performed on the selected customers.
365 * (e.g., Possible values: 'delete_customers', 'change_customer_status')
366 * 'customer_ids' => (array) Required. Customer IDs whose action will be performed.
367 * ]
368 *
369 */
370 public static function manageCustomer($params = [])
371 {
372
373 $action = Arr::get($params, 'action', '');
374 $customerIds = Arr::get($params, 'customer_ids', []);
375
376 $customerIds = array_map(function ($id) {
377 return (int)$id;
378 }, $customerIds);
379
380
381 $customerIds = array_filter($customerIds);
382
383 if (!$customerIds) {
384 return static::makeErrorResponse([
385 ['code' => 403, 'message' => __('Customers selection is required', 'fluent-cart')]
386 ]);
387 }
388
389 if ($action == 'delete_customers') {
390 return static::delete(null, ['ids' => $customerIds]);
391 }
392
393 if ($action == 'change_customer_status') {
394 return static::updateStatus($params);
395 }
396
397 return static::makeErrorResponse([
398 ['code' => 400, 'message' => __('Selected action is invalid', 'fluent-cart')]
399 ]);
400 }
401
402 public static function getCurrentCustomer(bool $createIfNotExists = false): ?object
403 {
404 if (static::$currentCustomerRuntimeCache !== null) {
405 return static::$currentCustomerRuntimeCache;
406 }
407
408 if (!is_user_logged_in()) {
409 return null;
410 }
411
412 $currentUser = get_user_by('ID', get_current_user_id());
413
414 // Try to get the existing customer
415 $query = Customer::query()->where('user_id', $currentUser->ID)
416 ->orWhere('email', $currentUser->user_email)
417 ->with(['billing_address', 'shipping_address']);
418
419
420 $existingCustomer = $query->first();
421
422 // Return if found
423 if ($existingCustomer) {
424 if ($existingCustomer->user_id != $currentUser->ID) {
425 // Update the user_id if it doesn't match
426 $existingCustomer->user_id = $currentUser->ID;
427 $existingCustomer->save();
428 }
429
430 static::$currentCustomerRuntimeCache = $existingCustomer;
431 return $existingCustomer;
432 }
433
434 if (!$createIfNotExists) {
435 return null;
436 }
437
438 $userId = $currentUser->ID;
439
440 $appRequestData = App::request()->all();
441
442 $customer = Customer::query()->create([
443 'first_name' => $currentUser->first_name,
444 'last_name' => $currentUser->last_name,
445 'email' => $currentUser->user_email,
446 'user_id' => $userId,
447 'country' => Arr::get($appRequestData, 'country', ''),
448 'city' => Arr::get($appRequestData, 'city', ''),
449 'state' => Arr::get($appRequestData, 'state', ''),
450 'postcode' => Arr::get($appRequestData, 'postcode', ''),
451 ]);
452
453 // get customer by id
454 static::$currentCustomerRuntimeCache = static::getQuery()
455 ->where('id', $customer->id)
456 ->with(['billing_address', 'shipping_address'])
457 ->first();
458
459 return static::$currentCustomerRuntimeCache;
460
461 }
462
463 private static function resolveCustomerName(array $data): array
464 {
465 if (CheckoutFieldsSchema::isFullNameRequired()) {
466 $fullName = trim(Arr::get($data, 'full_name', ''));
467 $nameParts = AddressHelper::guessFirstNameAndLastName($fullName);
468 $data['first_name'] = Arr::get($nameParts, 'first_name', '');
469 $data['last_name'] = Arr::get($nameParts, 'last_name', '');
470 } else {
471 $data['first_name'] = trim(Arr::get($data, 'first_name', ''));
472 $data['last_name'] = trim(Arr::get($data, 'last_name', ''));
473 }
474
475 return $data;
476 }
477
478 private static function updateUser($data, $userId)
479 {
480 $firstName = sanitize_text_field(Arr::get($data, 'first_name'));
481 $lastName = sanitize_text_field(Arr::get($data, 'last_name'));
482 $name = trim($firstName . ' ' . $lastName);
483 $email = sanitize_email(Arr::get($data, 'email', ''));
484
485 if (!$name) {
486 return false;
487 }
488
489 $data = array_filter([
490 'ID' => $userId,
491 'first_name' => $firstName,
492 'last_name' => $lastName,
493 'nickname' => $name,
494 'user_nicename' => $name,
495 'display_name' => $name,
496 'user_url' => Arr::get($data, 'user_url'),
497 ]);
498
499 $allowEmailUpdate = current_user_can('manage_options');
500
501 if (!$allowEmailUpdate) {
502 $currentUser = wp_get_current_user();
503 $currentEmail = strtolower($currentUser->user_email);
504
505 $targetUser = get_userdata($userId);
506 $targetEmail = $targetUser ? strtolower($targetUser->user_email) : null;
507
508 // Non-admin: allow only if editing own account
509 if ($currentEmail && $currentEmail === $targetEmail) {
510 $allowEmailUpdate = true;
511 }
512 }
513
514 if ($allowEmailUpdate) {
515 $data['user_email'] = $email;
516 $data['user_login'] = $email;
517 }
518
519 // Update basic user data
520 $result = wp_update_user($data);
521
522 if (is_wp_error($result)) {
523 return $result;
524 }
525
526 return $result;
527
528 }
529
530 }
531