简体   繁体   English

Laravel custom messages for array validation

[英]Laravel custom messages for array validation

I am having a form and I have an array of input fields for video urls, now when I validate form if I have multiple invalid fields with video urls, I get the same message for each of the invalid field, since I made my own custom messages. I don't want for each input field the same error message and I don't want the default Laravel error messages for arrays where the name of the field is shown with the error message, instead of that, I would like to have error messages with the value, in this case url written from the user. How to do that?

This is my request file with messages and rules:

public function messages(){

    $messages = [
      'title.required' => 'Du må ha tittel.',
      'type.required' => 'Du må velge artikkeltype.',
      'category.required' => 'Du må velge kategori.',
      'summary.required' => 'Du må ha inngress.',
      'text.required' => 'Du må ha artikkeltekst.',
      'active_url' => 'Du må ha gyldig url.',
    ];
  }

  public function rules(){

    $rules = [
      'external_media.*' => 'active_url',
      'title' => 'required',
      'type' => 'required',
      'category' => 'required',
      'summary' => 'required',
      'text' => 'required',
      //'image' => 'required|image|max:20000',
    ];

    return $rules;

  }

Updated code to make the question clearer

When I have my request file like this:

public function messages(){

    $messages = [
      'title.required'    => 'Du må ha tittel.',
      'type.required'    => 'Du må velge artikkeltype.',
      'category.required'    => 'Du må velge kategori.',
      'summary.required'    => 'Du må ha inngress.',
      'text.required'    => 'Du må ha artikkeltekst.',
      'external_media.active_url' => 'Du må ha gyldig url.',
   ];

   return $messages;
  }

  public function rules(){

    $rules = [
      'external_media.*' => 'active_url',
      'title' => 'required',
      'type' => 'required',
      'category' => 'required',
      'summary' => 'required',
      'text' => 'required',
      //'image' => 'required|image|max:20000',
    ];

    return $rules;

  }

I get the output:

The external_media.0 is not a valid URL.
The external_media.1 is not a valid URL.
The external_media.2 is not a valid URL.

Instead of that kind of output I would like to take the value for each of those inputs and have something like:

The htt:/asdfas.com  is not a valid URL.
public function messages() {

    $messages = [
        'title.required'    => 'Du må ha tittel.',
        'type.required'     => 'Du må velge artikkeltype.',
        'category.required' => 'Du må velge kategori.',
        'summary.required'  => 'Du må ha inngress.',
        'text.required'     => 'Du må ha artikkeltekst.',

    ];

    foreach ($this->get('external_media') as $key => $val) {
        $messages["external_media.$key.active_url"] = "$val is not a valid active url";
    }

    return $messages;

}

To use a custom messages from outside the validation language file, you can use it this way:

$messages = ['username.required' => 'customeError'];

$validator = \Validator::make(
    $data,
    ['username' => 'required'],
    messages
);

You can just pass an array of your custom messages as the third parameter as I have used it above. Hope this helps.

For laravel 7.x, I have found the following solution. You can basically use 'field.rule' => 'message'

public function rules() 
{
   return [
      'user.*.firstname' => 'string|required',
   ];
}

And then in the messages (I use a FormRequest for all requests):

public function messages() 
{
   'user.*.firstname.required' => 'Firstname of the user is required',
}

You can also pass a translation string like 'passwords.user' to the message.

I think this will help you if you are using "name=location[]" this in your view page.

 for ($i = 0; $i <= sizeof($location); $i++) {
 $this->validate($request,
 [
// set the rules
  'location.'.$i => 'required',
  'contact_no.'.$i => 'required',
   'email.'.$i => 'required|email',
 ], 
[
// set your custom error messages here 
  'location.'.$i.'.'.'required' => 'Contact no. is required', 
  'contact_no.'.$i.'.'.'required' => 'Contact no. is required',
  'email.'.$i.'.'.'required' => 'Email is required',
  'email.'.$i.'.'.'email' => 'Please enter valid email address'
]);
}
public function messages()
        {
            $messages = [];
            foreach ($this->request->get('external_media') as $key => $val) {
                $messages['external_media.' . $key . '.active_url'] = 'The '.$val .' is not a valid URL.'
            }
            return $messages;
        }
'external_media.*.required' => 'active_url',

Edit with potential solution

After some digging around, I've had a look at the Validator class and how it is adding it's error messages to see if it has any available placeholders.

In the Illuminate\Validation\Validator the function that I think is run to validate a request is validate, which runs each of the rules in turn and adds error messages. The piece of code related to adding an error message is this one, at the end of the function:

    $value = $this->getValue($attribute);

    $validatable = $this->isValidatable($rule, $attribute, $value);

    $method = "validate{$rule}";

    if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
        $this->addFailure($attribute, $rule, $parameters);
    }

As you can see, it's not actually passing the value of the field that was validated through when it's adding a failure, which in turn adds an error message.

I've managed to get something working to achieve what you're after. If you add these two methods into your base Request class which is usually at App\Http\Requests\Request:

protected function formatErrors(Validator $validator)
{
    $errors = parent::formatErrors($validator);

    foreach ($errors as $attribute => $eachError) {
        foreach ($eachError as $key => $error) {
            if (strpos($error, ':value') !== false) {
                $errors[$attribute][$key] = str_replace(':value', $this->getValueByAttribute($attribute), $error);
            }
        }
    }

    return $errors;
}

protected function getValueByAttribute($attribute)
{
    if (($dotPosition = strpos($attribute, '.')) !== false) {
        $name = substr($attribute, 0, $dotPosition);
        $index = substr($attribute, $dotPosition + 1);

        return $this->get($name)[$index];
    }

    return $this->get($attribute);
}

Then in your validation messages you should be able to put the :value replacer, which should be replaced with the actual value that was validated, like this:

public function messages()
{
    return [
        'external_media.*.active_url' => 'The URL :value is not valid'
    ];
}

I've noticed a couple of problems with your code:

  • In your messages function you've provided a message for active_url which is the name of the rule, rather than the name of the field. This should be external_media.
  • You're not returning the $messages variable from your messages function. You need to add return $messages; to the end.

With regards to your question however, the class in which you write this code is an instance of Illuminate\Http\Request and you should be able to access the actual values provided to that request when generating your error messages. For example, you could do something like this:

public function messages()
{
    return [
        'external_media' => 'The following URL is invalid: ' . $this->get('external_media')
    ];
}

public function rules()
{
    return [
        'external_media' => 'active_url'
    ];
}

Which would include the value provided to external_media in the error message. Hope this helps.

You can using Customizing The Error Format

protected function formatErrors(Validator $validator)
{
    $results = [];
    $flag = false;
    $messages = $validator->errors()->messages();
    foreach ($messages as $key => $value) {
        if (!str_contains($key, 'external_media') || !$flag) {
            $results[] = $value[0];
        }
        if (str_contains($key, 'external_media') && !$flag) {
            $flag = true;
        }
    }
    return $results;
}

This works fine for me in laravel 5.5

$request->validate([
            'files.*' => 'required|mimes:jpeg,png,jpg,gif,pdf,doc|max:10000'
        ],
        [
            'files.*.mimes' => 'Los archivos solo pueden estar con formato: jpeg, png, jpg, gif, pdf, doc.',
        ]
    );

You can use as

$messages = [
    'title.required' => 'Du må ha tittel.',
    'type.required' => 'Du må velge artikkeltype.',
    'category.required' => 'Du må velge kategori.',
    'summary.required' => 'Du må ha inngress.',
    'text.required' => 'Du må ha artikkeltekst.',
    'active_url' => 'Du må ha gyldig url.',
];

$validator = Validator::make($data, [
    'external_media.*' => 'active_url',
    'title' => 'required',
    'type' => 'required',
    'category' => 'required',
    'summary' => 'required',
    'text' => 'required',
    //'image' => 'required|image|max:20000'
], $messages);

if ($validator->fails()){
    // handle validation error
} else {
    // no validation error found
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM