Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: required_without rule logic in Validation class. #6589

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions system/Validation/Rules.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,29 +279,48 @@ 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
kenjis marked this conversation as resolved.
Show resolved Hide resolved
{
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;
}

// 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;
Expand Down
8 changes: 5 additions & 3 deletions system/Validation/StrictRules/Rules.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion system/Validation/Validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
33 changes: 31 additions & 2 deletions tests/system/Validation/ValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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')
);
}
}
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/v4.2.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
************
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/installation/upgrade_427.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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() <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
*************
Expand Down