Skip to content

Commit

Permalink
Adds more implicit validation rules for present based on other fiel…
Browse files Browse the repository at this point in the history
…ds (#48908)
  • Loading branch information
diamondobama authored Nov 8, 2023
1 parent 79b3fa3 commit f58ec8c
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/Illuminate/Translation/lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
],
'present' => 'The :attribute field must be present.',
'present_if' => 'The :attribute field must be present when :other is :value.',
'present_unless' => 'The :attribute field must be present unless :other is :value.',
'present_with' => 'The :attribute field must be present when :values is present.',
'present_with_all' => 'The :attribute field must be present when :values are present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
Expand Down
62 changes: 62 additions & 0 deletions src/Illuminate/Validation/Concerns/ReplacesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,68 @@ protected function replaceMimes($message, $attribute, $rule, $parameters)
return str_replace(':values', implode(', ', $parameters), $message);
}

/**
* Replace all place-holders for the present_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);

return str_replace([':other', ':value'], $parameters, $message);
}

/**
* Replace all place-holders for the present_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentUnless($message, $attribute, $rule, $parameters)
{
return str_replace([':other', ':value'], [
$this->getDisplayableAttribute($parameters[0]),
$this->getDisplayableValue($parameters[0], $parameters[1]),
], $message);
}

/**
* Replace all place-holders for the present_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
}

/**
* Replace all place-holders for the present_with_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentWithAll($message, $attribute, $rule, $parameters)
{
return $this->replacePresentWith($message, $attribute, $rule, $parameters);
}

/**
* Replace all place-holders for the required_with rule.
*
Expand Down
80 changes: 80 additions & 0 deletions src/Illuminate/Validation/Concerns/ValidatesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,86 @@ public function validatePresent($attribute, $value)
return Arr::has($this->data, $attribute);
}

/**
* Validate that an attribute is present when another attribute has a given value.
*
* @param string $attribute
* @param mixed $value
* @param array<int, int|string> $parameters
* @return bool
*/
public function validatePresentIf($attribute, $value, $parameters)
{
$this->requireParameterCount(2, $parameters, 'present_if');

[$values, $other] = $this->parseDependentRuleParameters($parameters);

if (in_array($other, $values, is_bool($other) || is_null($other))) {
return $this->validatePresent($attribute, $value, $parameters);
}

return true;
}

/**
* Validate that an attribute is present unless another attribute has a given value.
*
* @param string $attribute
* @param mixed $value
* @param array<int, int|string> $parameters
* @return bool
*/
public function validatePresentUnless($attribute, $value, $parameters)
{
$this->requireParameterCount(2, $parameters, 'present_unless');

[$values, $other] = $this->parseDependentRuleParameters($parameters);

if (! in_array($other, $values, is_bool($other) || is_null($other))) {
return $this->validatePresent($attribute, $value, $parameters);
}

return true;
}

/**
* Validate that an attribute is present when any given attribute is present.
*
* @param string $attribute
* @param mixed $value
* @param array<int, int|string> $parameters
* @return bool
*/
public function validatePresentWith($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'present_with');

if (Arr::hasAny($this->data, $parameters)) {
return $this->validatePresent($attribute, $value, $parameters);
}

return true;
}

/**
* Validate that an attribute is present when all given attributes are present.
*
* @param string $attribute
* @param mixed $value
* @param array<int, int|string> $parameters
* @return bool
*/
public function validatePresentWithAll($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'present_with_all');

if (Arr::has($this->data, $parameters)) {
return $this->validatePresent($attribute, $value, $parameters);
}

return true;
}

/**
* Validate that an attribute passes a regular expression check.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Illuminate/Validation/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ class Validator implements ValidatorContract
'MissingWith',
'MissingWithAll',
'Present',
'PresentIf',
'PresentUnless',
'PresentWith',
'PresentWithAll',
'Required',
'RequiredIf',
'RequiredIfAccepted',
Expand Down
128 changes: 128 additions & 0 deletions tests/Validation/ValidationValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,134 @@ public function testValidatePresent()
$this->assertTrue($v->passes());
}

public function testValidatePresentIf()
{
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_if' => 'The :attribute field must be present when :other is :value.'], 'en');

$v = new Validator($trans, ['bar' => 1], ['foo' => 'present_if:bar,1']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present when bar is 1.', $v->errors()->first('foo'));

$v = new Validator($trans, ['bar' => 1, 'foo' => null], ['foo' => 'present_if:bar,2']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 1, 'foo' => ''], ['foo' => 'present_if:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 1, 'foo' => [['name' => 'a']]], ['foo.*.id' => 'present_if:bar,1']);
$this->assertFalse($v->passes());
$this->assertSame('The foo.0.id field must be present when bar is 1.', $v->errors()->first('foo.0.id'));

$v = new Validator($trans, ['bar' => 1, 'foo' => [['id' => '', 'name' => 'a']]], ['foo.*.id' => 'present_if:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 1, 'foo' => [['id' => null, 'name' => 'a']]], ['foo.*.id' => 'present_if:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 1, 'foo' => '2'], ['foo' => 'present_if:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2], ['foo' => 'present_if:bar,1']);
$this->assertTrue($v->passes());
}

public function testValidatePresentUnless()
{
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_unless' => 'The :attribute field must be present unless :other is :value.'], 'en');

$v = new Validator($trans, ['bar' => 2], ['foo' => 'present_unless:bar,1']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present unless bar is 1.', $v->errors()->first('foo'));

$v = new Validator($trans, ['bar' => 2, 'foo' => null], ['foo' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2, 'foo' => ''], ['foo' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2, 'foo' => [['name' => 'a']]], ['foo.*.id' => 'present_unless:bar,1']);
$this->assertFalse($v->passes());
$this->assertSame('The foo.0.id field must be present unless bar is 1.', $v->errors()->first('foo.0.id'));

$v = new Validator($trans, ['bar' => 2, 'foo' => [['id' => '', 'name' => 'a']]], ['foo.*.id' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2, 'foo' => [['id' => null, 'name' => 'a']]], ['foo.*.id' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2, 'foo' => '2'], ['foo' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 1], ['foo' => 'present_unless:bar,1']);
$this->assertTrue($v->passes());
}

public function testValidatePresentWith()
{
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_with' => 'The :attribute field must be present when :values is present.'], 'en');

$v = new Validator($trans, ['foo' => 1, 'bar' => 2], ['foo' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => null, 'bar' => 2], ['foo' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => '', 'bar' => 2], ['foo' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => [['name' => 'a']], 'bar' => 2], ['foo.*.id' => 'present_with:bar']);
$this->assertFalse($v->passes());
$this->assertSame('The foo.0.id field must be present when bar is present.', $v->errors()->first('foo.0.id'));

$v = new Validator($trans, ['foo' => [['id' => '']], 'bar' => 2], ['foo.*.id' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => [['id' => null]], 'bar' => 2], ['foo.*.id' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => 1], ['foo' => 'present_with:bar']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2], ['foo' => 'present_with:bar']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present when bar is present.', $v->errors()->first('foo'));
}

public function testValidatePresentWithAll()
{
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_with_all' => 'The :attribute field must be present when :values are present.'], 'en');

$v = new Validator($trans, ['foo' => 1, 'bar' => 2, 'baz' => 1], ['foo' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => null, 'bar' => 2, 'baz' => 1], ['foo' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => '', 'bar' => 2, 'baz' => 1], ['foo' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => [['name' => 'a']], 'bar' => 2, 'baz' => 1], ['foo.*.id' => 'present_with_all:bar,baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo.0.id field must be present when bar / baz are present.', $v->errors()->first('foo.0.id'));

$v = new Validator($trans, ['foo' => [['id' => '']], 'bar' => 2, 'baz' => 1], ['foo.*.id' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => [['id' => null]], 'bar' => 2, 'baz' => 1], ['foo.*.id' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['foo' => 1, 'bar' => 2], ['foo' => 'present_with_all:bar,baz']);
$this->assertTrue($v->passes());

$v = new Validator($trans, ['bar' => 2, 'baz' => 1], ['foo' => 'present_with_all:bar,baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present when bar / baz are present.', $v->errors()->first('foo'));
}

public function testValidateRequired()
{
$trans = $this->getIlluminateArrayTranslator();
Expand Down

0 comments on commit f58ec8c

Please sign in to comment.