From 74d9f7ef82c451e5514a178bdfbd9031fa251d42 Mon Sep 17 00:00:00 2001 From: Markus Heinemann Date: Tue, 21 Oct 2025 13:06:50 +0200 Subject: [PATCH] allow precognitive requests to use wildcards (#57437) --- .../Http/Concerns/CanBePrecognitive.php | 15 ++++- .../Integration/Routing/PrecognitionTest.php | 61 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Http/Concerns/CanBePrecognitive.php b/src/Illuminate/Http/Concerns/CanBePrecognitive.php index 78aceaaa363e..0298774ed015 100644 --- a/src/Illuminate/Http/Concerns/CanBePrecognitive.php +++ b/src/Illuminate/Http/Concerns/CanBePrecognitive.php @@ -18,9 +18,20 @@ public function filterPrecognitiveRules($rules) return $rules; } + $validateOnly = explode(',', $this->header('Precognition-Validate-Only')); + $validateOnlyPatterns = (new Collection($validateOnly)) + ->map(fn ($pattern) => '/^'.str_replace('\*', '\d+', preg_quote($pattern, '/')).'$/'); + return (new Collection($rules)) - ->only(explode(',', $this->header('Precognition-Validate-Only'))) - ->all(); + ->filter(function ($rule, $ruleKey) use ($validateOnlyPatterns) { + foreach ($validateOnlyPatterns as $pattern) { + if (preg_match($pattern, $ruleKey)) { + return true; + } + } + + return false; + })->all(); } /** diff --git a/tests/Integration/Routing/PrecognitionTest.php b/tests/Integration/Routing/PrecognitionTest.php index a87ca82b9180..b1e8bde20ebf 100644 --- a/tests/Integration/Routing/PrecognitionTest.php +++ b/tests/Integration/Routing/PrecognitionTest.php @@ -456,6 +456,51 @@ public function testClientCanSpecifyInputsToValidateWhenUsingControllerValidateW ]); } + public function testClientCanSpecifySimpleArrayWildcardToValidateWhenUsingControllerValidateWithPassingArrayOfRules() + { + Route::post('test-route', [PrecognitionTestController::class, 'methodWhereArrayRulesAreValidateViaControllerValidate']) + ->middleware(PrecognitionInvokingController::class); + + $response = $this->postJson('test-route', [ + 'raw_array' => [123, 'someString'], + ], [ + 'Precognition' => 'true', + 'Precognition-Validate-Only' => 'raw_array.*', + ]); + + $response->assertUnprocessable(); + $response->assertHeaderMissing('Precognition-Success'); + $response->assertJsonPath('errors', [ + 'raw_array.0' => [ + 'The raw_array.0 field must be a string.', + ], + ]); + } + + public function testClientCanSpecifyComplexArrayWildcardToValidateWhenUsingControllerValidateWithPassingArrayOfRules() + { + Route::post('test-route', [PrecognitionTestController::class, 'methodWhereArrayRulesAreValidateViaControllerValidate']) + ->middleware(PrecognitionInvokingController::class); + + $response = $this->postJson('test-route', [ + 'nested_array' => [ + ['name' => 123], + ['name' => 'someString'], + ], + ], [ + 'Precognition' => 'true', + 'Precognition-Validate-Only' => 'nested_array.*.name', + ]); + + $response->assertUnprocessable(); + $response->assertHeaderMissing('Precognition-Success'); + $response->assertJsonPath('errors', [ + 'nested_array.0.name' => [ + 'The nested_array.0.name field must be a string.', + ], + ]); + } + public function testItAppendsAnAdditionalVaryHeaderInsteadOfReplacingAnyExistingVaryHeaders() { Route::get('test-route', function () { @@ -1117,6 +1162,22 @@ public function methodWhereNestedRulesAreValidatedViaControllerValidate(Request fail(); } + public function methodWhereArrayRulesAreValidateViaControllerValidate(Request $request) + { + precognitive(function () use ($request) { + $this->validate($request, [ + 'nested_array' => ['required', 'array', 'min:1'], + 'nested_array.*.name' => ['required', 'string'], + 'raw_array' => ['required', 'array', 'min:1'], + 'raw_array.*' => ['required', 'string'], + ]); + + fail(); + }); + + fail(); + } + public function methodWhereNestedRulesAreValidatedViaControllerValidateWith(Request $request) { precognitive(function () {