Laravel validation of all elements in a comma separated string
Asked Answered
P

3

8

How can I validate one input with multiple values? I'm using bootstrap tagsinput plugin. It returns all tags in one field. I need to validate this tags - unique. First I'm trying to place this tags into array and then validate it in request but still no luck. Here is my code in request:

public function all()
{
    $postData = parent::all();

    // checkbox status
    if(array_key_exists('keywords', $postData)) {

        // put keywords into array
        $keywords = explode(',', $postData['keywords']);

        $test = [];
        $i = 0;
        foreach($keywords as $keyword)
        {
            $test[$i] = $keyword;
            $i++;
        }
        $postData['keywords'] = $test;

    }

    return $postData;

}

public function rules()
{
    $rules = [
        'title' => 'required|min:3|unique:subdomain_categories,title|unique:subdomain_keywords,keyword',
        'description' => '',
        'image' => 'required|image',
        'keywords.*' => 'min:3'
    ];

    return $rules;
}

But as soon as keyword becomes invalid I get this error:

ErrorException in helpers.php line 531: htmlentities() expects parameter 1 to be string, array given.

Any ideas what's wrong?

Peadar answered 6/10, 2017 at 21:13 Comment(0)
P
15

I was running into a similar problem with comma separated emails on 5.4. Here's how I solved it:

In your request class, override the prepareForValidation() method (which does nothing by default, btw), and explode your comma separated string.

/**
 * @inheritDoc
 */
protected function prepareForValidation()
{
    $this->replace(['keywords' => explode(',', $this->keywords)]);
}

Now you can just use Laravel's normal array validation!

Since my case needed to be a bit more explicit on the messages, too, I added in some attribute and message customizations as well. I made a gist, if that's of interest to you as well

Paragraph answered 20/12, 2017 at 15:45 Comment(1)
This didn't seem to work for me on 5.8. I ended up doing this: $this->merge(['keywords' => explode(',', $this->keywords)]);Lithotomy
C
7

Instead of mutating your input, for Laravel 6+ there is a package to apply these validations to a comma separated string of values:

https://github.com/spatie/laravel-validation-rules#delimited

You need to install it:

composer require spatie/laravel-validation-rules

Then, you can use it as a rule (using a FormRequest is recommended).

For example, to validate all items are emails, not long of 20 characters and there are at least 3 of them, you can use:

use Spatie\ValidationRules\Rules\Delimited;

// ...

public function rules()
{
    return [
        'emails' => [(new Delimited('email|max:20'))->min(3)],
    ];
}

The constructor of the validator accepts a validation rule string, a validation instance, or an array. That rules are used to validate all separate values; in this case, 'email|max:20'.

Corrie answered 8/7, 2021 at 15:45 Comment(0)
P
0

It's not a good practice to override the all() method to validate a field.

If you receive a comma separated String and not an array that you want to validate, but there is no common Method available, write your own.

in your App/Providers/ValidatorServiceProvider just add a new Validation:

public function boot()
{
    Validator::extend('keywords', function ($attribute, $value, $parameters, $validator)
    {
        // put keywords into array
        $keywords = explode(',', $value);

        foreach($keywords as $keyword)
        {
            // do validation logic
            if(strlen($keyword) < 3)
            {
                return false;
            }
        }

        return true;
    }
}

Now you can use this Validation Rule on any request if needed, it is reusable.

public function rules()
{
    $rules = [
        'title'       => 'required|min:3|unique:subdomain_categories,title|unique:subdomain_keywords,keyword',
        'description' => 'nullable',
        'image'       => 'required|image',
        'keywords'    => 'keywords',
    ];

    return $rules;
}

If the validation pass and you want to make the keywords be accessible in your request as array, write your own method:

public function keywords ()
{
    return explode(',', $this->get('keywords'));
}
Preconscious answered 6/10, 2017 at 21:35 Comment(2)
This is not working... It's strange because if I try: dd($this->request->get('keywords')); It dumps "item 1, item 2". Seems like it didn't changed keywords to arrayPeadar
What is the content of $this->request->get('keywords') is it an array with key => value or is a comma separated stringPreconscious

© 2022 - 2024 — McMap. All rights reserved.