Search code examples
phplaravellaravel-validationphp-7.4php-8.1

Return validator messages in custom Rule


A problem that we probably didn't know about when switching to PHP 8+: Very often, in the rules we created earlier, we returned a message like this:

public function message(): array
{
    return [$this->validator->errors()->messages()];
} 

When using PHP 7.4 - this isn't a problem, but not for PHP 8+

Since looking "deeper" into how the laravel framework forms messages, we get an error in the replaceAttributePlaceholder method of the FormatsMessages class:

/**
 * 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
    );
}

And indeed, if we open any editor and run the same code, but for two different versions, we'll get: enter image description here

If you return the message like this:

public function message(): array
{
    return $this->validator->errors()->messages();
}

We will avoid the error, but accordingly, the format of the message will be different - this doesn't suit me, and the format of the message should remain the same. Does anyone have any ideas on how to save the format and fix the error?


Solution

  • To solve this problem I had to follow these steps:

    1. Use your validator instead of the framework 'validator':
    $this->app->make('validator')->resolver(function ($translator, $data, $rules, $messages) {
        return new App\Validation\Validator($translator, $data, $rules, $messages);
    });
    
    1. Override replaceAttributePlaceholder function:
    <?php
    
    namespace App\Validation;
    
    use Illuminate\Validation\Concerns\FormatsMessages;
    use Illuminate\Validation\Validator as ValidationValidator;
    
    class Validator extends ValidationValidator
    {
        use FormatsMessages {
            replaceAttributePlaceholder as protected traitReplaceAttributePlaceholder;
        }
    
        /**
         * Replace the :attribute placeholder in the given message.
         *
         * @param  string  $message
         * @param  string  $value
         * @return string
         */
        protected function replaceAttributePlaceholder($message, $value)
        {
            if (is_string($message)) {
                return $this->traitReplaceAttributePlaceholder($message, $value);
            }
    
            $messages = $message;
            foreach ($messages as $key => $message) {
                if (is_array($message)) {
                    $this->replaceAttributePlaceholder($message, $key);
                }
    
                $messages[$key] = $this->replaceAttributePlaceholder($message, $key);
            }
    
            return $messages;
        }
    }