PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
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 1.2.0 All 47 releases
fluent-cart / app / Http / Rules / RequiredWhenRule.php

RequiredWhenRule.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Http/Rules/RequiredWhenRule.php

152 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Http\Rules;
4
5 use Closure;
6 use Countable;
7 use FluentCart\Framework\Support\Arr;
8
9 /**
10 * A closure replacement for the `required_if` rule string.
11 *
12 * `Validator::filterRequiredIf()` discards EVERY rule on an attribute whose
13 * `required_if` condition is unmet — `in:`, `numeric`, `min:` and closures
14 * alike — so a rule set that carried both a conditional requirement and value
15 * constraints silently lost the value constraints exactly when the condition
16 * did not apply. A closure carries the same condition without that side
17 * effect: with no `required_if` string left on the attribute,
18 * filterRequiredIf() returns the rule set untouched and the value rules keep
19 * running.
20 *
21 * Closures are also invoked before the presence check in
22 * Validator::validateAttribute(), so this still fires for an attribute the
23 * request never sent — which is what `required_if` did.
24 *
25 * Emptiness and the condition match mirror ValidatesAttributes::
26 * validateRequired() and validateRequiredIf() so the swap is behaviour-for-
27 * behaviour, with one deliberate exception: the wildcard in $otherField is
28 * resolved for every row, where the framework's `/\.\d\./` matched only
29 * single-digit indexes and quietly gave up from row 10 on.
30 *
31 * See dev-docs/framework-update-2.12.6-qa.md findings 5 and 10.
32 */
33 class RequiredWhenRule
34 {
35 /**
36 * Build the rule closure.
37 *
38 * @param string $otherField Dot path of the field the condition reads. May
39 * contain `*`, resolved against the attribute
40 * being validated.
41 * @param string|array $expectedValues Value(s) that make the field required.
42 * @param string $message Error message, already translated.
43 * @return \Closure
44 */
45 public static function make($otherField, $expectedValues, $message): Closure
46 {
47 $expectedValues = (array) $expectedValues;
48
49 return function ($attribute, $value, $rules, $data) use ($otherField, $expectedValues, $message) {
50 $path = static::resolvePath($otherField, $attribute);
51
52 if ($path === null) {
53 return null;
54 }
55
56 $otherValue = Arr::get($data, $path);
57
58 if (!in_array($otherValue, static::matchTypeOf($otherValue, $expectedValues))) {
59 return null;
60 }
61
62 return static::isFilled($value) ? null : $message;
63 };
64 }
65
66 /**
67 * Substitute each `*` in the condition path with the row index the
68 * attribute under validation carries at the same position.
69 *
70 * @param string $path
71 * @param string $attribute
72 * @return string|null Null when the row index cannot be resolved.
73 */
74 protected static function resolvePath($path, $attribute)
75 {
76 if (strpos($path, '*') === false) {
77 return $path;
78 }
79
80 $pathSegments = explode('.', $path);
81 $attributeSegments = explode('.', $attribute);
82
83 foreach ($pathSegments as $index => $segment) {
84 if ($segment !== '*') {
85 continue;
86 }
87
88 $rowIndex = Arr::get($attributeSegments, $index);
89
90 if ($rowIndex === null || !is_numeric($rowIndex)) {
91 return null;
92 }
93
94 $pathSegments[$index] = $rowIndex;
95 }
96
97 return implode('.', $pathSegments);
98 }
99
100 /**
101 * Mirror validateRequiredIf(): compare as booleans when the other field
102 * holds one, so 'true' / 'false' in a rule definition still match.
103 *
104 * @param mixed $otherValue
105 * @param array $expectedValues
106 * @return array
107 */
108 protected static function matchTypeOf($otherValue, array $expectedValues): array
109 {
110 if (!is_bool($otherValue)) {
111 return $expectedValues;
112 }
113
114 return array_map(function ($expected) {
115 if ($expected === 'true') {
116 return true;
117 }
118
119 if ($expected === 'false') {
120 return false;
121 }
122
123 return $expected;
124 }, $expectedValues);
125 }
126
127 /**
128 * Mirror validateRequired(). Public so WhenFilledRule can draw the same
129 * empty/filled line this rule does — the two are complementary halves of one
130 * attribute's validation and must agree on where that line sits.
131 *
132 * @param mixed $value
133 * @return bool
134 */
135 public static function isFilled($value): bool
136 {
137 if (is_null($value)) {
138 return false;
139 }
140
141 if (is_string($value) && trim($value) === '') {
142 return false;
143 }
144
145 if ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
146 return false;
147 }
148
149 return true;
150 }
151 }
152