diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 1c55c3b515b9..0af5be8166f0 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Validation; +use CodeIgniter\Helpers\Array\ArrayHelper; use Config\Database; use InvalidArgumentException; @@ -443,6 +444,10 @@ public function field_exists( ?string $error = null, ?string $field = null ): bool { + if (strpos($field, '.') !== false) { + return ArrayHelper::dotKeyExists($field, $data); + } + return array_key_exists($field, $data); } } diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 78c13315e3f3..bf671c3bec4f 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Validation\StrictRules; +use CodeIgniter\Helpers\Array\ArrayHelper; use CodeIgniter\Validation\Rules as NonStrictRules; use Config\Database; @@ -419,6 +420,10 @@ public function field_exists( ?string $error = null, ?string $field = null ): bool { + if (strpos($field, '.') !== false) { + return ArrayHelper::dotKeyExists($field, $data); + } + return array_key_exists($field, $data); } } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 63e9d701491c..b03badb9bf43 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -184,7 +184,7 @@ public function run(?array $data = null, ?string $group = null, ?string $dbGroup if ($values === []) { // We'll process the values right away if an empty array - $this->processRules($field, $setup['label'] ?? $field, $values, $rules, $data); + $this->processRules($field, $setup['label'] ?? $field, $values, $rules, $data, $field); continue; } @@ -196,7 +196,7 @@ public function run(?array $data = null, ?string $group = null, ?string $dbGroup } } else { // Process single field - $this->processRules($field, $setup['label'] ?? $field, $values, $rules, $data); + $this->processRules($field, $setup['label'] ?? $field, $values, $rules, $data, $field); } } @@ -325,9 +325,13 @@ protected function processRules( $found = true; - $passed = ($param === false && $rule !== 'field_exists') - ? $set->{$rule}($value, $error) - : $set->{$rule}($value, $param, $data, $error, $field); + if ($rule === 'field_exists') { + $passed = $set->{$rule}($value, $param, $data, $error, $originalField); + } else { + $passed = ($param === false) + ? $set->{$rule}($value, $error) + : $set->{$rule}($value, $param, $data, $error, $field); + } break; } diff --git a/tests/system/Validation/RulesTest.php b/tests/system/Validation/RulesTest.php index 4aa7f29a5532..844daab3f005 100644 --- a/tests/system/Validation/RulesTest.php +++ b/tests/system/Validation/RulesTest.php @@ -47,6 +47,7 @@ class RulesTest extends CIUnitTestCase protected function setUp(): void { parent::setUp(); + $this->validation = new Validation((object) $this->config, Services::renderer()); $this->validation->reset(); } @@ -863,32 +864,93 @@ public function testFieldExists(array $rules, array $data, bool $expected): void public static function provideFieldExists(): iterable { + // Do not use `foo`, because there is a lang file `Foo`, and + // the error message may be messed up. yield from [ - [ - ['foo' => 'field_exists'], - ['foo' => ''], + 'empty string' => [ + ['fiz' => 'field_exists'], + ['fiz' => ''], true, ], - [ - ['foo' => 'field_exists'], - ['foo' => null], + 'null' => [ + ['fiz' => 'field_exists'], + ['fiz' => null], true, ], - [ - ['foo' => 'field_exists'], - ['foo' => false], + 'false' => [ + ['fiz' => 'field_exists'], + ['fiz' => false], true, ], - [ - ['foo' => 'field_exists'], - ['foo' => []], + 'empty array' => [ + ['fiz' => 'field_exists'], + ['fiz' => []], true, ], - [ - ['foo' => 'field_exists'], + 'empty data' => [ + ['fiz' => 'field_exists'], [], false, ], + 'dot array syntax: true' => [ + ['fiz.bar' => 'field_exists'], + [ + 'fiz' => ['bar' => null], + ], + true, + ], + 'dot array syntax: false' => [ + ['fiz.bar' => 'field_exists'], + [], + false, + ], + 'dot array syntax asterisk: true' => [ + ['fiz.*.baz' => 'field_exists'], + [ + 'fiz' => [ + 'bar' => [ + 'baz' => null, + ], + ], + ], + true, + ], + 'dot array syntax asterisk: false' => [ + ['fiz.*.baz' => 'field_exists'], + [ + 'fiz' => [ + 'bar' => [ + 'baz' => null, + ], + 'hoge' => [ + // 'baz' is missing. + ], + ], + ], + false, + ], ]; } + + public function testFieldExistsErrorMessage(): void + { + $this->validation->setRules(['fiz.*.baz' => 'field_exists']); + $data = [ + 'fiz' => [ + 'bar' => [ + 'baz' => null, + ], + 'hoge' => [ + // 'baz' is missing. + ], + ], + ]; + + $this->assertFalse($this->validation->run($data)); + $this->assertSame( + // This errror message is not perfect. + ['fiz.bar.baz' => 'The fiz.*.baz field must exist.'], + $this->validation->getErrors() + ); + } }