diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index e3e256b099b4..17d3ced44e44 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -429,15 +429,22 @@ public function required_without( $fieldData = dot_array_search($otherField, $data); $fieldSplitArray = explode('.', $field); - $fieldKey = $fieldSplitArray[1]; + $fieldKey = $fieldSplitArray[1] ?? null; if (is_array($fieldData)) { - return ! empty(dot_array_search($otherField, $data)[$fieldKey]); + if (empty($fieldData[$fieldKey])) { + return false; + } + + continue; } - $nowField = str_replace('*', $fieldKey, $otherField); + + $nowField = str_replace('*', (string) $fieldKey, $otherField); $nowFieldVaule = dot_array_search($nowField, $data); - return null !== $nowFieldVaule; + if ($nowFieldVaule === null) { + return false; + } } } diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 7e0f4411f079..7cf70deaeb27 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1854,6 +1854,51 @@ public function testRequireWithoutWithAsterisk(): void ); } + /** + * Test that `required_without` checks all fields in dot-notation when there are multiple fields. + */ + public function testRequireWithoutMultipleWithAsterisk(): void + { + $data = [ + 'a' => [ + ['b' => 1, 'c' => 2, 'd' => ''], + ['b' => 1, 'c' => '', 'd' => ''], + ], + ]; + + $this->validation->setRules([ + 'a.*.d' => 'required_without[a.*.b,a.*.c]', + ])->run($data); + + $this->assertSame( + 'The a.*.d field is required when a.*.b,a.*.c is not present.', + $this->validation->getError('a.1.d'), + ); + } + + /** + * Test that `required_without` handles a non-asterisk field checked against an asterisk field + * without throwing undefined array key warnings for `$fieldSplitArray[1]`. + */ + public function testRequireWithoutAsteriskOnNonAsteriskField(): void + { + $data = [ + 'foo' => '', + 'a' => [ + ['b' => ''], + ], + ]; + + $this->validation->setRules([ + 'foo' => 'required_without[a.*.b]', + ])->run($data); + + $this->assertSame( + 'The foo field is required when a.*.b is not present.', + $this->validation->getError('foo'), + ); + } + /** * @see https://github.com/codeigniter4/CodeIgniter4/issues/8128 */ diff --git a/user_guide_src/source/changelogs/v4.7.4.rst b/user_guide_src/source/changelogs/v4.7.4.rst index 1cf22169f347..3535bbe7b424 100644 --- a/user_guide_src/source/changelogs/v4.7.4.rst +++ b/user_guide_src/source/changelogs/v4.7.4.rst @@ -42,6 +42,7 @@ Bugs Fixed - **Model:** Fixed a bug in ``Model::objectToRawArray()`` where the ``$recursive`` parameter was ignored. - **Session:** Fixed a bug in ``RedisHandler`` where the configured ``$lockMaxRetries`` and ``$lockRetryInterval`` values were not respected when acquiring session locks. - **Testing:** Fixed a bug where using ``MockInputOutput`` within a test that also uses ``StreamFilterTrait`` tore down the trait's stream filters, so CLI output produced after the ``MockInputOutput`` interaction (such as in ``tearDown()``) was no longer captured and leaked to the console. +- **Validation:** Fixed bugs in the ``required_without`` rule logic where using array dot notation caused early exits ignoring subsequent fields and triggered an ``Undefined array key`` warning for missing keys. See the repo's `CHANGELOG.md `_