PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.3
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.3
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 trunk All 48 releases
fluent-cart / api / Resource / ProductVariationResource.php

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

457 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 FluentCart\Api\Resource;
4
5 use FluentCart\App\Helpers\Helper;
6 use FluentCart\App\Helpers\Status;
7 use FluentCart\App\Models\ProductDetail;
8 use FluentCart\App\Models\ProductVariation;
9 use FluentCart\Framework\Database\Orm\Builder;
10 use FluentCart\Framework\Support\Arr;
11
12 class ProductVariationResource extends BaseResourceApi
13 {
14
15 public static function getQuery(): Builder
16 {
17 return ProductVariation::query();
18 }
19
20 public static function get(array $params = []): array
21 {
22 $variantIdsForFilter = Arr::get($params, 'variant_ids', []);
23
24 $query = static::getQuery()
25 ->select(Arr::get($params, 'select', '*'))
26 ->whereIn('id', $variantIdsForFilter);
27
28 if (Arr::get($params, 'with_detail')) {
29 $query->with('product_detail');
30 }
31
32 if (Arr::get($params, 'with_product')) {
33 $query->with('product');
34 }
35
36 $variants = $query->orderBy(
37 sanitize_sql_orderby(Arr::get($params, 'order_by', 'ID')) ?: 'ID',
38 sanitize_sql_orderby(Arr::get($params, 'order_type', 'ASC')) ?: 'ASC'
39 )
40 ->get();
41
42 return [
43 'variants' => $variants
44 ];
45 }
46
47 /**
48 * Find variant by its id.
49 *
50 * @param int $variantId The id of the variant.
51 * @param array $data Additional data for finding variant (optional).
52 *
53 */
54 public static function find($variantId, $data = [])
55 {
56 return static::getQuery()->find($variantId);
57 }
58
59 /**
60 * Create a new variant with the given data.
61 *
62 * @param array $data Array containing the necessary parameters.
63 *
64 * $data = [
65 * 'id' => (int) Required. The variant ID.
66 * 'post_id' => (int) Required. The product ID.
67 * 'variant_title' => (string) Required. The variant title.
68 * 'item_price' => (float) Required. The item price.
69 * 'compare_price' => (float) Required. The compare price.
70 * 'manage_cost' => (string) Optional. Whether to manage costs.
71 * 'item_cost' => (float) Required if manage cost is yes. The item cost.
72 * 'manage_stock' => (string) Required. Whether to manage stock.
73 * 'stock_status' => (string) Required. The stock status.
74 * 'stock' => (int) Required. The stock quantity.
75 * 'media' => (array) Optional. Info of media files for each variant.
76 * 'id' => (string) Required if upload any media. The media ID.
77 * 'url' => (string) Required if upload any media. The media URL.
78 * 'title' => (string) Required if upload any media. The media title.
79 * 'other_info' => (array) Optional. Other information for the variant.
80 * 'payment_type' => (string) Required. The payment type.
81 * 'times' => (string) Required. The number of times.
82 * 'repeat_interval' => (string) Required. The repeat interval unit.
83 * 'signup_fee' => (string) Required. The signup fee.
84 * 'downloadable_files' => (array) Required if downloadable is true.
85 * 'download_limit' => (string) Required. The download limit.
86 * 'download_expiry' => (string) Required. The download expiry.
87 * 'downloadable' => (bool) Optional. Whether the product is downloadable.
88 * 'files' => (array) Optional. Info of downloadable files for each variant.
89 * 'title' => (string) Required if files. The file title.
90 * 'type' => (string) Required if files. The file type.
91 * 'file_name' => (string) Required if files. The file name.
92 * 'file_path' => (string) Required if files. The file path.
93 * 'file_url' => (string) Required if files. The file URL.
94 * 'serial' => (string) Required if files. The file serial
95 * ];
96 */
97 public static function create($variant, $params = [])
98 {
99 $otherInfo = Arr::wrap(Arr::get($variant, 'other_info'));
100 $otherInfo['tax_exempt'] = Arr::get($otherInfo, 'tax_exempt', 'no');
101 $otherInfo['tax_class'] = Arr::get($otherInfo, 'tax_class', 'standard');
102
103 if (Arr::get($otherInfo, 'payment_type') == 'onetime') {
104 $otherInfo = Arr::only($otherInfo, [
105 'payment_type',
106 'description',
107 'tax_class',
108 'tax_exempt',
109 'tax_inclusion',
110 'package_slug',
111 'weight',
112 'weight_unit',
113 'length',
114 'width',
115 'height',
116 ]);
117 }
118 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
119 if (Arr::get($otherInfo, 'manage_setup_fee') == 'no') {
120 unset($otherInfo['signup_fee_name']);
121 unset($otherInfo['signup_fee']);
122 unset($otherInfo['setup_fee_per_item']);
123 }
124 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
125 $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
126 Arr::set($otherInfo, 'signup_fee', $signupFee);
127 }
128 }
129
130 // Cast physical attributes in other_info to float
131 foreach (['weight', 'length', 'width', 'height'] as $attr) {
132 if (isset($otherInfo[$attr])) {
133 $otherInfo[$attr] = floatval($otherInfo[$attr]);
134 }
135 }
136
137 $hasSubscription = Arr::get($variant, 'other_info.payment_type') === 'subscription';
138 $isDownloadable = Arr::get($variant, 'downloadable', true);
139 $itemPrice = Arr::get($variant, 'item_price', 1);
140 $comparePrice = Arr::get($variant, 'compare_price');
141 $available = Arr::get($variant, 'available', 0);
142 $stockStatus = Arr::get($variant, 'stock_status', Helper::IN_STOCK);
143 if (Arr::get($variant, 'manage_stock') == 1) {
144 $stockStatus = ($available > 0) ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
145 } else if (Arr::get($variant, 'manage_stock') == 0) {
146 $stockStatus = Helper::IN_STOCK;
147 }
148 $variantData = [
149 'post_id' => Arr::get($variant, 'post_id'),
150 'serial_index' => Arr::get($variant, 'serial_index'),
151 'manage_stock' => Arr::get($variant, 'manage_stock', 0),
152 'total_stock' => Arr::get($variant, 'total_stock'),
153 'available' => Arr::get($variant, 'available'),
154 'committed' => Arr::get($variant, 'committed'),
155 'on_hold' => Arr::get($variant, 'on_hold'),
156 'stock_status' => $stockStatus,
157 'item_price' => Helper::toCent($itemPrice),
158 //'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : Helper::toCent($itemPrice),
159 'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : 0,
160 'item_cost' => Helper::toCent(Arr::get($variant, 'item_cost', 0)),
161 'manage_cost' => Arr::get($variant, 'manage_cost', 'false'),
162 'fulfillment_type' => Arr::get($variant, 'fulfillment_type', 'physical'),
163 'shipping_class' => Arr::get($variant, 'shipping_class') ?: null,
164 'variation_title' => Arr::get($variant, 'variation_title', ''),
165 'other_info' => $otherInfo,
166 'downloadable' => $isDownloadable,
167 'payment_type' => $hasSubscription ? 'subscription' : 'onetime',
168 ];
169
170 $sku = Arr::get($variant, 'sku');
171 if (!empty($sku)) {
172 $variantData['sku'] = $sku;
173 }
174
175 $isCreated = static::getQuery()->create($variantData);
176 if ($isCreated) {
177 ProductDetailResource::update(
178 [],
179 Arr::get($variant, 'detail_id'),
180 ['action' => 'variant_modified']
181 );
182 $media = Arr::get($variant, 'media', []);
183 if (!empty($media)) {
184 static::setImage($media, $isCreated->id);
185 }
186 return static::makeSuccessResponse(
187 $isCreated,
188 __('Pricing has been created', 'fluent-cart')
189 );
190 }
191 return static::makeErrorResponse([
192 ['code' => 400, 'message' => __('Pricing creation failed!', 'fluent-cart')]
193 ]);
194 }
195
196 /**
197 * Update a variant with the given data.
198 *
199 * @param array $data Array containing the necessary parameters.
200 *
201 * $variant = [
202 * 'id' => (int) Required. The variant ID.
203 * 'post_id' => (int) Required. The product ID.
204 * 'variant_title' => (string) Required. The variant title.
205 * 'item_price' => (float) Required. The item price.
206 * 'compare_price' => (float) Required. The compare price.
207 * 'manage_cost' => (string) Optional. Whether to manage costs.
208 * 'item_cost' => (float) Required if manage cost is yes. The item cost.
209 * 'manage_stock' => (string) Required. Whether to manage stock.
210 * 'stock_status' => (string) Required. The stock status.
211 * 'stock' => (int) Required. The stock quantity.
212 * 'media' => (array) Optional. Info of media files for each variant.
213 * 'id' => (string) Required if upload any media. The media ID.
214 * 'url' => (string) Required if upload any media. The media URL.
215 * 'title' => (string) Required if upload any media. The media title.
216 * 'other_info' => (array) Optional. Other information for the variant.
217 * 'payment_type' => (string) Required. The payment type.
218 * 'times' => (string) Required. The number of times.
219 * 'repeat_interval' => (string) Required. The repeat interval unit.
220 * 'signup_fee' => (string) Required. The signup fee.
221 * 'downloadable_files' => (array) Required if downloadable is true.
222 * 'download_limit' => (string) Required. The download limit.
223 * 'download_expiry' => (string) Required. The download expiry.
224 * 'downloadable' => (bool) Optional. Whether the product is downloadable.
225 * 'files' => (array) Optional. Info of downloadable files for each variant.
226 * 'title' => (string) Required if files. The file title.
227 * 'type' => (string) Required if files. The file type.
228 * 'file_name' => (string) Required if files. The file name.
229 * 'file_path' => (string) Required if files. The file path.
230 * 'file_url' => (string) Required if files. The file URL.
231 * 'serial' => (string) Required if files. The file serial
232 * 'product_terms' => (array) Optional. Terms of the product.
233 * 'product-categories' => (array) Required if categories. Product categories.
234 * [0] => (int) Optional. The category ID.
235 * 'product-tags' => (array) Required if tags. Product tags.
236 * [0] => (int) Optional. The tag ID.
237 * 'product-types' => (array) Required if types. Product types.
238 * [0] => (int) Optional. The type ID.
239 * ];
240 */
241 public static function update($variant, $variantId, $params = [])
242 {
243 $variant ??= [];
244 $variantId = Arr::get($variant, 'id');
245 $otherInfo = Arr::get($variant, 'other_info');
246
247
248 // Get existing variation to preserve other_info values
249 $existingVariation = static::getQuery()->find($variantId);
250 $existingOtherInfo = $existingVariation->other_info ?? [];
251
252 if (Arr::get($otherInfo, 'payment_type') == 'onetime') {
253 $otherInfo = Arr::only($otherInfo, [
254 'payment_type',
255 'description',
256 'tax_class',
257 'tax_exempt',
258 'tax_inclusion',
259 'bundle_child_ids',
260 'package_slug',
261 'weight',
262 'weight_unit',
263 'length',
264 'width',
265 'height',
266 ]);
267 }
268 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
269 if (Arr::get($otherInfo, 'manage_setup_fee') == 'no') {
270 unset($otherInfo['signup_fee_name']);
271 unset($otherInfo['signup_fee']);
272 unset($otherInfo['setup_fee_per_item']);
273 }
274 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
275 $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
276 Arr::set($otherInfo, 'signup_fee', $signupFee);
277 }
278 }
279
280 $otherInfo['is_bundle_product'] = Arr::get($existingOtherInfo, 'is_bundle_product', 'no');
281 $otherInfo['bundle_child_ids'] = Arr::get($existingOtherInfo, 'bundle_child_ids', []);
282
283 // Preserve physical attributes — use new value from other_info if present, else keep existing
284 foreach (['weight', 'length', 'width', 'height'] as $attr) {
285 if (isset($otherInfo[$attr])) {
286 $otherInfo[$attr] = floatval($otherInfo[$attr]);
287 } elseif (isset($existingOtherInfo[$attr])) {
288 $otherInfo[$attr] = $existingOtherInfo[$attr];
289 }
290 }
291
292 $isDownloadable = Arr::get($variant, 'downloadable', true);
293 $itemPrice = Arr::get($variant, 'item_price', 1);
294 $comparePrice = Arr::get($variant, 'compare_price');
295 $available = Arr::get($variant, 'available', 0);
296 $stockStatus = Arr::get($variant, 'stock_status', Helper::IN_STOCK);
297 if (Arr::get($variant, 'manage_stock') == 1) {
298 $stockStatus = ($available > 0) ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
299 } else if (Arr::get($variant, 'manage_stock') == 0) {
300 $stockStatus = Helper::IN_STOCK;
301 }
302
303 $hasSubscription = Arr::get($variant, 'other_info.payment_type') === 'subscription';
304 $variantData = [
305 'post_id' => Arr::get($variant, 'post_id'),
306 'serial_index' => Arr::get($variant, 'serial_index'),
307 'manage_stock' => Arr::get($variant, 'manage_stock', 0),
308 'total_stock' => Arr::get($variant, 'total_stock'),
309 'available' => Arr::get($variant, 'available'),
310 'committed' => Arr::get($variant, 'committed'),
311 'on_hold' => Arr::get($variant, 'on_hold'),
312 'shipping_class' => Arr::get($variant, 'shipping_class') ?: null,
313 'stock_status' => $stockStatus,
314 'item_price' => Helper::toCent($itemPrice),
315 //'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : Helper::toCent($itemPrice),
316 'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : 0,
317 'item_cost' => Helper::toCent(Arr::get($variant, 'item_cost', 0)),
318 'manage_cost' => Arr::get($variant, 'manage_cost', 'false'),
319 'fulfillment_type' => Arr::get($variant, 'fulfillment_type', 'physical'),
320 'variation_title' => Arr::get($variant, 'variation_title', ''),
321 'sku' => Arr::get($variant, 'sku') ?: null,
322 'other_info' => $otherInfo,
323 'downloadable' => $isDownloadable,
324 'payment_type' => $hasSubscription ? 'subscription' : 'onetime',
325 ];
326
327 // $result = ProductVariation::query()->find($variantId)->fill($variantData)->save();
328 $isUpdated = static::getQuery()->find($variantId);
329 $isUpdated->update($variantData);
330 if ($isUpdated) {
331 ProductDetailResource::update(
332 [],
333 Arr::get($params, 'detail_id'),
334 ['action' => 'variant_modified']
335 );
336 $media = Arr::get($variant, 'media', []);
337 if (!empty($media)) {
338 static::setImage($media, $variantId);
339 } else {
340 ProductMetaResource::delete($variantId);
341 }
342
343
344 return static::makeSuccessResponse(
345 $isUpdated,
346 __('Pricing has been updated', 'fluent-cart')
347 );
348 }
349 return static::makeErrorResponse([
350 ['code' => 400, 'message' => __('Pricing creation failed!', 'fluent-cart')]
351 ]);
352 }
353
354
355 /**
356 * Delete a variant and its associated data.
357 *
358 * @param int $variantId The id of the variant to be deleted.
359 * @param array $params Additional parameters for the deletion process.
360 *
361 */
362 public static function delete($variantId, $params = [])
363 {
364 $variant = static::getQuery()
365 ->with('order_items', function ($query) use ($variantId) {
366 return $query->whereHas('order', function ($query) {
367 return $query->search(["status" => ["column" => "status", "operator" => "in", "value" => [Status::ORDER_PROCESSING, Status::ORDER_ON_HOLD]]]);
368 });
369 })
370 ->find($variantId);
371 $variantTitle = $variant->variation_title;
372
373 if (!empty($variant)) {
374 if (count($variant->order_items) > 0) {
375 return static::makeErrorResponse([
376 ['code' => 400, 'message' => __('This pricing cannot be deleted at the moment. There are pending orders associated with it. Deleting the pricing will disrupt the order processing and might cause inconvenience to our customers.', 'fluent-cart')]
377 ]);
378 }
379 $variant->media()->delete();
380 // $variant->downloadable_files()->delete();
381 $deletedVariant = $variant->delete();
382 if ($deletedVariant) {
383 fluent_cart_success_log(
384 __('Pricing deleted', 'fluent-cart'),
385 sprintf(
386 /* translators: %s is the pricing title */
387 __('Pricing %s is deleted', 'fluent-cart'), $variantTitle),
388 [
389 'module_name' => 'Product',
390 'module_id' => 0,
391 'module_type' => ProductVariation::class,
392 ]
393 );
394 return static::makeSuccessResponse(
395 '',
396 __('Selected pricing and associated data has been deleted', 'fluent-cart')
397 );
398 }
399
400
401 return static::makeErrorResponse([
402 ['code' => 400, 'message' => __('Pricing deletion failed!', 'fluent-cart')]
403 ]);
404 }
405
406 return static::makeErrorResponse([
407 ['code' => 404, 'message' => __('Pricing not found in database.', 'fluent-cart')]
408 ]);
409
410 }
411
412 public static function setImage($media, $variantId, $params = [])
413 {
414
415 $media ??= [];
416 $exist = ProductMetaResource::find($variantId);
417 if ($exist) {
418 return ProductMetaResource::update($media, $variantId);
419
420 } else {
421 return ProductMetaResource::create($media, ['product_id' => $variantId]);
422 }
423 }
424
425 /**
426 * Update a variant pricing table info with the given data.
427 * @param int $variantId The id of the variant.
428 * @param array $data Array containing the necessary parameters.
429 *
430 * $variant = [
431 * 'description' => (string) Required. The variant description.
432 * ];
433 */
434 public static function updatePricingTable($variant, $variantId, $params = [])
435 {
436
437 $variant ??= [];
438 $description = Arr::get($variant, 'description');
439 $isUpdated = static::getQuery()->find($variantId);
440 $otherInfo = $isUpdated->other_info;
441 $otherInfo['description'] = $description;
442 $isUpdated->update([
443 'other_info' => $otherInfo,
444 ]);
445
446 if ($isUpdated) {
447 return static::makeSuccessResponse(
448 $isUpdated,
449 __('Pricing table has been updated', 'fluent-cart')
450 );
451 }
452 return static::makeErrorResponse([
453 ['code' => 400, 'message' => __('Failed to update pricing table!', 'fluent-cart')]
454 ]);
455 }
456 }
457