PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
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.6.1, at api/Resource/ProductVariationResource.php

455 lines 20.4 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-types' => (array) Required if types. Product types.
236 * [0] => (int) Optional. The type ID.
237 * ];
238 */
239 public static function update($variant, $variantId, $params = [])
240 {
241 $variant ??= [];
242 $variantId = Arr::get($variant, 'id');
243 $otherInfo = Arr::get($variant, 'other_info');
244
245
246 // Get existing variation to preserve other_info values
247 $existingVariation = static::getQuery()->find($variantId);
248 $existingOtherInfo = $existingVariation->other_info ?? [];
249
250 if (Arr::get($otherInfo, 'payment_type') == 'onetime') {
251 $otherInfo = Arr::only($otherInfo, [
252 'payment_type',
253 'description',
254 'tax_class',
255 'tax_exempt',
256 'tax_inclusion',
257 'bundle_child_ids',
258 'package_slug',
259 'weight',
260 'weight_unit',
261 'length',
262 'width',
263 'height',
264 ]);
265 }
266 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
267 if (Arr::get($otherInfo, 'manage_setup_fee') == 'no') {
268 unset($otherInfo['signup_fee_name']);
269 unset($otherInfo['signup_fee']);
270 unset($otherInfo['setup_fee_per_item']);
271 }
272 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
273 $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
274 Arr::set($otherInfo, 'signup_fee', $signupFee);
275 }
276 }
277
278 $otherInfo['is_bundle_product'] = Arr::get($existingOtherInfo, 'is_bundle_product', 'no');
279 $otherInfo['bundle_child_ids'] = Arr::get($existingOtherInfo, 'bundle_child_ids', []);
280
281 // Preserve physical attributes — use new value from other_info if present, else keep existing
282 foreach (['weight', 'length', 'width', 'height'] as $attr) {
283 if (isset($otherInfo[$attr])) {
284 $otherInfo[$attr] = floatval($otherInfo[$attr]);
285 } elseif (isset($existingOtherInfo[$attr])) {
286 $otherInfo[$attr] = $existingOtherInfo[$attr];
287 }
288 }
289
290 $isDownloadable = Arr::get($variant, 'downloadable', true);
291 $itemPrice = Arr::get($variant, 'item_price', 1);
292 $comparePrice = Arr::get($variant, 'compare_price');
293 $available = Arr::get($variant, 'available', 0);
294 $stockStatus = Arr::get($variant, 'stock_status', Helper::IN_STOCK);
295 if (Arr::get($variant, 'manage_stock') == 1) {
296 $stockStatus = ($available > 0) ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
297 } else if (Arr::get($variant, 'manage_stock') == 0) {
298 $stockStatus = Helper::IN_STOCK;
299 }
300
301 $hasSubscription = Arr::get($variant, 'other_info.payment_type') === 'subscription';
302 $variantData = [
303 'post_id' => Arr::get($variant, 'post_id'),
304 'serial_index' => Arr::get($variant, 'serial_index'),
305 'manage_stock' => Arr::get($variant, 'manage_stock', 0),
306 'total_stock' => Arr::get($variant, 'total_stock'),
307 'available' => Arr::get($variant, 'available'),
308 'committed' => Arr::get($variant, 'committed'),
309 'on_hold' => Arr::get($variant, 'on_hold'),
310 'shipping_class' => Arr::get($variant, 'shipping_class') ?: null,
311 'stock_status' => $stockStatus,
312 'item_price' => Helper::toCent($itemPrice),
313 //'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : Helper::toCent($itemPrice),
314 'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : 0,
315 'item_cost' => Helper::toCent(Arr::get($variant, 'item_cost', 0)),
316 'manage_cost' => Arr::get($variant, 'manage_cost', 'false'),
317 'fulfillment_type' => Arr::get($variant, 'fulfillment_type', 'physical'),
318 'variation_title' => Arr::get($variant, 'variation_title', ''),
319 'sku' => Arr::get($variant, 'sku') ?: null,
320 'other_info' => $otherInfo,
321 'downloadable' => $isDownloadable,
322 'payment_type' => $hasSubscription ? 'subscription' : 'onetime',
323 ];
324
325 // $result = ProductVariation::query()->find($variantId)->fill($variantData)->save();
326 $isUpdated = static::getQuery()->find($variantId);
327 $isUpdated->update($variantData);
328 if ($isUpdated) {
329 ProductDetailResource::update(
330 [],
331 Arr::get($params, 'detail_id'),
332 ['action' => 'variant_modified']
333 );
334 $media = Arr::get($variant, 'media', []);
335 if (!empty($media)) {
336 static::setImage($media, $variantId);
337 } else {
338 ProductMetaResource::delete($variantId);
339 }
340
341
342 return static::makeSuccessResponse(
343 $isUpdated,
344 __('Pricing has been updated', 'fluent-cart')
345 );
346 }
347 return static::makeErrorResponse([
348 ['code' => 400, 'message' => __('Pricing creation failed!', 'fluent-cart')]
349 ]);
350 }
351
352
353 /**
354 * Delete a variant and its associated data.
355 *
356 * @param int $variantId The id of the variant to be deleted.
357 * @param array $params Additional parameters for the deletion process.
358 *
359 */
360 public static function delete($variantId, $params = [])
361 {
362 $variant = static::getQuery()
363 ->with('order_items', function ($query) use ($variantId) {
364 return $query->whereHas('order', function ($query) {
365 return $query->search(["status" => ["column" => "status", "operator" => "in", "value" => [Status::ORDER_PROCESSING, Status::ORDER_ON_HOLD]]]);
366 });
367 })
368 ->find($variantId);
369 $variantTitle = $variant->variation_title;
370
371 if (!empty($variant)) {
372 if (count($variant->order_items) > 0) {
373 return static::makeErrorResponse([
374 ['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')]
375 ]);
376 }
377 $variant->media()->delete();
378 // $variant->downloadable_files()->delete();
379 $deletedVariant = $variant->delete();
380 if ($deletedVariant) {
381 fluent_cart_success_log(
382 __('Pricing deleted', 'fluent-cart'),
383 sprintf(
384 /* translators: %s is the pricing title */
385 __('Pricing %s is deleted', 'fluent-cart'), $variantTitle),
386 [
387 'module_name' => 'Product',
388 'module_id' => 0,
389 'module_type' => ProductVariation::class,
390 ]
391 );
392 return static::makeSuccessResponse(
393 '',
394 __('Selected pricing and associated data has been deleted', 'fluent-cart')
395 );
396 }
397
398
399 return static::makeErrorResponse([
400 ['code' => 400, 'message' => __('Pricing deletion failed!', 'fluent-cart')]
401 ]);
402 }
403
404 return static::makeErrorResponse([
405 ['code' => 404, 'message' => __('Pricing not found in database.', 'fluent-cart')]
406 ]);
407
408 }
409
410 public static function setImage($media, $variantId, $params = [])
411 {
412
413 $media ??= [];
414 $exist = ProductMetaResource::find($variantId);
415 if ($exist) {
416 return ProductMetaResource::update($media, $variantId);
417
418 } else {
419 return ProductMetaResource::create($media, ['product_id' => $variantId]);
420 }
421 }
422
423 /**
424 * Update a variant pricing table info with the given data.
425 * @param int $variantId The id of the variant.
426 * @param array $data Array containing the necessary parameters.
427 *
428 * $variant = [
429 * 'description' => (string) Required. The variant description.
430 * ];
431 */
432 public static function updatePricingTable($variant, $variantId, $params = [])
433 {
434
435 $variant ??= [];
436 $description = Arr::get($variant, 'description');
437 $isUpdated = static::getQuery()->find($variantId);
438 $otherInfo = $isUpdated->other_info;
439 $otherInfo['description'] = $description;
440 $isUpdated->update([
441 'other_info' => $otherInfo,
442 ]);
443
444 if ($isUpdated) {
445 return static::makeSuccessResponse(
446 $isUpdated,
447 __('Pricing table has been updated', 'fluent-cart')
448 );
449 }
450 return static::makeErrorResponse([
451 ['code' => 400, 'message' => __('Failed to update pricing table!', 'fluent-cart')]
452 ]);
453 }
454 }
455