diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 04e7f0771c17..40a5955b157d 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -279,18 +279,20 @@ public function required_with($str = null, ?string $fields = null, array $data = * required_without[id,email] * * @param string|null $str + * @param string|null $otherFields The param fields of required_without[]. + * @param string|null $field This rule param fields aren't present, this field is required. */ - public function required_without($str = null, ?string $fields = null, array $data = []): bool + public function required_without($str = null, ?string $otherFields = null, array $data = [], ?string $error = null, ?string $field = null): bool { - if ($fields === null || empty($data)) { - throw new InvalidArgumentException('You must supply the parameters: fields, data.'); + if ($otherFields === null || empty($data)) { + throw new InvalidArgumentException('You must supply the parameters: otherFields, data.'); } // If the field is present we can safely assume that // the field is here, no matter whether the corresponding // search field is present or not. - $fields = explode(',', $fields); - $present = $this->required($str ?? ''); + $otherFields = explode(',', $otherFields); + $present = $this->required($str ?? ''); if ($present) { return true; @@ -298,10 +300,27 @@ public function required_without($str = null, ?string $fields = null, array $dat // Still here? Then we fail this test if // any of the fields are not present in $data - foreach ($fields as $field) { - if ((strpos($field, '.') === false && (! array_key_exists($field, $data) || empty($data[$field]))) || (strpos($field, '.') !== false && empty(dot_array_search($field, $data)))) { + foreach ($otherFields as $otherField) { + if ((strpos($otherField, '.') === false) && (! array_key_exists($otherField, $data) || empty($data[$otherField]))) { return false; } + if (strpos($otherField, '.') !== false) { + if ($field === null) { + throw new InvalidArgumentException('You must supply the parameters: field.'); + } + + $fieldData = dot_array_search($otherField, $data); + $fieldSplitArray = explode('.', $field); + $fieldKey = $fieldSplitArray[1]; + + if (is_array($fieldData)) { + return ! empty(dot_array_search($otherField, $data)[$fieldKey]); + } + $nowField = str_replace('*', $fieldKey, $otherField); + $nowFieldVaule = dot_array_search($nowField, $data); + + return null !== $nowFieldVaule; + } } return true; diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index af8970122f5c..76f32a9c1fe4 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -302,10 +302,12 @@ public function required_with($str = null, ?string $fields = null, array $data = * * required_without[id,email] * - * @param mixed $str + * @param string|null $str + * @param string|null $otherFields The param fields of required_without[]. + * @param string|null $field This rule param fields aren't present, this field is required. */ - public function required_without($str = null, ?string $fields = null, array $data = []): bool + public function required_without($str = null, ?string $otherFields = null, array $data = [], ?string $error = null, ?string $field = null): bool { - return $this->nonStrictRules->required_without($str, $fields, $data); + return $this->nonStrictRules->required_without($str, $otherFields, $data, $error, $field); } } diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index 8537ed602f6f..04bd3c8fcf98 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -312,7 +312,7 @@ protected function processRules( $found = true; $passed = $param === false ? $set->{$rule}($value, $error) - : $set->{$rule}($value, $param, $data, $error); + : $set->{$rule}($value, $param, $data, $error, $field); break; } diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 09faa1bb2282..bd191204449a 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1414,13 +1414,20 @@ public function testNestedArrayThrowsException(): void 'credit_amount' => null, 'purpose' => 'A', ], + 'account_3' => [ + 'account_number' => '', + 'credit_amount' => 2000, + 'purpose' => '', + ], ], ]; $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.', + 'beneficiaries_accounts.account_3.account_number' => 'The BENEFICIARY ACCOUNT NUMBER field must be exactly 5 characters in length.', + '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.', + 'beneficiaries_accounts.account_3.purpose' => 'The PURPOSE field is required when BENEFICIARY ACCOUNT NUMBER is not present.', ], $this->validation->getErrors()); } @@ -1436,4 +1443,26 @@ public function testRuleWithLeadingAsterisk(): void $this->assertFalse($this->validation->run($data)); $this->assertSame('Required *.foo', $this->validation->getError('*.foo')); } + + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/5942 + */ + public function testRequireWithoutWithWildCard() + { + $data = [ + 'a' => [ + ['b' => 1, 'c' => 2], + ['c' => ''], + ], + ]; + + $this->validation->setRules([ + 'a.*.c' => 'required_without[a.*.b]', + ])->run($data); + + $this->assertSame( + 'The a.*.c field is required when a.*.b is not present.', + $this->validation->getError('a.1.c') + ); + } } diff --git a/user_guide_src/source/changelogs/v4.2.7.rst b/user_guide_src/source/changelogs/v4.2.7.rst index 721ee4b62732..b341ea5a0c61 100644 --- a/user_guide_src/source/changelogs/v4.2.7.rst +++ b/user_guide_src/source/changelogs/v4.2.7.rst @@ -14,6 +14,7 @@ BREAKING - The default values of the parameters in :php:func:`set_cookie()` and :php:meth:`CodeIgniter\\HTTP\\Response::setCookie()` has been fixed. Now the default values of ``$secure`` and ``$httponly`` are ``null``, and these values will be replaced with the ``Config\Cookie`` values. - ``Time::__toString()`` is now locale-independent. It returns database-compatible strings like '2022-09-07 12:00:00' in any locale. +- The Validation rule ``Validation\Rule::required_without()`` and ``Validation\StrictRules\Rule::required_without()`` parameters have been changed and the logic of these rule has also been fixed. Enhancements ************ diff --git a/user_guide_src/source/installation/upgrade_427.rst b/user_guide_src/source/installation/upgrade_427.rst index 5a520c99cfc4..4ad0a1b2586e 100644 --- a/user_guide_src/source/installation/upgrade_427.rst +++ b/user_guide_src/source/installation/upgrade_427.rst @@ -53,6 +53,7 @@ Others ====== - ``Time::__toString()`` is now locale-independent. It returns database-compatible strings like '2022-09-07 12:00:00' in any locale. Most locales are not affected by this change. But in a few locales like `ar`, `fa`, ``Time::__toString()`` (or ``(string) $time`` or implicit casting to a string) no longer returns a localized datetime string. if you want to get a localized datetime string, use :ref:`Time::toDateTimeString() ` instead. +- The logic of Validation rule ``required_without`` has been changed to validate each array item separately when validating fields with asterisk (``*``), and the method signature of the rule method has also been changed. Extending classes should likewise update the parameters so as not to break LSP. Project Files *************