PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.20
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.20
1.6.6 1.6.5 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 All 49 releases
fluent-cart / api / Resource / CustomerResource.php

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

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