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

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