getInlineMessage($attribute, $rule); // First we will retrieve the custom message for the validation rule if one // exists. If a custom validation message is being used we'll return the // custom message, otherwise we'll keep searching for a valid message. if (! is_null($inlineMessage)) { return $inlineMessage; } $lowerRule = Str::snake($rule); $customMessage = $this->getCustomMessageFromTranslator( $customKey = "validation.custom.{$attribute}.{$lowerRule}" ); // First we check for a custom defined validation message for the attribute // and rule. This allows the developer to specify specific messages for // only some attributes and rules that need to get specially formed. if ($customMessage !== $customKey) { return $customMessage; } // If the rule being validated is a "size" rule, we will need to gather the // specific error message for the type of attribute being validated such // as a number, file or string which all have different message types. elseif (in_array($rule, $this->sizeRules)) { return $this->getSizeMessage($attribute, $rule); } // Finally, if no developer specified messages have been set, and no other // special messages apply for this rule, we will just pull the default // messages out of the translator service for this validation rule. $key = "validation.{$lowerRule}"; if ($key != ($value = $this->translator->trans($key))) { return $value; } return $this->getFromLocalArray( $attribute, $lowerRule, $this->fallbackMessages ) ?: $key; } /** * Get the proper inline error message for standard and size rules. * * @param string $attribute * @param string $rule * @return string|null */ protected function getInlineMessage($attribute, $rule) { $inlineEntry = $this->getFromLocalArray($attribute, Str::snake($rule)); return is_array($inlineEntry) && in_array($rule, $this->sizeRules) ? $inlineEntry[$this->getAttributeType($attribute)] : $inlineEntry; } /** * Get the inline message for a rule if it exists. * * @param string $attribute * @param string $lowerRule * @param array|null $source * @return string|null */ protected function getFromLocalArray($attribute, $lowerRule, $source = null) { $source = $source ?: $this->customMessages; $keys = ["{$attribute}.{$lowerRule}", $lowerRule]; // First we will check for a custom message for an attribute specific rule // message for the fields, then we will check for a general custom line // that is not attribute specific. If we find either we'll return it. foreach ($keys as $key) { foreach (array_keys($source) as $sourceKey) { if (Str::is($sourceKey, $key)) { return $source[$sourceKey]; } } } } /** * Get the custom error message from translator. * * @param string $key * @return string */ protected function getCustomMessageFromTranslator($key) { if (($message = $this->translator->trans($key)) !== $key) { return $message; } // If an exact match was not found for the key, we will collapse all of these // messages and loop through them and try to find a wildcard match for the // given key. Otherwise, we will simply return the key's value back out. $shortKey = preg_replace( '/^validation\.custom\./', '', $key ); return $this->getWildcardCustomMessages(Arr::dot( (array) $this->translator->trans('validation.custom') ), $shortKey, $key); } /** * Check the given messages for a wildcard key. * * @param array $messages * @param string $search * @param string $default * @return string */ protected function getWildcardCustomMessages($messages, $search, $default) { foreach ($messages as $key => $message) { if ($search === $key || (Str::contains($key, ['*']) && Str::is($key, $search))) { return $message; } } return $default; } /** * Get the proper error message for an attribute and size rule. * * @param string $attribute * @param string $rule * @return string */ protected function getSizeMessage($attribute, $rule) { $lowerRule = Str::snake($rule); // There are three different types of size validations. The attribute may be // either a number, file, or string so we will check a few things to know // which type of value it is and return the correct line for that type. $type = $this->getAttributeType($attribute); $key = "validation.{$lowerRule}.{$type}"; return $this->translator->trans($key); } /** * Get the data type of the given attribute. * * @param string $attribute * @return string */ protected function getAttributeType($attribute) { // We assume that the attributes present in the file array are files so that // means that if the attribute does not have a numeric rule and the files // list doesn't have it we'll just consider it a string by elimination. if ($this->hasRule($attribute, $this->numericRules)) { return 'numeric'; } elseif ($this->hasRule($attribute, ['Array'])) { return 'array'; } elseif ($this->getValue($attribute) instanceof UploadedFile) { return 'file'; } return 'string'; } /** * Replace all error message place-holders with actual values. * * @param string $message * @param string $attribute * @param string $rule * @param array $parameters * @return string */ public function makeReplacements($message, $attribute, $rule, $parameters) { $message = $this->replaceAttributePlaceholder( $message, $this->getDisplayableAttribute($attribute) ); $message = $this->replaceInputPlaceholder($message, $attribute); if (isset($this->replacers[Str::snake($rule)])) { return $this->callReplacer($message, $attribute, Str::snake($rule), $parameters, $this); } elseif (method_exists($this, $replacer = "replace{$rule}")) { return $this->$replacer($message, $attribute, $rule, $parameters); } return $message; } /** * Get the displayable name of the attribute. * * @param string $attribute * @return string */ public function getDisplayableAttribute($attribute) { $primaryAttribute = $this->getPrimaryAttribute($attribute); $expectedAttributes = $attribute != $primaryAttribute ? [$attribute, $primaryAttribute] : [$attribute]; foreach ($expectedAttributes as $name) { // The developer may dynamically specify the array of custom attributes on this // validator instance. If the attribute exists in this array it is used over // the other ways of pulling the attribute name for this given attributes. if (isset($this->customAttributes[$name])) { return $this->customAttributes[$name]; } // We allow for a developer to specify language lines for any attribute in this // application, which allows flexibility for displaying a unique displayable // version of the attribute name instead of the name used in an HTTP POST. if ($line = $this->getAttributeFromTranslations($name)) { return $line; } } // When no language line has been specified for the attribute and it is also // an implicit attribute we will display the raw attribute's name and not // modify it with any of these replacements before we display the name. if (isset($this->implicitAttributes[$primaryAttribute])) { return $attribute; } return str_replace('_', ' ', Str::snake($attribute)); } /** * Get the given attribute from the attribute translations. * * @param string $name * @return string */ protected function getAttributeFromTranslations($name) { return Arr::get($this->translator->trans('validation.attributes'), $name); } /** * Replace the :attribute placeholder in the given message. * * @param string $message * @param string $value * @return string */ protected function replaceAttributePlaceholder($message, $value) { return str_replace( [':attribute', ':ATTRIBUTE', ':Attribute'], [$value, Str::upper($value), Str::ucfirst($value)], $message ); } /** * Replace the :input placeholder in the given message. * * @param string $message * @param string $attribute * @return string */ protected function replaceInputPlaceholder($message, $attribute) { $actualValue = $this->getValue($attribute); if (is_scalar($actualValue) || is_null($actualValue)) { $message = str_replace(':input', $actualValue, $message); } return $message; } /** * Get the displayable name of the value. * * @param string $attribute * @param mixed $value * @return string */ public function getDisplayableValue($attribute, $value) { if (isset($this->customValues[$attribute][$value])) { return $this->customValues[$attribute][$value]; } $key = "validation.values.{$attribute}.{$value}"; if (($line = $this->translator->trans($key)) !== $key) { return $line; } return $value; } /** * Transform an array of attributes to their displayable form. * * @param array $values * @return array */ protected function getAttributeList(array $values) { $attributes = []; // For each attribute in the list we will simply get its displayable form as // this is convenient when replacing lists of parameters like some of the // replacement functions do when formatting out the validation message. foreach ($values as $key => $value) { $attributes[$key] = $this->getDisplayableAttribute($value); } return $attributes; } /** * Call a custom validator message replacer. * * @param string $message * @param string $attribute * @param string $rule * @param array $parameters * @param \Illuminate\Validation\Validator $validator * @return string|null */ protected function callReplacer($message, $attribute, $rule, $parameters, $validator) { $callback = $this->replacers[$rule]; if ($callback instanceof Closure) { return call_user_func_array($callback, func_get_args()); } elseif (is_string($callback)) { return $this->callClassBasedReplacer($callback, $message, $attribute, $rule, $parameters, $validator); } } /** * Call a class based validator message replacer. * * @param string $callback * @param string $message * @param string $attribute * @param string $rule * @param array $parameters * @param \Illuminate\Validation\Validator $validator * @return string */ protected function callClassBasedReplacer($callback, $message, $attribute, $rule, $parameters, $validator) { list($class, $method) = Str::parseCallback($callback, 'replace'); return call_user_func_array([$this->container->make($class), $method], array_slice(func_get_args(), 1)); } }