Skip to content

Commit 60fbafc

Browse files
Fix default properties generating validation rules when not provided
1 parent ede0f9e commit 60fbafc

File tree

2 files changed

+75
-13
lines changed

2 files changed

+75
-13
lines changed

src/Resolvers/DataValidationRulesResolver.php

+33-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ public function execute(
3434
): array {
3535
$dataClass = $this->dataConfig->getDataClass($class);
3636

37+
$withoutValidationProperties = [];
38+
3739
foreach ($dataClass->properties as $dataProperty) {
3840
$propertyPath = $path->property($dataProperty->inputMappedName ?? $dataProperty->name);
3941

40-
if ($dataProperty->validate === false) {
42+
if ($this->shouldSkipPropertyValidation($dataProperty, $fullPayload, $propertyPath)) {
43+
$withoutValidationProperties[] = $dataProperty->name;
44+
4145
continue;
4246
}
4347

@@ -63,11 +67,33 @@ public function execute(
6367
$dataRules->add($propertyPath, $rules);
6468
}
6569

66-
$this->resolveOverwrittenRules($dataClass, $fullPayload, $path, $dataRules);
70+
$this->resolveOverwrittenRules(
71+
$dataClass,
72+
$fullPayload,
73+
$path,
74+
$dataRules,
75+
$withoutValidationProperties
76+
);
6777

6878
return $dataRules->rules;
6979
}
7080

81+
protected function shouldSkipPropertyValidation(
82+
DataProperty $dataProperty,
83+
array $fullPayload,
84+
ValidationPath $propertyPath,
85+
): bool {
86+
if ($dataProperty->validate === false) {
87+
return true;
88+
}
89+
90+
if ($dataProperty->hasDefaultValue && Arr::has($fullPayload, $propertyPath->get()) === false) {
91+
return true;
92+
}
93+
94+
return false;
95+
}
96+
7197
protected function resolveDataSpecificRules(
7298
DataProperty $dataProperty,
7399
array $fullPayload,
@@ -169,6 +195,7 @@ protected function resolveOverwrittenRules(
169195
array $fullPayload,
170196
ValidationPath $path,
171197
DataRules $dataRules,
198+
array $withoutValidationProperties
172199
): void {
173200
if (! method_exists($class->name, 'rules')) {
174201
return;
@@ -183,6 +210,10 @@ protected function resolveOverwrittenRules(
183210
$overwrittenRules = app()->call([$class->name, 'rules'], ['context' => $validationContext]);
184211

185212
foreach ($overwrittenRules as $key => $rules) {
213+
if (in_array($key, $withoutValidationProperties)) {
214+
continue;
215+
}
216+
186217
$dataRules->add(
187218
$path->property($key),
188219
collect(Arr::wrap($rules))

tests/ValidationTest.php

+42-11
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,15 @@
99
use Illuminate\Support\Facades\Validator as ValidatorFacade;
1010
use Illuminate\Validation\Rules\Enum;
1111
use Illuminate\Validation\Rules\Exists as LaravelExists;
12-
1312
use Illuminate\Validation\ValidationException;
1413
use Illuminate\Validation\Validator;
15-
16-
use function Pest\Laravel\mock;
17-
use function PHPUnit\Framework\assertFalse;
18-
1914
use Spatie\LaravelData\Attributes\DataCollectionOf;
20-
2115
use Spatie\LaravelData\Attributes\MapInputName;
22-
2316
use Spatie\LaravelData\Attributes\MapName;
2417
use Spatie\LaravelData\Attributes\Validation\ArrayType;
25-
2618
use Spatie\LaravelData\Attributes\Validation\Bail;
27-
2819
use Spatie\LaravelData\Attributes\Validation\Exists;
2920
use Spatie\LaravelData\Attributes\Validation\In;
30-
3121
use Spatie\LaravelData\Attributes\Validation\IntegerType;
3222
use Spatie\LaravelData\Attributes\Validation\Max;
3323
use Spatie\LaravelData\Attributes\Validation\Min;
@@ -41,7 +31,6 @@
4131
use Spatie\LaravelData\Data;
4232
use Spatie\LaravelData\DataCollection;
4333
use Spatie\LaravelData\DataPipeline;
44-
4534
use Spatie\LaravelData\DataPipes\AuthorizedDataPipe;
4635
use Spatie\LaravelData\DataPipes\CastPropertiesDataPipe;
4736
use Spatie\LaravelData\DataPipes\DefaultValuesDataPipe;
@@ -71,6 +60,8 @@
7160
use Spatie\LaravelData\Tests\Fakes\SimpleDataWithOverwrittenRules;
7261
use Spatie\LaravelData\Tests\Fakes\Support\FakeInjectable;
7362
use Spatie\LaravelData\Tests\TestSupport\DataValidationAsserter;
63+
use function Pest\Laravel\mock;
64+
use function PHPUnit\Framework\assertFalse;
7465

7566
it('can validate a string', function () {
7667
$dataClass = new class () extends Data {
@@ -2169,3 +2160,43 @@ public static function rules(): array
21692160
expect($validatorToPass->passes())->toBeTrue()
21702161
->and($validatorToFail->passes())->toBeFalse();
21712162
});
2163+
2164+
it('wont validate default values when they are not provided', function () {
2165+
$dataClass = new class () extends Data {
2166+
#[Min(10)]
2167+
public string $default = 'Hello World';
2168+
};
2169+
2170+
DataValidationAsserter::for($dataClass)
2171+
->assertOk([])
2172+
->assertOk(['default' => 'Hi there in this world'])
2173+
->assertErrors(['default' => 'minimal'])
2174+
->assertErrors(['default' => null])
2175+
->assertRules([], payload: [])
2176+
->assertRules([
2177+
'default' => ['required', 'string', 'min:10'],
2178+
], ['default' => 'something']);
2179+
});
2180+
2181+
it('wont validate default values when they are not provided and rules are overwritten', function () {
2182+
$dataClass = new class () extends Data {
2183+
public string $default = 'Hello World';
2184+
2185+
public static function rules(ValidationContext $context): array
2186+
{
2187+
return [
2188+
'default' => ['required', 'string', 'min:10'],
2189+
];
2190+
}
2191+
};
2192+
2193+
DataValidationAsserter::for($dataClass)
2194+
->assertOk([])
2195+
->assertOk(['default' => 'Hi there in this world'])
2196+
->assertErrors(['default' => 'minimal'])
2197+
->assertErrors(['default' => null])
2198+
->assertRules([], payload: [])
2199+
->assertRules([
2200+
'default' => ['required', 'string', 'min:10'],
2201+
], ['default' => 'something']);
2202+
});

0 commit comments

Comments
 (0)