PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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
← All changes | app/Http/Requests/ProductVariationRequest.php +99 -16 1.5.2 → 1.6.5 View file →
@@ -1,8 +1,10 @@
1 1 <?php
2 2
3 3 namespace FluentCart\App\Http\Requests;
4 4
5 +use FluentCart\App\Helpers\Helper;
6 +use FluentCart\App\Http\Rules\RequiredWhenRule;
5 7 use FluentCart\Framework\Foundation\RequestGuard;
6 8 use FluentCart\Framework\Support\Arr;
7 9
8 10 class ProductVariationRequest extends RequestGuard
@@ -73,8 +75,32 @@
73 75 * @return array
74 76 */
75 77 public function rules()
76 78 {
79 + // total_stock / available / committed / on_hold are `INT(11) NULL DEFAULT 0`
80 + // (ProductVariationMigrator), so an empty counter is a legitimate stored state for
81 + // a variation that never tracked stock. ProductEditModel::createOrUpdatePricing()
82 + // posts the variation back exactly as the drawer loaded it, NULLs included, so
83 + // demanding a number unconditionally rejected a plain price edit on such a row.
84 + //
85 + // This cannot be expressed as `nullable|numeric`: Validator::filterExcludeables()
86 + // drops EVERY rule for a falsy value the moment `nullable` is present, which
87 + // would disarm the conditional requirement too. It cannot sit beside a
88 + // `required_if` string either — filterRequiredIf() discarded this closure along
89 + // with every other rule whenever tracking was off, leaving the guard inert. The
90 + // requirement is a RequiredWhenRule closure below for that reason.
91 + $numericWhenProvided = function ($attribute, $value) {
92 + if ($value === null || $value === '') {
93 + return null;
94 + }
95 +
96 + if (!is_numeric($value)) {
97 + return esc_html__('Stock quantity must be a number.', 'fluent-cart');
98 + }
99 +
100 + return null;
101 + };
102 +
77 103 return [
78 104 'variants.variation_title' => 'required|sanitizeText|maxLength:200',
79 105 'variants.sku' => 'nullable|sanitizeText|maxLength:30|unique:fct_product_variations,sku' . ($this->get('variants.id') ? ',' . $this->get('variants.id') : ''),
80 106 'variants.item_price' => 'nullable|numeric|min:0',
@@ -92,9 +118,15 @@
92 118 return null;
93 119 },
94 120 ],
95 121 'variants.manage_cost' => 'nullable|sanitizeText|maxLength:10',
96 - 'variants.item_cost' => 'required_if:variants.manage_cost,true',
122 + 'variants.item_cost' => [
123 + RequiredWhenRule::make(
124 + 'variants.manage_cost',
125 + 'true',
126 + esc_html__('Item cost is required.', 'fluent-cart')
127 + ),
128 + ],
97 129 'variants.fulfillment_type' => 'required|sanitizeText|maxLength:100',
98 130 'variants.shipping_class' => function ($attr, $value) {
99 131 if ($value && !(\FluentCart\App\Models\ShippingClass::find(intval($value)))) {
100 132 return __('The selected shipping class does not exist.', 'fluent-cart');
@@ -102,11 +134,35 @@
102 134 return null;
103 135 },
104 136
105 137 'variants.manage_stock' => 'nullable|numeric',
106 - 'variants.stock_status' => 'required_if:variants.manage_stock,1|sanitizeText|maxLength:50',
107 - 'variants.total_stock' => 'required|numeric',
108 - 'variants.available' => 'required|numeric',
138 + 'variants.stock_status' => [
139 + RequiredWhenRule::make(
140 + 'variants.manage_stock',
141 + '1',
142 + esc_html__('Stock status is required.', 'fluent-cart')
143 + ),
144 + 'sanitizeText',
145 + 'maxLength:50',
146 + ],
147 + // Quantities are only demanded once tracking is actually on, mirroring
148 + // stock_status directly above.
149 + 'variants.total_stock' => [
150 + RequiredWhenRule::make(
151 + 'variants.manage_stock',
152 + '1',
153 + esc_html__('Stock quantity is required when inventory tracking is on.', 'fluent-cart')
154 + ),
155 + $numericWhenProvided,
156 + ],
157 + 'variants.available' => [
158 + RequiredWhenRule::make(
159 + 'variants.manage_stock',
160 + '1',
161 + esc_html__('Available quantity is required when inventory tracking is on.', 'fluent-cart')
162 + ),
163 + $numericWhenProvided,
164 + ],
109 165 // 'variants.available' => [
110 166 // 'required',
111 167 // 'numeric',
112 168 // function ($attribute, $value, $fail) {
@@ -115,10 +171,12 @@
115 171 // }
116 172 // return null;
117 173 // },
118 174 // ],
119 - 'variants.committed' => 'required|numeric',
120 - 'variants.on_hold' => 'required|numeric',
175 + // Ledger columns the merchant never edits — they are maintained by the stock
176 + // listeners, so they only have to be a number when the payload carries one.
177 + 'variants.committed' => [$numericWhenProvided],
178 + 'variants.on_hold' => [$numericWhenProvided],
121 179
122 180 'variants.serial_index' => 'nullable|numeric',
123 181
124 182 'variants.other_info' => 'required|array',
@@ -131,9 +189,9 @@
131 189 }
132 190 if (!empty($value) && !is_numeric($value)) {
133 191 return __('Times must be a number.', 'fluent-cart');
134 192 }
135 - return null;
193 + return Helper::installmentTimesError($this->get('variants.other_info'));
136 194 },
137 195 ],
138 196 'variants.other_info.trial_days' => [
139 197 function ($attribute, $value) {
@@ -148,13 +206,43 @@
148 206 }
149 207 return null;
150 208 },
151 209 ],
152 - 'variants.other_info.repeat_interval' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
210 + 'variants.other_info.repeat_interval' => [
211 + RequiredWhenRule::make(
212 + 'variants.other_info.payment_type',
213 + 'subscription',
214 + esc_html__('Interval is required.', 'fluent-cart')
215 + ),
216 + 'sanitizeText',
217 + 'maxLength:100',
218 + ],
153 219 'variants.other_info.billing_summary' => 'nullable|sanitizeTextArea|maxLength:255',
154 - 'variants.other_info.manage_setup_fee' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
155 - 'variants.other_info.signup_fee' => 'required_if:variants.other_info.manage_setup_fee,yes',
156 - 'variants.other_info.signup_fee_name' => 'required_if:variants.other_info.manage_setup_fee,yes|sanitizeText|maxLength:100',
220 + 'variants.other_info.manage_setup_fee' => [
221 + RequiredWhenRule::make(
222 + 'variants.other_info.payment_type',
223 + 'subscription',
224 + esc_html__('Setup Fee option is required.', 'fluent-cart')
225 + ),
226 + 'sanitizeText',
227 + 'maxLength:100',
228 + ],
229 + 'variants.other_info.signup_fee' => [
230 + RequiredWhenRule::make(
231 + 'variants.other_info.manage_setup_fee',
232 + 'yes',
233 + esc_html__('Setup Fee Amount is required.', 'fluent-cart')
234 + ),
235 + ],
236 + 'variants.other_info.signup_fee_name' => [
237 + RequiredWhenRule::make(
238 + 'variants.other_info.manage_setup_fee',
239 + 'yes',
240 + esc_html__('Setup Fee Name is required.', 'fluent-cart')
241 + ),
242 + 'sanitizeText',
243 + 'maxLength:100',
244 + ],
157 245 'variants.other_info.package_slug' => 'nullable|sanitizeText|maxLength:100',
158 246 'variants.other_info.weight' => 'nullable|numeric',
159 247 'variants.other_info.weight_unit' => 'nullable|sanitizeText|maxLength:10',
160 248 'variants.other_info.length' => 'nullable|numeric',
@@ -203,18 +291,13 @@
203 291 'variants.sku.unique' => esc_html__('The SKU must be unique.', 'fluent-cart'),
204 292 'variants.item_price.required' => esc_html__('Price is required.', 'fluent-cart'),
205 293 'variants.item_price.numeric' => esc_html__('Price must be a number.', 'fluent-cart'),
206 294 'variants.item_price.min' => esc_html__('Price must be a positive number greater than 0.', 'fluent-cart'),
207 - 'variants.stock_status.required_if' => esc_html__('Stock status is required.', 'fluent-cart'),
208 - 'variants.item_cost.required_if' => esc_html__('Item cost is required.', 'fluent-cart'),
209 295 'variants.fulfillment_type.required' => esc_html__('Fulfilment Type is required.', 'fluent-cart'),
210 296
211 297 'variants.other_info.description.max' => esc_html__('Description may not be greater than 255 characters.', 'fluent-cart'),
212 298 'variants.other_info.payment_type.required' => esc_html__('Payment Type is required.', 'fluent-cart'),
213 299 'variants.other_info.times.required_if' => esc_html__('Times is required.', 'fluent-cart'),
214 - 'variants.other_info.repeat_interval.required_if' => esc_html__('Interval is required.', 'fluent-cart'),
215 - 'variants.other_info.signup_fee.required_if' => esc_html__('Setup Fee Amount is required.', 'fluent-cart'),
216 - 'variants.other_info.signup_fee_name.required_if' => esc_html__('Setup Fee Name is required.', 'fluent-cart'),
217 300 'variants.other_info.trial_days.numeric' => esc_html__('Trial days must be a number.', 'fluent-cart'),
218 301 'variants.other_info.trial_days.max' => esc_html__('Trial period cannot exceed 365 days.', 'fluent-cart'),
219 302 ];
220 303 }