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

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

225 lines 9.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\Events\StockChanged;
6 use FluentCart\App\Helpers\Helper;
7 use FluentCart\App\Helpers\ProductAdminHelper;
8 use FluentCart\App\Models\ProductDetail;
9 use FluentCart\Framework\Database\Orm\Builder;
10 use FluentCart\Framework\Support\Arr;
11
12 class ProductDetailResource extends BaseResourceApi
13 {
14
15 public static function getQuery(): Builder
16 {
17 return ProductDetail::query();
18 }
19
20 public static function get(array $params = [])
21 {
22 //
23 }
24
25 /**
26 * Find product detail by its id.
27 *
28 * @param int $id The id of the product detail.
29 * @param array $data Additional data for finding product (optional).
30 *
31 */
32 public static function find($id, $data = [])
33 {
34 return static::getQuery()->find($id);
35 }
36
37 /**
38 * Create a new product detail with the given data.
39 *
40 * @param array $data Array containing the necessary parameters.
41 *
42 * $data = [
43 * 'post_id' => (int) Required. The product ID.
44 * 'fulfillment_type' => (string) Required. The fulfillment type default:physical.
45 * 'variation_type' => (string) Required. The variation type default:simple.
46 * 'manage_stock' => (int) Required. The manage stock default:1.
47 * ];
48 */
49 public static function create($data, $params = [])
50 {
51 $isCreated = static::getQuery()->create($data);
52
53 if ($isCreated) {
54 return static::makeSuccessResponse(
55 $isCreated,
56 __('Product has been created successfully', 'fluent-cart')
57 );
58 }
59
60 return static::makeErrorResponse([
61 ['code' => 400, 'message' => __('Product creation failed!', 'fluent-cart')]
62 ]);
63 }
64
65 /**
66 * Update a product detail with the given data.
67 * @param int $id The id of the product detail to be updated.
68 * @param array $data Array containing the necessary parameters.
69 *
70 * $data = [
71 * 'id' => (int) Required. The detail id.
72 * 'post_id' => (int) Required. The product ID.
73 * 'fulfillment_type' => (string) Required. The fulfillment type.
74 * 'variation_type' => (string) Required. The variation type.
75 * 'default_variation_id' => (int) Required. The default variation ID.
76 * 'manage_stock' => (int) Required. The manage stock default:1.
77 * ];
78 * @param array $params Additional parameters for the update process.
79 * $params = [
80 * 'action' => (string) Required. This param will help to update detail based on the specific action i.e: variant_modified(Triggers when variant modified which covers all mutations), change_variation_type(Triggers when variation type will change).
81 * ];
82 */
83 public static function update($data, $id, $params = [])
84 {
85 $data ??= [];
86
87 if (!$id) {
88 return static::makeErrorResponse([
89 ['code' => 403, 'message' => __('Please edit a valid product!', 'fluent-cart')]
90 ]);
91 }
92
93 $detail = static::getQuery()->find($id);
94
95 if (!$detail) {
96 return static::makeErrorResponse([
97 ['code' => 404, 'message' => __('Product not found, please reload the page and try again!', 'fluent-cart')]
98 ]);
99 }
100
101 $triggeredAction = Arr::get($params, 'action');
102
103 // Advanced Variations is terminal: once a product uses it, variation_type
104 // can never be changed to Simple / Simple Variations — the attribute
105 // config and generated combinations are the product's source of truth and
106 // a downgrade would orphan them. Guarded on ANY update path that writes
107 // variation_type (not just the change_variation_type action) — the full
108 // product save also sends variation_type and would otherwise bypass this —
109 // and for any API client, not just the disabled admin dropdown. Only an
110 // actual downgrade is blocked: re-saving the same advanced type, or an
111 // update that omits variation_type, passes through untouched.
112 if (
113 Arr::has($data, 'variation_type')
114 && $detail->variation_type === Helper::PRODUCT_TYPE_ADVANCE_VARIATION
115 && Arr::get($data, 'variation_type') !== Helper::PRODUCT_TYPE_ADVANCE_VARIATION
116 ) {
117 return static::makeErrorResponse([
118 ['code' => 422, 'message' => __('A product using Advanced Variations cannot be switched back to Simple or Simple Variations.', 'fluent-cart')]
119 ]);
120 }
121
122 // Stock & Price Range Handling
123 if ($triggeredAction === 'variant_modified') {
124 $manageStock = Arr::has($data, 'manage_stock') ? Arr::get($data, 'manage_stock') : $detail->manage_stock;
125
126 if (!$manageStock) {
127 $data['stock_availability'] = Helper::IN_STOCK;
128 } else {
129 $hasInStock = \FluentCart\App\Models\ProductVariation::query()
130 ->where('post_id', $detail->post_id)
131 ->where('stock_status', 'in-stock')
132 ->exists();
133 $data['stock_availability'] = $hasInStock ? Helper::IN_STOCK : Helper::OUT_OF_STOCK;
134 }
135 }
136
137 if ($triggeredAction === 'change_variation_type' && Arr::get($data, 'variation_type') === 'simple') {
138 $variationIds = Arr::get($data, 'variation_ids', []);
139 if (!empty($detail->post_id) && count($variationIds) > 0) {
140 ProductAdminHelper::deleteOrphanVariant(
141 $detail->post_id,
142 $variationIds,
143 __("the product variation type was changed to 'Simple'", 'fluent-cart')
144 );
145 }
146 }
147
148 // Switching INTO Advanced Variations (from Simple or Simple Variations)
149 // deletes the existing variants now. They have no place in an
150 // attribute-based product (the merchant builds fresh combinations from
151 // attribute options), and Advanced Variations is terminal so there is
152 // nothing to preserve them for — matching the destructive admin confirm
153 // ("delete all current variations ... cannot be undone") and the editor
154 // clearing them client-side. An empty keep-list deletes every variant for
155 // the product; an unconfigured advanced product is hidden on the
156 // storefront until the merchant generates combinations, so the empty
157 // variant set never leaks. Keyed on the non-advanced -> advanced
158 // transition itself, NOT the change_variation_type action, so the side
159 // effect is identical on every write path that sets variation_type — the
160 // dedicated detail endpoint AND the full pricing save (which calls update()
161 // with action=variant_modified). Otherwise a full save or API client could
162 // land a product on Advanced Variations without the deletion, leaving
163 // inconsistent variant state. Mirrors the downgrade guard above.
164 if (
165 Arr::get($data, 'variation_type') === Helper::PRODUCT_TYPE_ADVANCE_VARIATION
166 && $detail->variation_type !== Helper::PRODUCT_TYPE_ADVANCE_VARIATION
167 && !empty($detail->post_id)
168 ) {
169 ProductAdminHelper::deleteOrphanVariant(
170 $detail->post_id,
171 [],
172 __("the product variation type was changed to 'Advanced Variations'", 'fluent-cart')
173 );
174 }
175
176 $data['min_price'] = Arr::get($data, 'min_price') ?: ($detail->min_price ?? 0);
177 $data['max_price'] = Arr::get($data, 'max_price') ?: ($detail->max_price ?? 0);
178
179 // Handle Default Variation
180 if (empty(Arr::get($data, 'default_variation_id'))) {
181 $data['default_variation_id'] = NULL;
182 }
183
184 // Handle other_info merge
185 if (Arr::has($data, 'other_info')) {
186 $existingOtherInfo = $detail->other_info ?? [];
187 $newOtherInfo = Arr::get($data, 'other_info', []);
188
189 // Merge existing with new data (new data overwrites existing)
190 $mergedOtherInfo = array_merge($existingOtherInfo, $newOtherInfo);
191
192 // Handle subscription-specific logic
193 if (Arr::get($mergedOtherInfo, 'payment_type') == 'subscription' && Arr::get($mergedOtherInfo, 'manage_setup_fee') == 'yes') {
194 $signupFee = Helper::toCent(floatval(Arr::get($mergedOtherInfo, 'signup_fee', 0)));
195 $mergedOtherInfo['signup_fee'] = $signupFee;
196 }
197
198 $data['other_info'] = $mergedOtherInfo;
199 }
200
201 $isUpdated = $detail->update($data);
202
203 if ($isUpdated) {
204 return static::makeSuccessResponse($isUpdated, __('Product pricing has been changed!', 'fluent-cart'));
205 }
206
207 return static::makeErrorResponse([
208 ['code' => 400, 'message' => __('Product update failed.', 'fluent-cart')]
209 ]);
210 }
211
212 /**
213 * Delete product detail and its associated data.
214 *
215 * @param int $id The id of the product detail to be deleted.
216 * @param array $params Additional parameters for the deletion process.
217 *
218 */
219 public static function delete($id, $params = [])
220 {
221 //
222 }
223
224 }
225