Skip to content
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
94 changes: 83 additions & 11 deletions src/Illuminate/Validation/Concerns/ReplacesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Validation\Concerns;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;

trait ReplacesAttributes
{
Expand All @@ -21,7 +22,11 @@ protected function replaceAcceptedIf($message, $attribute, $rule, $parameters)

$parameters[0] = $this->getDisplayableAttribute($parameters[0]);

return str_replace([':other', ':value'], $parameters, $message);
return str_replace(
[':other', ':OTHER', ':Other', ':value', ':VALUE', ':Value'],
[$parameters[0], Str::upper($parameters[0]), Str::ucfirst($parameters[0]), $parameters[1], Str::upper($parameters[1]), Str::ucfirst($parameters[1])],
$message
);
Comment on lines +25 to +29
Copy link
Contributor

@shaedrich shaedrich Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to move that logic to its own method and then reuse it?

private function replaceKeepCase($message, $placeholder, $parameter)
{
	$fn = [ Str::lower(...), Str::upper(...), Str::ucfirst(...) ];
	$cases = array_map(fn (callable $fn) => ':' . $fn($placeholder), $fn);
	$replacements = array_map(fn (callable $fn) => $fn($parameter), $fn);
	return str_replace($cases, $replacements, $message);
}

protected function replaceAcceptedIf($message, $attribute, $rule, $parameters)
{
    $parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));

    $parameters[0] = $this->getDisplayableAttribute($parameters[0]);

    return $this->replaceKeepCase(
		$this->replaceKeepCase($message, 'other', $parameters[0]),
		'value',
		$parameters[1],
	);
}

}

/**
Expand Down Expand Up @@ -240,7 +245,15 @@ protected function replaceMissingUnless($message, $attribute, $rule, $parameters
*/
protected function replaceMissingWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
return str_replace(
[':values', ':VALUES', ':Values'],
[
implode(' / ', $this->getAttributeList($parameters)),
Str::upper(implode(' / ', $this->getAttributeList($parameters))),
implode(' / ', array_map(Str::ucfirst(...), $this->getAttributeList($parameters))),
],
$message
);
}

/**
Expand Down Expand Up @@ -286,7 +299,15 @@ protected function replaceIn($message, $attribute, $rule, $parameters)
$parameter = $this->getDisplayableValue($attribute, $parameter);
}

return str_replace(':values', implode(', ', $parameters), $message);
return str_replace(
[':values', ':VALUES', ':Values'],
[
implode(', ', $parameters),
Str::upper(implode(', ', $parameters)),
implode(', ', array_map(Str::ucfirst(...), $parameters)),
],
$message,
);
}

/**
Expand Down Expand Up @@ -314,7 +335,13 @@ protected function replaceNotIn($message, $attribute, $rule, $parameters)
*/
protected function replaceInArray($message, $attribute, $rule, $parameters)
{
return str_replace(':other', $this->getDisplayableAttribute($parameters[0]), $message);
$value = $this->getDisplayableAttribute($parameters[0]);

return str_replace(
[':other', ':OTHER', ':Other'],
[$value, Str::upper($value), Str::ucfirst($value)],
$message
);
}

/**
Expand Down Expand Up @@ -412,7 +439,15 @@ protected function replacePresentUnless($message, $attribute, $rule, $parameters
*/
protected function replacePresentWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
return str_replace(
[':values', ':VALUES', ':Values'],
[
implode(' / ', $this->getAttributeList($parameters)),
Str::upper(implode(' / ', $this->getAttributeList($parameters))),
implode(' / ', array_map(Str::ucfirst(...), $this->getAttributeList($parameters))),
],
$message,
);
}

/**
Expand Down Expand Up @@ -440,7 +475,15 @@ protected function replacePresentWithAll($message, $attribute, $rule, $parameter
*/
protected function replaceRequiredWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
return str_replace(
[':values', ':VALUES', ':Values'],
[
implode(' / ', $this->getAttributeList($parameters)),
Str::upper(implode(' / ', $this->getAttributeList($parameters))),
implode(' / ', array_map(Str::ucfirst(...), $this->getAttributeList($parameters))),
],
$message,
);
}

/**
Expand Down Expand Up @@ -584,9 +627,13 @@ protected function replaceRequiredIf($message, $attribute, $rule, $parameters)
*/
protected function replaceRequiredIfAccepted($message, $attribute, $rule, $parameters)
{
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
$value = $this->getDisplayableAttribute($parameters[0]);

return str_replace([':other'], $parameters, $message);
return str_replace(
[':other', ':OTHER', ':Other'],
[$value, Str::upper($value), Str::ucfirst($value)],
$message
);
}

/**
Expand Down Expand Up @@ -622,7 +669,18 @@ protected function replaceRequiredUnless($message, $attribute, $rule, $parameter
$values[] = $this->getDisplayableValue($parameters[0], $value);
}

return str_replace([':other', ':values'], [$other, implode(', ', $values)], $message);
return str_replace(
[':other', ':OTHER', ':Other', ':values', ':VALUES', ':Values'],
[
$other,
Str::upper($other),
Str::ucfirst($other),
implode(', ', $values),
Str::upper(implode(', ', $values)),
implode(', ', array_map(Str::ucfirst(...), $values)),
],
$message
);
}

/**
Expand Down Expand Up @@ -692,7 +750,15 @@ protected function replaceProhibitedUnless($message, $attribute, $rule, $paramet
*/
protected function replaceProhibits($message, $attribute, $rule, $parameters)
{
return str_replace(':other', implode(' / ', $this->getAttributeList($parameters)), $message);
return str_replace(
[':other', ':OTHER', ':Other'],
[
implode(' / ', $this->getAttributeList($parameters)),
Str::upper(implode(' / ', $this->getAttributeList($parameters))),
implode(' / ', array_map(Str::ucfirst(...), $this->getAttributeList($parameters))),
],
$message
);
}

/**
Expand All @@ -706,7 +772,13 @@ protected function replaceProhibits($message, $attribute, $rule, $parameters)
*/
protected function replaceSame($message, $attribute, $rule, $parameters)
{
return str_replace(':other', $this->getDisplayableAttribute($parameters[0]), $message);
$value = $this->getDisplayableAttribute($parameters[0]);

return str_replace(
[':other', ':OTHER', ':Other'],
[$value, Str::upper($value), Str::ucfirst($value)],
$message
);
}

/**
Expand Down
133 changes: 133 additions & 0 deletions tests/Validation/ValidationValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,139 @@ public function testInputIsReplaced()
$this->assertSame('empty is not a valid email', $v->messages()->first('email'));
}

public function testCapitalizedDisplayableValuesAreReplaced()
{
// accepted_if
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.accepted_if' => 'The :attribute field must be accepted when :Other is :Value.'], 'en');
$v = new Validator($trans, ['foo' => 'no', 'bar' => 'aaa'], ['foo' => 'accepted_if:bar,aaa']);
$this->assertFalse($v->passes());
$v->messages()->setFormat(':message');
$this->assertSame('The foo field must be accepted when Bar is Aaa.', $v->messages()->first('foo'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.accepted_if' => 'The :attribute field must be accepted when :OTHER is :VALUE.'], 'en');
$v = new Validator($trans, ['foo' => 'no', 'bar' => 'aaa'], ['foo' => 'accepted_if:bar,aaa']);
$this->assertFalse($v->passes());
$v->messages()->setFormat(':message');
$this->assertSame('The foo field must be accepted when BAR is AAA.', $v->messages()->first('foo'));

// in_array
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.in_array' => 'The value of :attribute does not exist in :Other.'], 'en');
$v = new Validator($trans, ['foo' => [1, 2, 3], 'bar' => [1, 2]], ['foo.*' => 'in_array:bar.*']);
$this->assertSame('The value of foo.2 does not exist in Bar.*.', $v->messages()->first('foo.2'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.in_array' => 'The value of :attribute does not exist in :OTHER.'], 'en');
$v = new Validator($trans, ['foo' => [1, 2, 3], 'bar' => [1, 2]], ['foo.*' => 'in_array:bar.*']);
$this->assertSame('The value of foo.2 does not exist in BAR.*.', $v->messages()->first('foo.2'));

// missing_with
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.missing_with' => 'The :attribute field must be missing when :Values is present.'], 'en');
$v = new Validator($trans, ['foo' => [], 'bar' => '2'], ['foo' => 'missing_with:baz,bar']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be missing when Baz / Bar is present.', $v->errors()->first('foo'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.missing_with' => 'The :attribute field must be missing when :VALUES is present.'], 'en');
$v = new Validator($trans, ['foo' => [], 'bar' => '2'], ['foo' => 'missing_with:baz,bar']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be missing when BAZ / BAR is present.', $v->errors()->first('foo'));

// present_with
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_with' => 'The :attribute field must be present when :Values is present.'], 'en');
$v = new Validator($trans, ['bar' => 2, 'baz' => 2], ['foo' => 'present_with:bar,baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present when Bar / Baz is present.', $v->errors()->first('foo'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.present_with' => 'The :attribute field must be present when :VALUES is present.'], 'en');
$v = new Validator($trans, ['bar' => 2, 'baz' => 2], ['foo' => 'present_with:bar,baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must be present when BAR / BAZ is present.', $v->errors()->first('foo'));

// prohibits
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.prohibits' => 'The :attribute field prohibits :Other being present.'], 'en');
$v = new Validator($trans, ['email' => 'foo', 'emails' => 'bar', 'email_address' => 'baz'], ['email' => 'prohibits:emails,email_address']);
$this->assertFalse($v->passes());
$this->assertSame('The email field prohibits Emails / Email address being present.', $v->messages()->first('email'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.prohibits' => 'The :attribute field prohibits :OTHER being present.'], 'en');
$v = new Validator($trans, ['email' => 'foo', 'emails' => 'bar', 'email_address' => 'baz'], ['email' => 'prohibits:emails,email_address']);
$this->assertFalse($v->passes());
$this->assertSame('The email field prohibits EMAILS / EMAIL ADDRESS being present.', $v->messages()->first('email'));

// required_if_accepted
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_if_accepted' => 'The :attribute field is required when :Other is accepted.'], 'en');
$v = new Validator($trans, ['foo' => 'yes', 'bar' => ''], ['bar' => 'required_if_accepted:foo']);
$this->assertFalse($v->passes());
$this->assertSame('The bar field is required when Foo is accepted.', $v->messages()->first('bar'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_if_accepted' => 'The :attribute field is required when :OTHER is accepted.'], 'en');
$v = new Validator($trans, ['foo' => 'yes', 'bar' => ''], ['bar' => 'required_if_accepted:foo']);
$this->assertFalse($v->passes());
$this->assertSame('The bar field is required when FOO is accepted.', $v->messages()->first('bar'));

// required_unless
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_unless' => 'The :attribute field is required unless :Other is in :Values.'], 'en');
$v = new Validator($trans, ['first' => 'dayle', 'last' => ''], ['last' => 'RequiredUnless:first,taylor,sven']);
$this->assertFalse($v->passes());
$this->assertSame('The last field is required unless First is in Taylor, Sven.', $v->messages()->first('last'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_unless' => 'The :attribute field is required unless :OTHER is in :VALUES.'], 'en');
$v = new Validator($trans, ['first' => 'dayle', 'last' => ''], ['last' => 'RequiredUnless:first,taylor,sven']);
$this->assertFalse($v->passes());
$this->assertSame('The last field is required unless FIRST is in TAYLOR, SVEN.', $v->messages()->first('last'));

// required_with
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_with' => 'The :attribute field is required when :Values is present.'], 'en');
$v = new Validator($trans, ['first' => 'Taylor', 'last' => ''], ['last' => 'required_with:first']);
$this->assertFalse($v->passes());
$this->assertSame('The last field is required when First is present.', $v->messages()->first('last'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.required_with' => 'The :attribute field is required when :VALUES is present.'], 'en');
$v = new Validator($trans, ['first' => 'Taylor', 'last' => ''], ['last' => 'required_with:first']);
$this->assertFalse($v->passes());
$this->assertSame('The last field is required when FIRST is present.', $v->messages()->first('last'));

// same
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.same' => 'The :attribute field must match :Other.'], 'en');
$v = new Validator($trans, ['foo' => 'bar', 'baz' => 'boom'], ['foo' => 'Same:baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must match Baz.', $v->messages()->first('foo'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.same' => 'The :attribute field must match :OTHER.'], 'en');
$v = new Validator($trans, ['foo' => 'bar', 'baz' => 'boom'], ['foo' => 'Same:baz']);
$this->assertFalse($v->passes());
$this->assertSame('The foo field must match BAZ.', $v->messages()->first('foo'));

// starts_with
$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.starts_with' => 'The :attribute must start with one of the following values :Values'], 'en');
$v = new Validator($trans, ['url' => 'laravel.com'], ['url' => 'starts_with:http,https']);
$this->assertFalse($v->passes());
$this->assertSame('The url must start with one of the following values Http, Https', $v->messages()->first('url'));

$trans = $this->getIlluminateArrayTranslator();
$trans->addLines(['validation.starts_with' => 'The :attribute must start with one of the following values :VALUES'], 'en');
$v = new Validator($trans, ['url' => 'laravel.com'], ['url' => 'starts_with:http,https']);
$this->assertFalse($v->passes());
$this->assertSame('The url must start with one of the following values HTTP, HTTPS', $v->messages()->first('url'));
}

public function testInputIsReplacedByItsDisplayableValue()
{
$frameworks = [
Expand Down