Skip to content

Commit 50ed11f

Browse files
authored
Merge pull request #4545 from paulbalandan/validation-dot-notation-if-exist
Fix dot notation for if_exist
2 parents ede6d96 + 71f42c7 commit 50ed11f

2 files changed

Lines changed: 108 additions & 20 deletions

File tree

system/Validation/Validation.php

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,30 @@ protected function processRules(string $field, string $label = null, $value, $ru
215215

216216
if (in_array('if_exist', $rules, true))
217217
{
218-
// If the if_exist rule is defined
219-
// and the current field does not exist in the input data
220-
// we can return true, ignoring all other rules to this field.
221-
if (! array_key_exists($field, array_flatten_with_dots($data)))
218+
$flattenedData = array_flatten_with_dots($data);
219+
$ifExistField = $field;
220+
221+
if (strpos($field, '.*') !== false)
222+
{
223+
// We'll change the dot notation into a PCRE pattern
224+
// that can be used later
225+
$ifExistField = str_replace('\.\*', '\.(?:[^\.]+)', preg_quote($field, '/'));
226+
227+
$dataIsExisting = array_reduce(array_keys($flattenedData), static function ($carry, $item) use ($ifExistField) {
228+
$pattern = sprintf('/%s/u', $ifExistField);
229+
return $carry || preg_match($pattern, $item) === 1;
230+
}, false);
231+
}
232+
else
233+
{
234+
$dataIsExisting = array_key_exists($ifExistField, $flattenedData);
235+
}
236+
237+
unset($ifExistField, $flattenedData);
238+
239+
if (! $dataIsExisting)
222240
{
241+
// we return early if `if_exist` is not satisfied. we have nothing to do here.
223242
return true;
224243
}
225244

@@ -345,6 +364,7 @@ protected function processRules(string $field, string $label = null, $value, $ru
345364
*/
346365
public function withRequest(RequestInterface $request): ValidationInterface
347366
{
367+
/** @var IncomingRequest $request */
348368
if (strpos($request->getHeaderLine('Content-Type'), 'application/json') !== false)
349369
{
350370
$this->data = $request->getJSON(true);
@@ -365,7 +385,6 @@ public function withRequest(RequestInterface $request): ValidationInterface
365385
return $this;
366386
}
367387

368-
//--------------------------------------------------------------------
369388
//--------------------------------------------------------------------
370389
// Rules
371390
//--------------------------------------------------------------------
@@ -814,24 +833,21 @@ protected function splitRules(string $rules): array
814833
{
815834
$nonEscapeBracket = '((?<!\\\\)(?:\\\\\\\\)*[\[\]])';
816835
$pipeNotInBracket = sprintf(
817-
'/\|(?=(?:[^\[\]]*%s[^\[\]]*%s)*(?![^\[\]]*%s))/',
818-
$nonEscapeBracket,
819-
$nonEscapeBracket,
820-
$nonEscapeBracket
836+
'/\|(?=(?:[^\[\]]*%s[^\[\]]*%s)*(?![^\[\]]*%s))/',
837+
$nonEscapeBracket,
838+
$nonEscapeBracket,
839+
$nonEscapeBracket
821840
);
822841

823-
$_rules = preg_split(
824-
$pipeNotInBracket,
825-
$rules
826-
);
842+
$_rules = preg_split($pipeNotInBracket, $rules);
827843

828844
return array_unique($_rules);
829845
}
830846

831-
//--------------------------------------------------------------------
832847
//--------------------------------------------------------------------
833848
// Misc
834849
//--------------------------------------------------------------------
850+
835851
/**
836852
* Resets the class to a blank slate. Should be called whenever
837853
* you need to process more than one array.
@@ -847,6 +863,4 @@ public function reset(): ValidationInterface
847863

848864
return $this;
849865
}
850-
851-
//--------------------------------------------------------------------
852866
}

tests/system/Validation/ValidationTest.php

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
<?php namespace CodeIgniter\Validation;
1+
<?php
2+
3+
namespace CodeIgniter\Validation;
24

35
use CodeIgniter\HTTP\IncomingRequest;
46
use CodeIgniter\HTTP\URI;
@@ -9,9 +11,11 @@
911
use Config\Services;
1012
use Tests\Support\Validation\TestRules;
1113

12-
class ValidationTest extends CIUnitTestCase
14+
/**
15+
* @internal
16+
*/
17+
final class ValidationTest extends CIUnitTestCase
1318
{
14-
1519
/**
1620
* @var Validation
1721
*/
@@ -945,5 +949,75 @@ public function testTranslatedLabelTagReplacement()
945949
$this->assertEquals($expected, $errors['Username']);
946950
}
947951

948-
//--------------------------------------------------------------------
952+
/**
953+
* @dataProvider dotNotationForIfExistProvider
954+
*
955+
* @see https://github.com/codeigniter4/CodeIgniter4/issues/4521
956+
*
957+
* @param boolean $expected
958+
* @param array $rules
959+
* @param array $data
960+
*
961+
* @return void
962+
*/
963+
public function testDotNotationOnIfExistRule(bool $expected, array $rules, array $data): void
964+
{
965+
$actual = $this->validation->setRules($rules)->run($data);
966+
$this->assertSame($expected, $actual);
967+
}
968+
969+
public function dotNotationForIfExistProvider()
970+
{
971+
yield 'dot-on-end-fail' => [
972+
false,
973+
['status.*' => 'if_exist|in_list[status_1,status_2]'],
974+
['status' => ['bad-status']],
975+
];
976+
977+
yield 'dot-on-end-pass' => [
978+
true,
979+
['status.*' => 'if_exist|in_list[status_1,status_2]'],
980+
['status' => ['status_1']],
981+
];
982+
983+
yield 'dot-on-middle-fail' => [
984+
false,
985+
['fizz.*.baz' => 'if_exist|numeric'],
986+
['fizz' => [
987+
'bar' => ['baz' => 'yes'],
988+
]],
989+
];
990+
991+
yield 'dot-on-middle-pass' => [
992+
true,
993+
['fizz.*.baz' => 'if_exist|numeric'],
994+
['fizz' => [
995+
'bar' => ['baz' => 30],
996+
]],
997+
];
998+
999+
yield 'dot-multiple-fail' => [
1000+
false,
1001+
['fizz.*.bar.*' => 'if_exist|numeric'],
1002+
['fizz' => [
1003+
'bos' => [
1004+
'bar' => [
1005+
'bub' => 'noo',
1006+
],
1007+
],
1008+
]],
1009+
];
1010+
1011+
yield 'dot-multiple-pass' => [
1012+
true,
1013+
['fizz.*.bar.*' => 'if_exist|numeric'],
1014+
['fizz' => [
1015+
'bos' => [
1016+
'bar' => [
1017+
'bub' => 5,
1018+
],
1019+
],
1020+
]],
1021+
];
1022+
}
9491023
}

0 commit comments

Comments
 (0)