diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index f330c91799f6..20144c778bf5 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -145,12 +145,16 @@ public function run(?array $data = null, ?string $group = null, ?string $dbGroup $rules = $this->splitRules($rules); } - $values = strpos($field, '*') !== false - ? array_filter(array_flatten_with_dots($data), static fn ($key) => preg_match( + if (strpos($field, '*') !== false) { + $values = array_filter(array_flatten_with_dots($data), static fn ($key) => preg_match( '/^' . str_replace('\.\*', '\..+', preg_quote($field, '/')) . '$/', $key - ), ARRAY_FILTER_USE_KEY) - : dot_array_search($field, $data); + ), ARRAY_FILTER_USE_KEY); + // if keys not found + $values = $values ?: [$field => null]; + } else { + $values = dot_array_search($field, $data); + } if ($values === []) { // We'll process the values right away if an empty array diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 664a92d5527f..4c63b689a995 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1244,4 +1244,72 @@ public function testPlaceholderReplacementSetMultipleRulesComplexArray() $this->placeholderReplacementResultDetermination(); } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5922 + */ + public function testNestedArrayThrowsException(): void + { + $rule = [ + 'customer_account_number' => [ + 'label' => 'ACCOUNT NUMBER', + 'rules' => 'required|exact_length[5]', + ], + 'debit_amount' => [ + 'label' => 'DEBIT AMOUNT', + 'rules' => 'required|decimal|is_natural_no_zero', + ], + 'beneficiaries_accounts.*.account_number' => [ + 'label' => 'BENEFICIARY ACCOUNT NUMBER', + 'rules' => 'exact_length[5]', + ], + 'beneficiaries_accounts.*.credit_amount' => [ + 'label' => 'CREDIT AMOUNT', + 'rules' => 'required|decimal|is_natural_no_zero', + ], + 'beneficiaries_accounts.*.purpose' => [ + 'label' => 'PURPOSE', + 'rules' => 'required_without[beneficiaries_accounts.*.account_number]|min_length[3]|max_length[255]', + ], + ]; + + $this->validation->setRules($rule); + $data = [ + 'customer_account_number' => 'A_490', + 'debit_amount' => '1500', + 'beneficiaries_accounts' => [], + ]; + $this->validation->run($data); + + $this->assertSame([ + 'beneficiaries_accounts.*.account_number' => 'The BENEFICIARY ACCOUNT NUMBER field must be exactly 5 characters in length.', + 'beneficiaries_accounts.*.credit_amount' => 'The CREDIT AMOUNT field is required.', + 'beneficiaries_accounts.*.purpose' => 'The PURPOSE field is required when BENEFICIARY ACCOUNT NUMBER is not present.', + ], $this->validation->getErrors()); + + $this->validation->reset(); + $this->validation->setRules($rule); + $data = [ + 'customer_account_number' => 'A_490', + 'debit_amount' => '1500', + 'beneficiaries_accounts' => [ + 'account_1' => [ + 'account_number' => 'A_103', + 'credit_amount' => 1000, + 'purpose' => 'Personal', + ], + 'account_2' => [ + 'account_number' => 'A_258', + 'credit_amount' => null, + 'purpose' => 'A', + ], + ], + ]; + $this->validation->run($data); + + $this->assertSame([ + 'beneficiaries_accounts.account_2.credit_amount' => 'The CREDIT AMOUNT field is required.', + 'beneficiaries_accounts.account_2.purpose' => 'The PURPOSE field must be at least 3 characters in length.', + ], $this->validation->getErrors()); + } }