PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.27
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.27
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 / ProductVariationResource.php

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

448 lines 20.2 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::get($variant, 'other_info');
100 if (Arr::get($otherInfo, 'payment_type') == 'onetime') {
101 $otherInfo = Arr::only($otherInfo, [
102 'payment_type',
103 'description',
104 'package_slug',
105 'weight',
106 'weight_unit',
107 'length',
108 'width',
109 'height',
110 ]);
111 }
112 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
113 if (Arr::get($otherInfo, 'manage_setup_fee') == 'no') {
114 unset($otherInfo['signup_fee_name']);
115 unset($otherInfo['signup_fee']);
116 unset($otherInfo['setup_fee_per_item']);
117 }
118 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
119 $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
120 Arr::set($otherInfo, 'signup_fee', $signupFee);
121 }
122 }
123
124 // Cast physical attributes in other_info to float
125 foreach (['weight', 'length', 'width', 'height'] as $attr) {
126 if (isset($otherInfo[$attr])) {
127 $otherInfo[$attr] = floatval($otherInfo[$attr]);
128 }
129 }
130
131 $hasSubscription = Arr::get($variant, 'other_info.payment_type') === 'subscription';
132 $isDownloadable = Arr::get($variant, 'downloadable', true);
133 $itemPrice = Arr::get($variant, 'item_price', 1);
134 $comparePrice = Arr::get($variant, 'compare_price');
135 $available = Arr::get($variant, 'available', 0);
136 $stockStatus = Arr::get($variant, 'stock_status', Helper::IN_STOCK);
137 if (Arr::get($variant, 'manage_stock') == 1) {
138 $stockStatus = ($available > 0) ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
139 } else if (Arr::get($variant, 'manage_stock') == 0) {
140 $stockStatus = Helper::IN_STOCK;
141 }
142 $variantData = [
143 'post_id' => Arr::get($variant, 'post_id'),
144 'serial_index' => Arr::get($variant, 'serial_index'),
145 'manage_stock' => Arr::get($variant, 'manage_stock', 0),
146 'total_stock' => Arr::get($variant, 'total_stock'),
147 'available' => Arr::get($variant, 'available'),
148 'committed' => Arr::get($variant, 'committed'),
149 'on_hold' => Arr::get($variant, 'on_hold'),
150 'stock_status' => $stockStatus,
151 'item_price' => Helper::toCent($itemPrice),
152 //'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : Helper::toCent($itemPrice),
153 'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : 0,
154 'item_cost' => Helper::toCent(Arr::get($variant, 'item_cost', 0)),
155 'manage_cost' => Arr::get($variant, 'manage_cost', 'false'),
156 'fulfillment_type' => Arr::get($variant, 'fulfillment_type', 'physical'),
157 'shipping_class' => Arr::get($variant, 'shipping_class') ?: null,
158 'variation_title' => Arr::get($variant, 'variation_title', ''),
159 'other_info' => $otherInfo,
160 'downloadable' => $isDownloadable,
161 'payment_type' => $hasSubscription ? 'subscription' : 'onetime',
162 ];
163
164 $sku = Arr::get($variant, 'sku');
165 if (!empty($sku)) {
166 $variantData['sku'] = $sku;
167 }
168
169 $isCreated = static::getQuery()->create($variantData);
170 if ($isCreated) {
171 ProductDetailResource::update(
172 [],
173 Arr::get($variant, 'detail_id'),
174 ['action' => 'variant_modified']
175 );
176 $media = Arr::get($variant, 'media', []);
177 if (!empty($media)) {
178 static::setImage($media, $isCreated->id);
179 }
180 return static::makeSuccessResponse(
181 $isCreated,
182 __('Pricing has been created', 'fluent-cart')
183 );
184 }
185 return static::makeErrorResponse([
186 ['code' => 400, 'message' => __('Pricing creation failed!', 'fluent-cart')]
187 ]);
188 }
189
190 /**
191 * Update a variant with the given data.
192 *
193 * @param array $data Array containing the necessary parameters.
194 *
195 * $variant = [
196 * 'id' => (int) Required. The variant ID.
197 * 'post_id' => (int) Required. The product ID.
198 * 'variant_title' => (string) Required. The variant title.
199 * 'item_price' => (float) Required. The item price.
200 * 'compare_price' => (float) Required. The compare price.
201 * 'manage_cost' => (string) Optional. Whether to manage costs.
202 * 'item_cost' => (float) Required if manage cost is yes. The item cost.
203 * 'manage_stock' => (string) Required. Whether to manage stock.
204 * 'stock_status' => (string) Required. The stock status.
205 * 'stock' => (int) Required. The stock quantity.
206 * 'media' => (array) Optional. Info of media files for each variant.
207 * 'id' => (string) Required if upload any media. The media ID.
208 * 'url' => (string) Required if upload any media. The media URL.
209 * 'title' => (string) Required if upload any media. The media title.
210 * 'other_info' => (array) Optional. Other information for the variant.
211 * 'payment_type' => (string) Required. The payment type.
212 * 'times' => (string) Required. The number of times.
213 * 'repeat_interval' => (string) Required. The repeat interval unit.
214 * 'signup_fee' => (string) Required. The signup fee.
215 * 'downloadable_files' => (array) Required if downloadable is true.
216 * 'download_limit' => (string) Required. The download limit.
217 * 'download_expiry' => (string) Required. The download expiry.
218 * 'downloadable' => (bool) Optional. Whether the product is downloadable.
219 * 'files' => (array) Optional. Info of downloadable files for each variant.
220 * 'title' => (string) Required if files. The file title.
221 * 'type' => (string) Required if files. The file type.
222 * 'file_name' => (string) Required if files. The file name.
223 * 'file_path' => (string) Required if files. The file path.
224 * 'file_url' => (string) Required if files. The file URL.
225 * 'serial' => (string) Required if files. The file serial
226 * 'product_terms' => (array) Optional. Terms of the product.
227 * 'product-categories' => (array) Required if categories. Product categories.
228 * [0] => (int) Optional. The category ID.
229 * 'product-tags' => (array) Required if tags. Product tags.
230 * [0] => (int) Optional. The tag ID.
231 * 'product-types' => (array) Required if types. Product types.
232 * [0] => (int) Optional. The type ID.
233 * ];
234 */
235 public static function update($variant, $variantId, $params = [])
236 {
237 $variant ??= [];
238 $variantId = Arr::get($variant, 'id');
239 $otherInfo = Arr::get($variant, 'other_info');
240
241
242 // Get existing variation to preserve other_info values
243 $existingVariation = static::getQuery()->find($variantId);
244 $existingOtherInfo = $existingVariation->other_info ?? [];
245
246 if (Arr::get($otherInfo, 'payment_type') == 'onetime') {
247 $otherInfo = Arr::only($otherInfo, [
248 'payment_type',
249 'description',
250 'bundle_child_ids',
251 'package_slug',
252 'weight',
253 'weight_unit',
254 'length',
255 'width',
256 'height',
257 ]);
258 }
259 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
260 if (Arr::get($otherInfo, 'manage_setup_fee') == 'no') {
261 unset($otherInfo['signup_fee_name']);
262 unset($otherInfo['signup_fee']);
263 unset($otherInfo['setup_fee_per_item']);
264 }
265 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
266 $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
267 Arr::set($otherInfo, 'signup_fee', $signupFee);
268 }
269 }
270
271 $otherInfo['is_bundle_product'] = Arr::get($existingOtherInfo, 'is_bundle_product', 'no');
272 $otherInfo['bundle_child_ids'] = Arr::get($existingOtherInfo, 'bundle_child_ids', []);
273
274 // Preserve physical attributes — use new value from other_info if present, else keep existing
275 foreach (['weight', 'length', 'width', 'height'] as $attr) {
276 if (isset($otherInfo[$attr])) {
277 $otherInfo[$attr] = floatval($otherInfo[$attr]);
278 } elseif (isset($existingOtherInfo[$attr])) {
279 $otherInfo[$attr] = $existingOtherInfo[$attr];
280 }
281 }
282
283 $isDownloadable = Arr::get($variant, 'downloadable', true);
284 $itemPrice = Arr::get($variant, 'item_price', 1);
285 $comparePrice = Arr::get($variant, 'compare_price');
286 $available = Arr::get($variant, 'available', 0);
287 $stockStatus = Arr::get($variant, 'stock_status', Helper::IN_STOCK);
288 if (Arr::get($variant, 'manage_stock') == 1) {
289 $stockStatus = ($available > 0) ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
290 } else if (Arr::get($variant, 'manage_stock') == 0) {
291 $stockStatus = Helper::IN_STOCK;
292 }
293
294 $hasSubscription = Arr::get($variant, 'other_info.payment_type') === 'subscription';
295 $variantData = [
296 'post_id' => Arr::get($variant, 'post_id'),
297 'serial_index' => Arr::get($variant, 'serial_index'),
298 'manage_stock' => Arr::get($variant, 'manage_stock', 0),
299 'total_stock' => Arr::get($variant, 'total_stock'),
300 'available' => Arr::get($variant, 'available'),
301 'committed' => Arr::get($variant, 'committed'),
302 'on_hold' => Arr::get($variant, 'on_hold'),
303 'shipping_class' => Arr::get($variant, 'shipping_class') ?: null,
304 'stock_status' => $stockStatus,
305 'item_price' => Helper::toCent($itemPrice),
306 //'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : Helper::toCent($itemPrice),
307 'compare_price' => ($comparePrice !== '' && $comparePrice >= $itemPrice) ? Helper::toCent($comparePrice) : 0,
308 'item_cost' => Helper::toCent(Arr::get($variant, 'item_cost', 0)),
309 'manage_cost' => Arr::get($variant, 'manage_cost', 'false'),
310 'fulfillment_type' => Arr::get($variant, 'fulfillment_type', 'physical'),
311 'variation_title' => Arr::get($variant, 'variation_title', ''),
312 'sku' => Arr::get($variant, 'sku') ?: null,
313 'other_info' => $otherInfo,
314 'downloadable' => $isDownloadable,
315 'payment_type' => $hasSubscription ? 'subscription' : 'onetime',
316 ];
317
318 // $result = ProductVariation::query()->find($variantId)->fill($variantData)->save();
319 $isUpdated = static::getQuery()->find($variantId);
320 $isUpdated->update($variantData);
321 if ($isUpdated) {
322 ProductDetailResource::update(
323 [],
324 Arr::get($params, 'detail_id'),
325 ['action' => 'variant_modified']
326 );
327 $media = Arr::get($variant, 'media', []);
328 if (!empty($media)) {
329 static::setImage($media, $variantId);
330 } else {
331 ProductMetaResource::delete($variantId);
332 }
333
334
335 return static::makeSuccessResponse(
336 $isUpdated,
337 __('Pricing has been updated', 'fluent-cart')
338 );
339 }
340 return static::makeErrorResponse([
341 ['code' => 400, 'message' => __('Pricing creation failed!', 'fluent-cart')]
342 ]);
343 }
344
345
346 /**
347 * Delete a variant and its associated data.
348 *
349 * @param int $variantId The id of the variant to be deleted.
350 * @param array $params Additional parameters for the deletion process.
351 *
352 */
353 public static function delete($variantId, $params = [])
354 {
355 $variant = static::getQuery()
356 ->with('order_items', function ($query) use ($variantId) {
357 return $query->whereHas('order', function ($query) {
358 return $query->search(["status" => ["column" => "status", "operator" => "in", "value" => [Status::ORDER_PROCESSING, Status::ORDER_ON_HOLD]]]);
359 });
360 })
361 ->find($variantId);
362 $variantTitle = $variant->variation_title;
363
364 if (!empty($variant)) {
365 if (count($variant->order_items) > 0) {
366 return static::makeErrorResponse([
367 ['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')]
368 ]);
369 }
370 $variant->media()->delete();
371 // $variant->downloadable_files()->delete();
372 $deletedVariant = $variant->delete();
373 if ($deletedVariant) {
374 fluent_cart_success_log(
375 __('Pricing deleted', 'fluent-cart'),
376 sprintf(
377 /* translators: %s is the pricing title */
378 __('Pricing %s is deleted', 'fluent-cart'), $variantTitle),
379 [
380 'module_name' => 'Product',
381 'module_id' => 0,
382 'module_type' => ProductVariation::class,
383 ]
384 );
385 return static::makeSuccessResponse(
386 '',
387 __('Selected pricing and associated data has been deleted', 'fluent-cart')
388 );
389 }
390
391
392 return static::makeErrorResponse([
393 ['code' => 400, 'message' => __('Pricing deletion failed!', 'fluent-cart')]
394 ]);
395 }
396
397 return static::makeErrorResponse([
398 ['code' => 404, 'message' => __('Pricing not found in database.', 'fluent-cart')]
399 ]);
400
401 }
402
403 public static function setImage($media, $variantId, $params = [])
404 {
405
406 $media ??= [];
407 $exist = ProductMetaResource::find($variantId);
408 if ($exist) {
409 return ProductMetaResource::update($media, $variantId);
410
411 } else {
412 return ProductMetaResource::create($media, ['product_id' => $variantId]);
413 }
414 }
415
416 /**
417 * Update a variant pricing table info with the given data.
418 * @param int $variantId The id of the variant.
419 * @param array $data Array containing the necessary parameters.
420 *
421 * $variant = [
422 * 'description' => (string) Required. The variant description.
423 * ];
424 */
425 public static function updatePricingTable($variant, $variantId, $params = [])
426 {
427
428 $variant ??= [];
429 $description = Arr::get($variant, 'description');
430 $isUpdated = static::getQuery()->find($variantId);
431 $otherInfo = $isUpdated->other_info;
432 $otherInfo['description'] = $description;
433 $isUpdated->update([
434 'other_info' => $otherInfo,
435 ]);
436
437 if ($isUpdated) {
438 return static::makeSuccessResponse(
439 $isUpdated,
440 __('Pricing table has been updated', 'fluent-cart')
441 );
442 }
443 return static::makeErrorResponse([
444 ['code' => 400, 'message' => __('Failed to update pricing table!', 'fluent-cart')]
445 ]);
446 }
447 }
448